DDD中如何引用AggregateRoot内部实体数据
How to reference AggregateRoot internal entity data in DDD
我对 DDD 的想法很感兴趣,但我对封装和保护 AggregateRoot 内部实体的概念以及如何引用它们有一些疑问。我创建了一个简单的例子(域设计不好不要打我,这只是一个澄清问题的例子).
// can be referenced from outside, is aggregate root
public class Library : AggregateRoot
{
IList<Shelf> shelfs { get; }
}
// can be referenced from outside, is aggregate root
public class Shelf : AggregateRoot
{
Book GetBookById(Guid Id) {...}
}
// should be accessed through Shelf, not referenced outside the context
public class Book : Entity<Guid>
{
Guid Id { get; } // or something else uniqe, e.g. ISBN
}
// how to reference a book here?
// I think, I should not use Id because this is internal and
//only valid for Shelf Aggregate (but I can't have a second one of this book)
public class Lending : AggregateRoot
{
// feels wrong because of passing internals from Shelf
Guid LendedBook { get; set; }
// should I Clone() an object with unique identity, is this allowed in DDD?
Book LendedBook { get; set;}
// create separate type for lending (but what should this type cointain)
LendedBookInfo LendedBook { get; set;}
}
希望得到一个明确的答案,因为大多数示例仅与 ValueObjects 有关(这很容易,因为它们无论如何都被复制并且没有真正被引用)。
我的示例使用了 C# 风格的代码,但也欢迎任何其他编程语言或伪代码作为答案。
您的外部 ID 使用示例
Guid LendedBook { get; set; }
没问题。为了使模型更明确,您可以做的是创建一个值对象 BookId
并使用该对象来引用书籍。值对象 BookId
是上下文范围的概念,即不是特定聚合的本地概念。
除此之外,您可能不希望 public setter 或 IList
作为 return 类型输入您的模型。但我想这只是一个例子,并不是真正的问题。
在 DDD 中,为真实世界建模从来都不是坏事。如果您与该领域的领域专家(图书管理员)交谈,他们可能拥有可以帮助您为您的领域建模的术语和工作流程。
例如,他们可能会使用 "book ticket system" 谈论 "lending a book",当他们想按书名查找一本书时,他们可能会查看 "title catalogue"。
这可能会导致您的 BookTicketService
class 和 LendBook(lender, book)
方法的设计,这可能会导致在 BookTicketSystem
中记录一个条目。或具有 SearchByTitle(title)
方法的 TitleCatalogueService
class。或者当图书馆接受一本新书入库时,它的记录可能会进入 AccessionRegister
,等等
如果您使用这种无处不在的语言为您的图书馆建模,它可以让图书馆领域的其他人更轻松地使用您的软件,最重要的是,它允许软件开发人员与图书馆员讨论他们的需求,而无需使用软件开发术语。
我会 Google 一些类似 "how to start a library" 或 "library glossary of terms" 的东西,然后尝试将您找到的一些语言和流程带入您的软件中。人们已经 运行 功能齐全的图书馆,拥有数以万计的图书和出借人。从理论上讲,您需要做的就是了解他们是如何做到这一点的,他们使用的术语以及应该可以在软件中对其进行建模。
我对 DDD 的想法很感兴趣,但我对封装和保护 AggregateRoot 内部实体的概念以及如何引用它们有一些疑问。我创建了一个简单的例子(域设计不好不要打我,这只是一个澄清问题的例子).
// can be referenced from outside, is aggregate root
public class Library : AggregateRoot
{
IList<Shelf> shelfs { get; }
}
// can be referenced from outside, is aggregate root
public class Shelf : AggregateRoot
{
Book GetBookById(Guid Id) {...}
}
// should be accessed through Shelf, not referenced outside the context
public class Book : Entity<Guid>
{
Guid Id { get; } // or something else uniqe, e.g. ISBN
}
// how to reference a book here?
// I think, I should not use Id because this is internal and
//only valid for Shelf Aggregate (but I can't have a second one of this book)
public class Lending : AggregateRoot
{
// feels wrong because of passing internals from Shelf
Guid LendedBook { get; set; }
// should I Clone() an object with unique identity, is this allowed in DDD?
Book LendedBook { get; set;}
// create separate type for lending (but what should this type cointain)
LendedBookInfo LendedBook { get; set;}
}
希望得到一个明确的答案,因为大多数示例仅与 ValueObjects 有关(这很容易,因为它们无论如何都被复制并且没有真正被引用)。 我的示例使用了 C# 风格的代码,但也欢迎任何其他编程语言或伪代码作为答案。
您的外部 ID 使用示例
Guid LendedBook { get; set; }
没问题。为了使模型更明确,您可以做的是创建一个值对象 BookId
并使用该对象来引用书籍。值对象 BookId
是上下文范围的概念,即不是特定聚合的本地概念。
除此之外,您可能不希望 public setter 或 IList
作为 return 类型输入您的模型。但我想这只是一个例子,并不是真正的问题。
在 DDD 中,为真实世界建模从来都不是坏事。如果您与该领域的领域专家(图书管理员)交谈,他们可能拥有可以帮助您为您的领域建模的术语和工作流程。
例如,他们可能会使用 "book ticket system" 谈论 "lending a book",当他们想按书名查找一本书时,他们可能会查看 "title catalogue"。
这可能会导致您的 BookTicketService
class 和 LendBook(lender, book)
方法的设计,这可能会导致在 BookTicketSystem
中记录一个条目。或具有 SearchByTitle(title)
方法的 TitleCatalogueService
class。或者当图书馆接受一本新书入库时,它的记录可能会进入 AccessionRegister
,等等
如果您使用这种无处不在的语言为您的图书馆建模,它可以让图书馆领域的其他人更轻松地使用您的软件,最重要的是,它允许软件开发人员与图书馆员讨论他们的需求,而无需使用软件开发术语。
我会 Google 一些类似 "how to start a library" 或 "library glossary of terms" 的东西,然后尝试将您找到的一些语言和流程带入您的软件中。人们已经 运行 功能齐全的图书馆,拥有数以万计的图书和出借人。从理论上讲,您需要做的就是了解他们是如何做到这一点的,他们使用的术语以及应该可以在软件中对其进行建模。