gpt4 book ai didi

domain-driven-design - 使用事件溯源时在哪里验证业务规则

转载 作者:行者123 更新时间:2023-12-04 12:03:12 26 4
gpt4 key购买 nike

我实现了事件源实体(在域驱动设计中它被称为聚合)。创建丰富的域模型是一种很好的做法。领域驱动设计 (DDD) 建议尽可能将所有与业务相关的事物放入核心实体和值(value)对象中。

但是在将这种方法与事件溯源结合使用时存在一个问题。与事件源系统中的传统方法相比,首先存储事件,然后在构建实体以执行某些方法时应用所有事件。

基于此,最大的问题是把业务逻辑放在哪里。通常,我想要一种方法,例如:

public void addNewAppointment(...)

在这种情况下,我希望该方法确保不违反任何业务规则。如果是这种情况,将引发异常。

但是在使用事件溯源时,我必须创建一个事件:
Event event = new AppointmentAddedEvent(...);
event store.save(event);

现在,我探索了两种在存储事件之前检查业务规则的方法。

首先,检查应用层内的业务规则。 DDD 中的应用层是委托(delegate)层。实际上,它不应该包含任何业务逻辑。它应该只委托(delegate)诸如获取核心实体、调用方法和保存东西之类的事情。在此示例中,将违反此规则:
List<Event> events = store.getEventsForConference(id);

// all events are applied to create the conference entity
Conference conf = factory.build(events);

if(conf.getState() == CANCELED) {
throw new ConferenceClosed()
}

Event event = new AppointmentAddedEvent(...);
event store.save(event);

显然,向取消的 session 添加约会的业务规则不应该泄露到非核心组件中。

我知道的第二种方法是将命令的处理方法添加到核心实体:
class Conference {
// ...

public List<Event> process(AddAppointmentCommand command) {
if(this.state == CANCELED) {
throw new ConferenceClosed()
}

return Array.asList(new AppointmentAddedEvent(...));
}

// ...
}

在这种情况下,好处是业务规则是核心实体的一部分。但是这违反了关注点分离原则。现在,实体负责创建存储在事件存储中的事件。除此之外,一个实体负责创建事件感觉很奇怪。我可以争论为什么一个实体可以处理事件是很自然的。但是为存储而不是自然发布而创建领域事件感觉是错误的。

你们中有人遇到过类似的问题吗?你是如何解决这些问题的?

现在,我将只使用应用程序服务解决方案中的业务规则。它仍然是一个地方并且还可以,但它违反了一些 DDD 原则。

我期待您对 DDD、事件溯源和传入更改验证的想法和经验。

提前致谢

最佳答案

我喜欢这个问题。当我第一次问这个问题时,那是在遵循模式和挑战自己了解真正发生的事情之间的突破。

the big question is where to put the business logic



通常的答案是“你以前做过的同一个地方”——在域实体的方法中。您的“第二种方法”是通常的想法。

But there is a violation of separation of concerns principle.



这不是真的,但它确实看起来很奇怪。

考虑一下我们在保存当前状态时通常会做什么。我们运行一些查询(通常通过存储库)以从记录簿中获取原始状态。我们使用该状态来创建实体。然后我们运行命令,实体在其中创建新状态。然后我们将对象保存在存储库中,它将原始状态替换为记录簿中的新状态。

在代码中,它看起来像
state = store.get(id)
conf = ConferenceFactory.build(state)
conf.state.appointments.add(...)
store.save(id, conf.state)

我们在事件溯源中真正做的是用持久的事件集合替换可变状态
history = store.get(id)
conf = ConferenceFactory.build(history)
conf.history.add(AppointmentScheduled(...))
store.save(id, conf.history)

在成熟的商业领域,如会计或银行,无处不在的语言包括事件历史: journal , ledger , transaction history ,... 之类的东西。在这些情况下,事件历史是域的固有部分。

在其他领域——比如日历调度——我们(还没有?)在领域语言中没有类似的实体,所以当我们改变事件时感觉就像我们在做一些奇怪的事情。但核心模式是相同的——我们从记录簿中提取历史,我们操纵历史,我们将更新保存到记录簿中。

因此,业务逻辑发生在与往常相同的地方。

也就是说,是的,域逻辑知道事件。

一个可能有帮助的练习:放开“面向对象”的约束,只考虑功能......
static final List<Event> scheduleAppointment(List<Event> history, AddAppointmentCommand addAppointment) {

var state = state(history)

if(state == CANCELED) {
throw new ConferenceClosed()
}

return Array.asList(new AppointmentAddedEvent(...));
}

private static final State state(List<Event> history) {...}

关于domain-driven-design - 使用事件溯源时在哪里验证业务规则,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51408707/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com