【发布时间】:2018-11-03 21:53:23
【问题描述】:
我最近开始了我第一次尝试使用领域驱动设计原则结合事件溯源和 CQRS 开发票务 Web 应用程序。
由于这是我第一次尝试摆脱传统的 CRUD 方法并进入 DDD 世界,我确信我有很多设计错误的东西,因为 DDD 需要付出很多努力才能提出正确的域分离和限界上下文等等
在我的设计中,我有接受命令、启动作业(工作单元)的命令处理程序,它们从聚合存储库加载所需的聚合(通过重播事件从事件存储加载聚合),并且它们通过每个聚合的公开操作来操作聚合,然后关闭作业。
聚合公开实际发出事件的操作。例如,company.Create(firmName, address, taxid, ...) 发出 CompanyCreated 事件,并将其应用于自身。当 Job 即将完成时,在该 Job 的上下文中加载的所有聚合中的所有事件都会被事件存储收集和保存。
现在,我遇到了一种情况,我确信这很常见,我在聚合之间存在关系。例如Customer 具有Contacts,或者SupportAgent 是Department 的成员。这些是我设计中的聚合。
让我们以Department 为例。 Department 的状态由标题、描述、一些其他属性以及属于该部门成员的那些代理的SupportAgent id 列表组成。 SupportAgent 的状态包括姓名、姓氏、电话号码、电子邮件……以及此代理所属部门的 Department id 列表。
现在,当处理AddAgentToDepartment(agentId, departmentId) 类型的命令时,会发出两个事件。为相应的代理发出DepartmentAdded,将部门id 添加到代理的状态,并为相应的部门发出SupportAgentAdded,将代理id 添加到部门的状态。
我的第一个问题是:将相关聚合的 id 保持在聚合状态中是否正确?“正确”是指最佳实践吗?还是有其他方法(例如,将关系保持在某种“DepartmentMemberManager”实体/聚合或其他东西中。实际上这个实体或这里的任何东西都是单例。在 DDD 世界中是否有这样的东西)?
我的另一个想法是关于事件回放。在前面的示例中,发出了两个事件,但是为了更新视图,只需要处理其中一个,因为这两个事件都描述了系统状态的完全相同的转换(代理和部门是链接的)。我选择只处理SupportAgentAdded 事件来更新视图。我的事件处理程序执行一个 SQL 脚本来更新相应的数据库表以反映系统的当前状态。
如果我们需要重放某些事件以使某个聚合的视图保持一致状态会发生什么?具体来说,当我想为支持代理重播事件时,只会重播 DepartmentAdded 事件,并且这些事件不会由任何人处理,因此不会更新视图。 为了使整个系统进入一致状态,是否应该部分重放事件存储中的某些事件或所有事件?
如果你是 DDD 和 ES 专家,或者至少你有经验,我想得到一些提示,告诉你我在做什么,或者在想什么,错了,我应该看什么方向。
【问题讨论】:
标签: domain-driven-design aggregate cqrs event-sourcing aggregateroot