没有业务属性的聚合或实体
Aggregate or entity without business attributes
关于以下摘录,关于 cqrs 和 ddd,摘自 领域驱动设计的模式、原则和实践作者:Nick Tune、Scott Millett
这是否意味着命令端的域模型可以省略大部分业务属性?
例如,客户实体会是什么样子?
客户实体可以省略名字、姓氏等吗?
如果是这样,这些业务属性在哪里?仅在 CustomerEntity 中的读取模型中?
或者除了包含所有业务属性的 CustomerEntity 之外,可能还会有 CustomerAggregate 用 1:1 关系包装 CustomerEntity,并且命令对象会在 CustomerAggregate 上运行? (对我来说似乎很奇怪)。
“客户实体没有意义”是什么意思?
如果您正在使用 Event Sourcing 那么您确实可以对聚合进行建模而无需添加它们不需要实现业务逻辑的属性。
这是一个例子:
class Customer {
public Guid ID { get; private set; }
public Customer(Guid id, firstName, lastName, ...) {
ID = id;
this.AddEvent(new CustomerCreatedEvent(id, firstName, ....);
}
public void ChangeName(firstName, lastName) {
this.AddEvent(new CustomerRenamedEvent(this.ID, firstName, lastName),
}
}
Custom 只有 ID 属性,因为它需要将它添加到它生成的每个事件中。 FirstName 和 LastName 被省略,因为即使调用 ChangeName 方法也不需要它们。它只记录发生这种情况的事件。如果您的逻辑需要 FirstName,那么您可以添加它。您可以省略任何不需要的属性。
你的 Repository 在这种情况下将只保存事件而不关心客户的属性值。
在阅读方面,您可能需要这些属性,因为您会将它们显示给您的用户。
如果您的聚合不是事件源的,那么您可能需要聚合上的更多属性来实现它的逻辑,并且它们将被保存到数据库中。
这是一个例子:
class Customer {
public Guid ID { get; private set; }
public string FirstName { get; private set; }
public string LastName { get; private set; }
public void ChangeName(firstName, lastName) {
FirstName = firstName;
LastName = lastName;
}
}
在这种情况下,您的 Repository 将需要这些属性,因为它将生成一个查询以使用新值更新数据库。
不确定 "Customer entity doesn't make sense" 是什么意思。
您指出的文本意味着您不必为整个系统甚至整个有界上下文建模可重用的实体(不要为现实生活中的可重用事物建模)。这样做是一个糟糕的设计。
您必须为执行操作的聚合建模。您仅向聚合提供执行该操作和聚合响应所需的数据,即域所遭受的变化,这是您必须坚持的。
为什么实体和 V.O.'s then?
为了模型的一致性,封装和解耦是基础部分,但这些是实现细节。对于 DDD,重要的是不同的角色(或概念)。
在为聚合提供数据(构造函数、函数调用参数等)时,聚合必须知道它是否正在使用实体 and/or 和 V.O。建立它的响应。
如果域操作意味着实体属性的更改(在整个系统中具有唯一标识的东西),则聚合的响应(检查所有规则和不变量后)应包括新属性值和允许保留更改的实体的标识。
因此,默认情况下,每个聚合都有自己的实体,具有聚合操作所需的唯一标识和属性。
一个聚合可以有一个带有 ID 及其名称的 Customer 实体。
另一个聚合可能有一个带有 ID 及其 Karma 点的客户实体。
所以每个聚合都有自己的 inner 客户实体可以使用。当您提供聚合时,您传递客户数据(即 ID 和名称或 ID 和 Karma 点)并且聚合将该信息视为一个实体(如果有结构,class 等,这是实现细节的问题在内部聚合以表示实体)。
一件重要的事情:如果您只需要处理实体 ID,则将其视为 V.O。 (CustomerIdentityVO) 因为 ID 是不可变的,并且可能在该操作中,您只需要在持久性的某个字段中写入此 CustomerIdentityVO,而不更改任何 Customer 属性。
这是标准视力。一旦您开始识别与多个聚合相关的通用结构,或者一个聚合可以使用相同的数据执行多个操作,您就可以开始重构、重用等。这只是良好的 OOP 设计和 SOLID 原则的问题。
请注意,我正在努力超越实施细节。我知道你几乎总是会有不需要的工件,这取决于编程范式类型、所选的编程语言等。但这种方法有助于避免你可能拥有的更糟糕的工件。
推荐读物:
http://blog.sapiensworks.com/post/2016/07/29/DDD-Entities-Value-Objects-Explained
http://blog.sapiensworks.com/post/2016/07/14/DDD-Aggregate-Decoded-1
http://blog.sapiensworks.com/post/2016/07/14/DDD-Aggregate-Decoded-2
https://blog.sapiensworks.com/post/2016/07/14/DDD-Aggregate-Decoded-3
和
https://blog.sapiensworks.com/post/2016/08/19/DDD-Application-Services-Explained
完整的拼图视觉。
关于以下摘录,关于 cqrs 和 ddd,摘自 领域驱动设计的模式、原则和实践作者:Nick Tune、Scott Millett
这是否意味着命令端的域模型可以省略大部分业务属性? 例如,客户实体会是什么样子?
客户实体可以省略名字、姓氏等吗? 如果是这样,这些业务属性在哪里?仅在 CustomerEntity 中的读取模型中?
或者除了包含所有业务属性的 CustomerEntity 之外,可能还会有 CustomerAggregate 用 1:1 关系包装 CustomerEntity,并且命令对象会在 CustomerAggregate 上运行? (对我来说似乎很奇怪)。
“客户实体没有意义”是什么意思?
如果您正在使用 Event Sourcing 那么您确实可以对聚合进行建模而无需添加它们不需要实现业务逻辑的属性。
这是一个例子:
class Customer {
public Guid ID { get; private set; }
public Customer(Guid id, firstName, lastName, ...) {
ID = id;
this.AddEvent(new CustomerCreatedEvent(id, firstName, ....);
}
public void ChangeName(firstName, lastName) {
this.AddEvent(new CustomerRenamedEvent(this.ID, firstName, lastName),
}
}
Custom 只有 ID 属性,因为它需要将它添加到它生成的每个事件中。 FirstName 和 LastName 被省略,因为即使调用 ChangeName 方法也不需要它们。它只记录发生这种情况的事件。如果您的逻辑需要 FirstName,那么您可以添加它。您可以省略任何不需要的属性。
你的 Repository 在这种情况下将只保存事件而不关心客户的属性值。
在阅读方面,您可能需要这些属性,因为您会将它们显示给您的用户。
如果您的聚合不是事件源的,那么您可能需要聚合上的更多属性来实现它的逻辑,并且它们将被保存到数据库中。
这是一个例子:
class Customer {
public Guid ID { get; private set; }
public string FirstName { get; private set; }
public string LastName { get; private set; }
public void ChangeName(firstName, lastName) {
FirstName = firstName;
LastName = lastName;
}
}
在这种情况下,您的 Repository 将需要这些属性,因为它将生成一个查询以使用新值更新数据库。
不确定 "Customer entity doesn't make sense" 是什么意思。
您指出的文本意味着您不必为整个系统甚至整个有界上下文建模可重用的实体(不要为现实生活中的可重用事物建模)。这样做是一个糟糕的设计。
您必须为执行操作的聚合建模。您仅向聚合提供执行该操作和聚合响应所需的数据,即域所遭受的变化,这是您必须坚持的。
为什么实体和 V.O.'s then?
为了模型的一致性,封装和解耦是基础部分,但这些是实现细节。对于 DDD,重要的是不同的角色(或概念)。
在为聚合提供数据(构造函数、函数调用参数等)时,聚合必须知道它是否正在使用实体 and/or 和 V.O。建立它的响应。
如果域操作意味着实体属性的更改(在整个系统中具有唯一标识的东西),则聚合的响应(检查所有规则和不变量后)应包括新属性值和允许保留更改的实体的标识。
因此,默认情况下,每个聚合都有自己的实体,具有聚合操作所需的唯一标识和属性。
一个聚合可以有一个带有 ID 及其名称的 Customer 实体。 另一个聚合可能有一个带有 ID 及其 Karma 点的客户实体。
所以每个聚合都有自己的 inner 客户实体可以使用。当您提供聚合时,您传递客户数据(即 ID 和名称或 ID 和 Karma 点)并且聚合将该信息视为一个实体(如果有结构,class 等,这是实现细节的问题在内部聚合以表示实体)。
一件重要的事情:如果您只需要处理实体 ID,则将其视为 V.O。 (CustomerIdentityVO) 因为 ID 是不可变的,并且可能在该操作中,您只需要在持久性的某个字段中写入此 CustomerIdentityVO,而不更改任何 Customer 属性。
这是标准视力。一旦您开始识别与多个聚合相关的通用结构,或者一个聚合可以使用相同的数据执行多个操作,您就可以开始重构、重用等。这只是良好的 OOP 设计和 SOLID 原则的问题。
请注意,我正在努力超越实施细节。我知道你几乎总是会有不需要的工件,这取决于编程范式类型、所选的编程语言等。但这种方法有助于避免你可能拥有的更糟糕的工件。
推荐读物:
http://blog.sapiensworks.com/post/2016/07/29/DDD-Entities-Value-Objects-Explained http://blog.sapiensworks.com/post/2016/07/14/DDD-Aggregate-Decoded-1 http://blog.sapiensworks.com/post/2016/07/14/DDD-Aggregate-Decoded-2 https://blog.sapiensworks.com/post/2016/07/14/DDD-Aggregate-Decoded-3
和
https://blog.sapiensworks.com/post/2016/08/19/DDD-Application-Services-Explained
完整的拼图视觉。