Domain driven design 在响应域事件时加载聚合
我正在使用领域驱动设计和事件源实现一个应用程序。我正在SQL Server的DomainEvents表中存储所有域事件 我有以下汇总:Domain driven design 在响应域事件时加载聚合,domain-driven-design,event-sourcing,aggregateroot,domain-events,Domain Driven Design,Event Sourcing,Aggregateroot,Domain Events,我正在使用领域驱动设计和事件源实现一个应用程序。我正在SQL Server的DomainEvents表中存储所有域事件 我有以下汇总: - City + Id + Enable() + Disable() - Company + Id + CityId + Enable() + Disable() - Employee + Id + CompnayId + Enable() + Disable() 每一个都封装了自己的域逻辑
- City
+ Id
+ Enable()
+ Disable()
- Company
+ Id
+ CityId
+ Enable()
+ Disable()
- Employee
+ Id
+ CompnayId
+ Enable()
+ Disable()
每一个都封装了自己的域逻辑和不变量。我将它们设计为单独的集合,因为一个城市可能有数千家(可能更多)公司,而公司也可能有大量员工。如果这些实体属于同一个聚合,我必须将它们加载在一起,在大多数情况下这是不必要的
调用Enable或Disable将生成域事件(例如,CityEnabled
,CompanyDisabled
或EmployeeEnabled
)。这些事件包含启用或禁用实体的主键
现在我的问题是一个新的要求,如果一个城市启用/禁用,我必须启用/禁用所有相关公司。如果公司已启用/禁用,则员工也需要这样做
在我的事件处理程序中,如果发生了例如CityDisabled
,将调用该事件处理程序
我需要为属于该城市的每个公司执行禁用公司命令
但我怎么知道哪些公司应该受到这种变化的影响呢
我的想法:
CityId
的公司才会更改其状态。即使我异步地这样做,它也会产生巨大的开销,将每个公司加载到这些事件上。而且,对于每个被禁用的公司,都应该重复相同的过程来禁用所有用户
也许您有更好的解决方案?您正在描述的问题可以通过收听
CityDisabled
事件的解决方案来解决。然后,它在该城市
中找到所有公司
的公司ID
(通过使用现有的读取模型
或通过维护城市ID
x公司ID
的私有状态),并向每个公司发送禁用公司
命令
这同样适用于CompanyDisabled
事件,关于禁用Employee
对不起,一个城市/公司/雇员对我来说似乎很难,这些似乎不是一般的普遍语言的术语,它不是很DDD,但是我认为你的设计在这个问题上是正确的。
你的要求是指在禁用城市时必须解雇一个公司禁用的事件吗? 如果不是,你的要求是一个被禁用的城市意味着所有的公司都被禁用,那么你要做的是在你的城市阅读模型投影上,你要收听城市被禁用的事件,并在阅读模型中标记被禁用的公司。(如果你的要求是为每个城市举办一场活动,那么康斯坦丁的回答是好的)
你的模型更像是一种儿童/父母关系——这打破了传统的“蓝皮书”思维,但我建议在你的领域中用更多的城市ID来表现这种关系 在我的应用程序中,类似这样的内容将被编码为public Task Handle(DoSomething command, IHandlerContext ctx)
{
var city = ctx.For<City>().Get(command.CityId);
var company = city.For<Company>().Get(command.CompanyId);
company.DoSomething();
}
public Company : Entity<City>
{
public void DoSomething()
{
// Parent is the City
if(Parent.Disabled)
throw new BusinessException("City is disabled");
Apply<SomethingDone>(x => {
x.CityId = Parent.Id;
x.CompanyId = Id;
...
});
}
}
公共任务句柄(DoSomething命令,IHandlerContext ctx)
{
var city=ctx.For().Get(command.CityId);
var company=city.For().Get(command.CompanyId);
公司。DoSomething();
}
上市公司:实体
{
公共无效剂量测定法()
{
//父母就是城市
如果(父级禁用)
抛出新的BusinessException(“城市已禁用”);
应用(x=>{
x、 CityId=Parent.Id;
x、 CompanyId=Id;
...
});
}
}
(Psuedo代码是NServiceBus样式的代码,使用my library Aggregates.NET)很可能您根本不必在域(写入)端明确强制执行“启用/禁用所有相关公司,如果启用/禁用城市”之类的规则 如果是这样的话,当一个城市被禁用时,没有必要在域内禁用所有公司。正如Charles在回答中提到的,只需引入一条规则,例如,“如果一家公司本身(直接)或其所在城市被禁用,则该公司被禁用”。公司及其员工也是如此 这条规则应该在读取端实现。read模型中的公司将有两个属性:第一个是启用的,直接从域映射;第二个是EnabledEffective,可根据公司的启用值及其所在城市的启用值进行计算。当CityDisabled事件发生时,读取模型的事件处理程序将遍历读取模型中城市的所有公司,并将其EnabledEffective属性设置为false;当CityEnabled事件发生时,处理程序将城市的每家公司的EnableDefEffective属性设置回其自己的Enabled值。它是您将在UI中使用的EnableDefEffective属性 对于CompanyEnabled/CompanyDisabled事件处理(对于员工而言),逻辑可能会稍微复杂一些,因为您必须同时考虑主办城市的事件信息和启用/禁用状态 如果公司处于启用/禁用(生效)状态