First of all, let me state that I am new to Command Query Responsibility Segregation and Event Sourcing (Message-Drive Architecture), but I'm already seeing some significant design benefits. However, there are still a few issues on which I'm unclear.
Say I have a Customer
class (an aggregate root) that contains a property called postalAddress
(an instance of the Address
class, which is a value object). I also have an Order
class (another aggregate root) that contains (among OrderItem
objects and other things) a property called deliveryAddress
(also an instance of the Address
class) and a string property called status
.
The customer places an order by issueing a PlaceOrder
command, which triggers the OrderReceived
event. At this point in time, the status of the order is "RECEIVED"
. When the order is shipped, someone in the warehouse issues an ShipOrder
command, which triggers the OrderShipped
event. At this point in time, the status of the order is "SHIPPED"
.
One of the business rules is that if a Customer
updates their postalAddress
before an order is shipped (i.e., while the status is still "RECEIVED"
), the deliveryAddress
of the Order
object should also be updated. If the status of the Order
were already "SHIPPED"
, the deliveryAddress
would not be updated.
Question 1. Is the best place to put this "conditionally cascading address update" in a Saga (a.k.a., Process Manager)? I assume so, given that it is translating an event ("The customer just updated their postal address...") to a command ("... so update the delivery address of order 123").
Question 2. If a Saga is the right tool for the job, how does it identify the orders that belong to the user, given that an aggregate can only be retrieved by it's unique ID (in my case a UUID)?
Continuing on, given that each aggregate represents a transactional boundary, if the system were to crash after the Customer
's postalAddress
was updated (the CustomerAddressUpdated
event being persisted to the event store) but before the OrderDeliveryAddressUpdated
could be updated (i.e., between the two transactions), then the system is left in an inconsistent state.
Question 3. How are such "violations" of consistency rules detected and rectified?
OrderProcessManager
object could provide a cache/index (a map of customer UUIDs to a vector of order UUIDs for those orders that have not been shipped, in this way being "stateful"), and when theOrderProcessManager
sees theOrderShipped
event, it simply removes the order UUID from it's internal cache, but your idea of arbitrary key/value pairs stored in the event store against an aggregate is insightful. – Hooper