Search code examples
c#domain-driven-designcqrsservicebusdomain-events

Who is responsible for entity's mutation when domain event is raised? DDD


I've been learning about CQRS/ES. Looking at small example projects I often see events mutating the entity state. For instance If we look at the Order aggregate root:

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);
    }
}

when OrderLineAddedEvent is applied it mutates Order by adding new order line. But I don't understand these things:

  • if it is a right approach how then the changes made are persisted?
  • Or should I publish the event somehow to a corresponding handler from Order? How do I implement this technically? Should I use a service bus to transmit events?

Solution

  • I am also still experimenting with ES so this is still somewhat of an opinion rather than any guidance :)

    At some stage I came across this post by Jan Kronquist: http://www.jayway.com/2013/06/20/dont-publish-domain-events-return-them/

    The gist of it is that event should be returned from the domain rather than being dispatched from within the domain. This really struck a chord with me.

    If one were to take a more traditional approach where a normal persistence-oriented repository is used the Application Layer would handle transactions and repository access. The domain would simply be called to perform the behaviour.

    Also, the domain should always stick to persistence ignorance. Having an aggregate root maintain a list of events always seemed somewhat odd to me and I definitely do not like having my ARs inheriting from some common base. It does not feel clean enough.

    So putting this together using what you have:

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

    In my POC I have also not been using an IEvent marker interface but rather just an object.

    Now the Application Layer is back in control of the persistence.

    I have an experimental GitHub repository going:

    I haven't had time to look at it for a while and I know I have already made some changes but you are welcome to have a look.

    The basic idea is then that the Application Layer will use the EventStore/EventStream to manage the events for an aggregate in the same way that the Application Layer would use a Repository. The EventStream will be applied to the aggregate. All events returned from the domain behaviours would be added to the EventStream after which it is persisted again.

    This keeps all the persistence-oriented bits out of the domain.