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:
- 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).
- 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?