域名事件被提出时谁负责实体的变异? DDD

时间:2015-02-25 12:26:17

标签: c# domain-driven-design cqrs servicebus domain-events

我一直在学习 CQRS / ES 。看一下小例子项目,我经常看到事件改变了实体状态。例如,如果我们查看Order 聚合根

public class Order : AggregateRoot {
    private void Apply(OrderLineAddedEvent @event) {
        var existingLine = this.OrderLines.FirstOrDefault(
            i => i.ProductId == @event.ProductId);

        if(existingLine != null) {
            existingLine.AddToQuantity(@event.Quantity);
            return;
        }

        this.OrderLines.Add(new OrderLine(@event.ProductId, @event.ProductTitle, @event.PricePerUnit, @event.Quantity));
    }

    public ICollection<OrderLine> OrderLines { get; private set; }

    public void AddOrderLine(/*parameters*/) {
        this.Apply(new OrderLineAddedEvent(/*parameters*/));
    }

    public Order() {
        this.OrderLines = new List<OrderLine>();
    }

    public Order(IEnumerable<IEvent> history) {
        foreach(IEvent @event in history) {
            this.ApplyChange(@event, false);
        }
    }
}

public abstract class AggregateRoot  {
    public Queue<IEvent> UncommittedEvents { get; protected set; }

    protected abstract void Apply(IEvent @event);

    public void CommitEvents() { 
        this.UncommittedEvents.Clear();
    }

    protected void ApplyChange(IEvent @event, Boolean isNew) {
        Apply(@event);
        if(isNew) this.UncommittedEvents.Enqueue(@event);
    }
}

当应用OrderLineAddedEvent时,它会通过添加新的订单行来改变Order。但我不明白这些事情:

  • 如果这是一个正确的方法,那么所做的更改是如何保持的?
  • 或者我应该以某种方式将事件发布到Order的相应处理程序?我如何在技术上实现这一点?我应该使用服务总线传输事件吗?

3 个答案:

答案 0 :(得分:5)

我还在尝试ES,所以这仍然是一种意见而不是任何指导:)

在某个阶段,Jan Kronquist发现了这篇文章:http://www.jayway.com/2013/06/20/dont-publish-domain-events-return-them/

它的要点是应该从域返回事件而不是从域内调度。这真的引起了我的共鸣。

如果要采用更加传统的方法,而使用正常的面向持久性的存储库,应用层将处理事务和存储库访问。只需调用域即可执行该行为。

此外,域名应始终坚持持久性无知。让聚合根维护一个事件列表对我来说似乎总是有点奇怪,我绝对不喜欢让我的AR继承一些共同的基础。它感觉不够干净。

所以用你所拥有的东西把它放在一起:

public OrderLineAddedEvent AddOrderLine(/*parameters*/) {
    return this.Apply(new OrderLineAddedEvent(/*parameters*/));
}

在我的POC中,我还没有使用IEvent标记界面,而只使用object

现在应用层重新控制了持久性。

我有一个实验性的GitHub存储库:

我暂时没有时间看它,我知道我已经做了一些改动,但欢迎你看一下。

基本的想法是,应用层将使用EventStore / EventStream来管理聚合的事件,其方式与应用程序相同图层将使用RepositoryEventStream将应用于汇总。从域行为返回的所有事件都将添加到EventStream,然后再次保留。

这使得所有面向持久性的位都不在域之外。

答案 1 :(得分:3)

实体不会神奇地更新自己。 Something (通常是服务)将调用实体的更新行为。因此,服务使用将生成和应用事件的实体,然后服务将通过存储库保存实体,然后它将从实体获取新事件并发布它们。

另一种方法是事件存储本身发布事件。

事件采购是指将实体状态表示为事件流,这就是实体通过生成和应用事件来更新自身的原因,它需要创建/添加对事件流的更改。该流也是它存储在db(即事件存储库)中的内容。

答案 2 :(得分:1)

最近我将我的实体分成两个对象。

首先是我称之为Document对象。这主要是一个仅限状态的ORM类,其所有配置都与信息的持久性有关。

然后我用Entity对象包装该Document,这实际上是一个包含所有行为的变异服务。

我的实体基本上是无状态的对象,当然除了包含的文档,但无论如何,我主要避免任何暴露于外部世界。