I'm trying to figure out how to deal with complex domain model using CQRS/ES approach. Let's imagine we have e.g. Order domain entity, which handles both state and behavior of order. It has a Status property with transition rules for switching between statuses (implementing State pattern or any other kind of state machine). According to DDD principles, this logic should be implemented in Order class (representing the Order model) itself, having methods like approve(), cancel(), ship(), etc.
Looking at different public examples of this kind architecture, it turns out that domain entity and aggregate root is the same, and it handles both the state and behavior and even its own projection from events. Isn't it a violation of SRP?
But my question is more concrete: if I want to process new command (and apply new event), should I reconstitute entity from event stream (i.e. from write model and write db) and call its behavioral methods (which applies events to state) to handle business rules? Or just handle commands and events themselves, without having any write-model entity?
Pseudocode to illustrate:
class ApproveOrderHandler
{
private EventStore eventStore
// ...
public void handle(ApproveOrder event)
{
Order order = this.eventStore.findById(event.getOrderId()); // getting order projection from event store
order.approve(); // handling business logic
this.eventStore.save(order.releaseEvents()); // save new events (OrderApproved)
}
}
class Order extends AbstractAggregate
{
private Uuid id;
private DateTime takenAt;
private OrderStatus status;
// ...
public void approve()
{
this.status.approve(); // business rules blah blah
this.Apply(new OrderApproved(this.id)); // applying event
}
// ...
}
Isn't that overdoing or somewhat?
And what should I do with relationships between entities in event-sourcing? If they exist only in "read model", there is no point in domain entity class.
EDIT: or maybe I should store state snapshot in "read database" and recover entity for operations from it? But it breaks idea of "different models for read & write"...
EDIT2: fixed read/write models mistake