views:

154

answers:

1

I'm working on an event-sourced CQRS implementation, using DDD in the application / domain layer. I have an object model that looks like this:

public class Person : AggregateRootBase
{
    private Guid? _bookingId;

    public Person(Identification identification)
    {
        Apply(new PersonCreatedEvent(identification));
    }

    public Booking CreateBooking() {
        // Enforce Person invariants
        var booking = new Booking();
        Apply(new PersonBookedEvent(booking.Id));
        return booking;
    }

    public void Release() {
        // Enforce Person invariants
        // Should we load the booking here from the aggregate repository?
        // We need to ensure that booking is released as well.
        var booking = BookingRepository.Load(_bookingId);
        booking.Release();
        Apply(new PersonReleasedEvent(_bookingId));
    }

    [EventHandler]
    public void Handle(PersonBookedEvent @event) { _bookingId = @event.BookingId; }

    [EventHandler]
    public void Handle(PersonReleasedEvent @event) { _bookingId = null; }
}

public class Booking : AggregateRootBase
{
    private DateTime _bookingDate;
    private DateTime? _releaseDate;

    public Booking()
    {
        //Enforce invariants
        Apply(new BookingCreatedEvent());
    }

    public void Release() 
    {
        //Enforce invariants
        Apply(new BookingReleasedEvent());
    }

    [EventHandler]
    public void Handle(BookingCreatedEvent @event) { _bookingDate = SystemTime.Now(); }
    [EventHandler]
    public void Handle(BookingReleasedEvent @event) { _releaseDate = SystemTime.Now(); }
    // Some other business activities unrelated to a person
}

With my understanding of DDD so far, both Person and Booking are seperate aggregate roots for two reasons:

  1. There are times when business components will pull Booking objects separately from the database. (ie, a person that has been released has a previous booking modified due to incorrect information).
  2. There should not be locking contention between Person and Booking whenever a Booking needs to be updated.

One other business requirement is that a Booking can never occur for a Person more than once at a time. Due to this, I'm concerned about querying the query database on the read side as there could potentially be some inconsistency there (due to using CQRS and having an eventually consistent read database).

Should the aggregate roots be allowed to query the event-sourced backing store by id for objects (lazy-loading them as needed)? Are there any other avenues of implementation that would make more sense?

A: 

First of all, do you really really need event sourcing in you case? It looks pretty simple to me. Event sourcing has both advantages and disadvantages. While it gives you a free audit trial and makes your domain model more expressive, it complicates the solution.

OK, I assume that at this point you thought over your decision and you are determined to stay with event sourcing. I think that you are missing the concept of messaging as a means of communication between aggregates. It is described best in Pat Helland's paper (which is, BTW, not about DDD or Event Sourcing, but about scalability).

The idea is aggregates can send messages to each other to force some behavior. There can be no synchronous (a.k.a method call) interaction between aggregates because this would introduce consistency problems.

In you example, a Person AR would send a Reserve message to a Booking AR. This message would be transported in some asynchronous and reliable way. Booking AR would handle this message and if it is already book by another person, it would reply with ReservationRejected message. Otherwise, it would send ReservationConfirmed. These messages would have to be handled by Person AR. Probably, they would generate yet another event which would be transformed into e-mail sent to customer or something like that.

There is no need of fetching query data in the model. Just messaging. If you want an example, you can download sources of "Messaging" branch of Ncqrs project and take a look at ScenarioTest class. It demonstrates messaging between ARs using Cargo and HandlingEvent sample from the Blue Book.

Does this answer your question?

Szymon Pobiega
What sorts of consistency problems would you run into if you forced synchronous handling? It seems that it would simply be an issue of scalability to me, but I may be missing something.
JD Courtoy
I assumed that storage model doesn't support storing cross-aggregate transactions. If it supports, you are right that normal method calls would be ok (at least as far as consistency is concerned).
Szymon Pobiega
After taking a look through Pat Helland's paper, I see where you are coming from. In this particular system, I'm not concerned with near-infinite scalability (though, that would be a nice problem to have). As an implementation detail, we're using MSMQ. I believe 2PC using MSMQ is acceptable in our scenario. I do like the idea, though, and has given me food for thought.
JD Courtoy