如何解决DDD 是否违反了一些 OOP 原则?
也许我可能会混淆或误解一些基本原则。在基于 Axon 的事件溯源项目中应用 DDD 时,您定义了许多聚合,每个聚合都有几个命令处理程序方法来验证聚合的状态更改是否有效,以及应用每个请求的状态更改的相应事件处理程序方法集合。
编辑开始: 所以聚合可能看起来像
@Aggregate
public class Customer {
@AggregateIdentifier
private CustomerId customerId;
private Name name;
@CommandHandler
private Customer(CreateCustomerCommand command) {
AggregateLifecycle.apply(new CustomerCreatedEvent(
command.getCustomerId(),command.getName());
}
@EventSourcingHandler
private void handleEvent(CustomerCreatedEvent event) {
this.customerId = event.getCustomerId();
this.name = event.getName();
}
}
编辑结束
所以我的第一个问题是:我是否认为聚合没有直接实现任何状态更改方法(更改聚合实例的属性的典型公共方法)但所有状态更改方法都定义在与Axon 的命令网关?
编辑开始: 换句话说,聚合是否应该定义负责向框架发送命令的设置器,这将导致通过将以下代码添加到聚合中来调用相应的命令处理程序和事件处理程序?
public void setName(String name){
commandGateway.send(new UpdateNameCommand(this.customerId,name));
}
@CommandHandler
private void handleCommand(UpdateNameCommand command) {
AggregateLifecycle.apply(new NameUpdatedEvent(command.getCustomerId(),command.getName());
}
@EventSourcingHandler
private void handleEvent(NameUpdatedEvent event) {
this.name = event.getName();
}
这似乎违反了一些建议,因为需要从聚合内部引用网关。
或者您是否通常在单独的服务类中定义这些方法,然后将命令发送到框架。
@Service
public class CustomerService {
@Autowired
private CommandGateway gateway;
public void createCustomer(String name) {
CreateCustomerCommand command = new CreateCustomerCommand(name);
gateway.send(command);
}
public void changeName(CustomerId customerId,String name) {
UpdateNameCommand command = new UpdateNameCommand (customerId,name);
gateway.send(command);
}
}
这对我来说似乎是正确的方法。这似乎使(至少在我看来)外部世界无法直接访问聚合的行为(所有命令和事件处理程序方法都可以设为私有),就像一个更“传统”的对象,它们是请求状态更改的入口点...
编辑结束
其次:这是否与定义其(公共)接口方法进行交互的每个类的 OOP 原则相矛盾?换句话说,这种方法是不是使对象或多或少变成了无法直接交互的转储对象?
感谢您的反馈, 库尔特
解决方法
所以我的第一个问题是:我是否认为聚合没有直接实现任何状态更改方法(更改聚合实例的属性的典型公共方法)但所有状态更改方法都定义在与Axon 的命令网关?
绝对不会!聚合本身负责任何状态更改。
在使用事件溯源时,可能误解是围绕聚合中的命令处理程序(方法)。在这种情况下,命令处理程序(方法)不应直接应用更改。相反,它应该应用一个事件,然后在同一个聚合实例中调用一个事件源处理程序(方法)来应用状态更改。
无论是否使用事件溯源,聚合都应仅公开操作(例如命令处理程序),并根据这些操作做出决定。最好,(命令处理程序的)聚合不暴露聚合边界之外的任何状态。
其次:这是否与定义其(公共)接口方法进行交互的每个类的 OOP 原则相矛盾?换句话说,这种方法是不是使对象或多或少变成了无法直接交互的转储对象?
如果聚合依赖外部组件来管理其状态,情况就会如此。但它没有。
问题编辑后的其他反应
所以我的第一个问题是:我是否认为聚合没有直接实现任何状态改变方法(改变聚合实例属性的典型公共方法),但所有状态改变方法都定义在一个单独的域服务交互中使用 Axon 的命令网关?
我认为恰恰相反。问题中的第一个聚合是一个在其内部具有所有状态更改操作的聚合示例。将 setter 暴露给“更改状态”是一个非常糟糕的想法。它强制逻辑决定何时在聚合之外更改此值。 那,在我看来,是对 OOP 的“违反”。
不应指示 DDD 和/或 CQRS 上下文中的聚合更改状态。相反,他们应该收到要做出反应的实际业务意图。这就是命令应该反映的内容:业务意图。因此,聚合可能会更改某些属性,但只是为了确保在此之后发生的任何命令的行为方式都反映了之前发生的事情。对于事件源聚合,有一个额外的中间步骤:应用事件。需要这样做以确保从过去的决策中获取聚合产生完全相同的状态。另请注意,这些事件不是“状态更改决策”,而是“业务决策”。状态变化是其结果。
最后评论
最后显示的服务类将是典型的交互。发送命令的组件,不直接与 Aggregate 实例交互。但是,将“UpdateNameCommand”与上一个示例中的“setName”进行比较让我失望,因为 Commands 不应该是“C(R)UD”操作,而是实际的业务操作。在这种情况下,UpdateName 可能是这样的业务操作。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。