2

我正在尝试域驱动设计和事件采购。我打算使用(在C#中开发)NServiceBus,JOliver的EventStore和NES绑定它们。我已经有了一个简单案例的基础结构(只有一个具有值对象的聚合根)。DDD:继承和交易

我正在阅读埃文斯蓝色书籍,我正在尝试开发一个简单的领域模型,并从我的工作领域(暖通空调维护公司的ERP和CRM)中抽取实例。

我正在建模一个简单的子域,即HVAC机器和它们之间的关系。机器有各种类型,例如熔炉,燃烧器,空调,压缩机,通用组件。每台机器可以有多台儿童机器。所有机器类型共享一些常见数据和一些常见行为。但每种类型都有其他数据和特定行为,例如,您只能将燃烧器对象添加到炉子中。

我的分析的第一个结果是每台机器都应该是一个聚合根(从NES中的AggregateBase继承而来),因为它必须能够保存对特定机器的引用(例如插入涉及单个机器的修复记录,故障记录等),还可以减少大型机器树中的并发问题。

我的假设是这样如下:

public class Machine : AggregateBase 
{ 
    public DateTime InstallationDate { get; private set; } 

    public Guid ManufacturerId { get; private set; } 

    public Guid ModelId { get; private set; } 
} 

public class Furnace : Machine 
{ 
    public List<Burner> burners { get; private set; } 

    // other furnace properties 

    public void AddBurner(Burner burner) 
    { 
     // perform validation 
     this.Apply<BurnerAdded>(x=> x.burnerAdded = burner); 
    } 

    public void Handle(BurnerAdded @event) 
    { 
     this.burners.Add(@event.burnerAdded); 
    } 
} 

public class Burner : Machine 
{ 
    // burner specific properties/methods 
} 

,但是我有些疑惑:

  1. 这是代表我的域名以正确的方式?我读过类继承是不鼓励的,但在我看来,这是一个完美的例子(一个燃烧器是一台机器,一个炉子也是如此)。我将仅限于一个级别的继承。

  2. 是否有可能通过Event Sourcing实现类继承?特别是建议的技术栈(nServiceBus,EventStore,NES)?

  3. 我应该如何执行添加子机(例如炉子的燃烧器)?此操作可分为两部分:

    1. 将新的Burner添加到Burner存储库。
    2. 添加一个引用到燃烧器的父炉燃烧器列表 但这两个操作全局修改两个聚合根,所以操作应该在两个独立的命令处理程序/交易中执行......但第二个取决于第一个...这是一些模型错误的证据吗?我可以批量nServicebusMessages在一起执行操作,但我读了这是不好的...

    如果我让孩子机器引用父,父母丢失儿童机器列表(其中需要进行验证),我无法查询事件采购存储库以获取Guid以外的其他属性。

预先感谢在讨论任何贡献,

回答

3

#1,我会说不。您示例中的聚合根应该是Furnace(仅)。正如你目前所拥有的那样,燃烧器应该被模拟为炉子上的一个集合,但不应该是一个聚合根。除了事实上你的刻录机现在是一个通过继承的聚合根(因为Burner => Machine => AggregateBase),我没有看到继承本身就是一个问题。

如果刻录机只在炉的情况下存在,你也许并不需要一个刻录机库 - 你将永远燃烧器添加到炉。我不清楚创建新的刻录机本身是否有趣,并且需要自己的事件。这个问题的答案将推动你是否需要这两个事件或只是“增加”事件。

+0

感谢您的帮助。问题在于燃烧器独立于炉子存在,它只是一个可以在熔炉之间切换的组件(因此它肯定具有标识),并且可以被其他域对象直接引用(例如服务事件) – Max 2013-02-18 08:49:14

1
  1. 我会避免继承。相反,请创建单个机器集合并创建本机器将引用的描述符对象。描述符将是一个值对象,可以区分不同的机器类型。如果绝对必要,请使用此对象的继承,但不要对聚合本身使用继承。

  2. 使用类继承与ES的能力仅由串行器的限制。 ES并没有规定数据如何被序列化,但如果你使用类似Protobuf或Newtonsoft.Json的东西,它们支持继承。但是,在JSON的情况下,使用继承确实在JSON输出中放置了$ type属性。

  3. 这取决于燃烧器是否需要独立于炉的存在。如果不是,那么它是炉体骨料的价值对象或实体部分,应该与炉体一起坚持。如果是的话,那么它必须是一个聚合体,并且应该首先被创建并且然后被添加到熔炉中。这可以通过一个NSerivceBus传奇来实现,其中AddBurnerToFurnaceCommand通过首先启动相应的传奇来处理,传递一个命令来创建一个Burner。一旦燃烧器被创建,创建燃烧器和熔炉之间的关联。炉子只会引用Guid的燃烧器。在ES中,所有查询通常都是通过预测来处理的,只有行为通过ID为规范聚合调用事件存储。

+0

感谢您的帮助。是的,燃烧器独立于炉子存在,它只是一个可以在熔炉之间切换的组件(所以它肯定有一个标识),并且可以被其他域对象直接引用(例如服务事件) – Max 2013-02-18 07:53:45