I have a web application where a user can customize one or more sandwiches to order.
Once the user has set up his sandwich, the sandwich is added to an unsubmitted order.
The user can add to the order has many different sandwiches has he or she likes.
When the user is satisfied he can submit the order for further processing.
I have decided to model three aggregates:
Order has a collection of
OrderItem references a
Sandwich of the
Sandwich as a reference to its
User completes a
Sandwich and submits it, a new
Order for that
Sandwich should be created if it does not already exist and the new
Sandwich should be assigned to the
You might think of the unsubmitted
Order as the shopping cart of an e-commerce application.
I'm trying to obey the rule: one aggregate per transaction, but I'm having difficulties finding an implementation that models this situation clearly.
Theoretically, if I want to keep the
Order and the
Sandwiches consistent, I must first create an
Order and the
Sandwiches and then save them together. This involves creating and storing two aggregates in one transaction, which breaks the rule.
To avoid creating two aggregates at once in one transaction, I decided to create a
Sandwich first, which will generate a
SandwichCreated domain event. Handling
SandwichCreated will create an
Order, if it does not exists, and then the
Order will add the
Sandwich as an
OrderItem, generating a
SandwichAddedToOrder will be used to update the order reference on the
Sandwich. This approach requires the concept of a "currently unsubmitted"
Order for the
User, because I otherwise would not be able to know which
Sandwich belongs to which
What I find "wrong" about this approach: there is a heavy "back and forth" between the
Sandwich and the
Order aggregates that obfuscates the actual business logic workflow. Another issue would be if the sequence of the events breaks it might leave the system in an inconsistent state, but I guess that is a side effect of allowing eventual consistency.
Is there a better approach to handle this kind of situation while still obeying the "one transaction per aggregate rule"? Additionally, how can I model this workflow in, preferably, one place so that it is not lost in many different event handlers making it hard to read and reason about?
Edit with a clarification:
I have decided to model a
Sandwich as an aggregate, because each
Sandwich of every
User is retrieved without considering the
Order. When the cooks prepare the sandwiches, they do not care about which
Sandwich belongs to, they want a list of
A cook can choose a
Sandwich to prepare and can change its status to something like: "Preparing", "Ready to pick up", and "Ingredients not available". Once all the
Sandwiches of an
Order are either "Ready to pick up" or "Cannot prepare" the
Order is considered "Complete" and payment will be requested.
To be able to "choose a
Sandwich to prepare" and individually change its status, a
Sandwich must have an identity, making it an aggregate.
Another use case is that the cook is able to write a "reason" why a
Sandwich might not be available, further cementing (at least to me) the fact that a
Sandwich can be considered an aggregate.
It is true that I could use
OrderItem to hold all
Sandwich status, but I don't like this idea because the business might add beverages and delivery. Then I would have
OrderItem for delivery and beverages with some bogus status that exists solely because a
Sandwich has status, thus forcing other entities to have attributes that are not needed.