There appears to be widespread agreement in the OOP community that the class constructor should not leave an object partly or even fully uninitialized.
What do I mean by "initialization"? Roughly speaking, the atomic process that brings a newly created object into a state where all of its class invariants hold. It should be the first thing that happens to an object, (it should only run once per object,) and nothing should be permitted to get hold of an un-initialized object. (Thus the frequent advice to perform object initialization right in the class constructor. For the same reason,
Initialize
methods are often frowned upon, as these break apart the atomicity and make it possible to get hold of, and use, an object that is not yet in a well-defined state.)
Problem: When CQRS is combined with event sourcing (CQRS+ES), where all state changes of an object are caught in an ordered series of events (event stream), I am left wondering when an object actually reaches a fully-initialized state: At the end of the class constructor, or after the very first event has been applied to the object?
Note: I'm refraining from using the term "aggregate root". If you prefer, substitute it whenever you read "object".
Example for discussion: Assume that each object is uniquely identified by some opaque Id
value (think GUID). An event stream representing that object's state changes can be identified in the event store by the same Id
value: (Let's not worry about correct event order.)
interface IEventStore
{
IEnumerable<IEvent> GetEventsOfObject(Id objectId);
}
Assume further that there are two object types Customer
and ShoppingCart
. Let's focus on ShoppingCart
: When created, shopping carts are empty and must be associated with exactly one customer. That last bit is a class invariant: A ShoppingCart
object that is not associated to a Customer
is in an invalid state.
In traditional OOP, one might model this in the constructor:
partial class ShoppingCart
{
public Id Id { get; private set; }
public Customer Customer { get; private set; }
public ShoppingCart(Id id, Customer customer)
{
this.Id = id;
this.Customer = customer;
}
}
I am however at a loss how to model this in CQRS+ES without ending up with deferred initialization. Since this simple bit of initialization is effectively a state change, wouldn't it have to be modelled as an event?:
partial class CreatedEmptyShoppingCart
{
public ShoppingCartId { get; private set; }
public CustomerId { get; private set; }
}
// Note: `ShoppingCartId` is not actually required, since that Id must be
// known in advance in order to fetch the event stream from the event store.
This would obviously have to be the very first event in any ShoppingCart
object's event stream, and that object would only be initialized once the event were applied to it.
So if initialization becomes part of the event stream "playback" (which is a very generic process that would likely work the same, whether for a Customer
object or a ShoppingCart
object or any other object type for that matter)…
- Should the constructor be parameter-less and do nothing, leaving all work to some
void Apply(CreatedEmptyShoppingCart)
method (which is much the same as the frowned-uponInitialize()
)? - Or should the constructor receive an event stream and play it back (which makes initialization atomic again, but means that each class' constructor contains the same generic "play back & apply" logic, i.e. unwanted code duplication)?
- Or should there be both a traditional OOP constructor (as shown above) that properly initializes the object, and then all events but the first are
void Apply(…)
-ied to it?
I do not expect of answer to provide a fully working demo implementation; I'd already be very happy if someone could explain where my reasoning is flawed, or whether object initialization really is a "pain point" in most CQRS+ES implementations.