I get your question, and from your question I guess you are contemplating how to decide your aggregates. I believe you might need to rethink the options you have provided.
Below are few statements about aggregates and event sourcing that could help you designing your system
- As per Evans’ DDD book, Aggregates are intended to define the consistency and transactional boundaries of your system.
- Aggregate is a group of entities (objects) put together. And one of these object is the aggregate root
- We get the aggregate and save the aggregate from the repository using the aggregate root.
- Each Aggregate (and naturally aggregate root) has a unique id
- When using event sourcing, each class of aggregate will NOT have a stream. But each instance of the aggregate will have a stream. (So if Order is your aggregate, all the events related to all the orders will NOT be in one stream. But all the events related to one specific order will be in one stream. The stream id could be probably order#order-id. So there will be multiple order streams)
- It is good to have small and many event streams. As while replaying, we will often replay only one of the specific stream to build the aggregate.
- When you are saving a event to steam, and stream already had 5 events in it when you read it to construct your aggregate, you could specify to store the event only if there are exactly 5 events in it. This is the way we achieve the transnational boundary.
To design a event sourced system, one of the most important thing is to understand how the transactional boundary will be applied to an aggregate (i.e the last point). So I am explaining that with an example below.
- Assume you have order as aggregate in your system
- Assume you have an order with order id 123
- Then you would be storing events related to this order in a stream perhaps named order-123
- Assume there are 5 events in the stream already
- Now we are getting 2 requests (nearly) simultaneously. One to cancel the order and one to mark it complete.
- Now in each of the order, the order aggregate will be build using the 5 events in the stream. In the cancellation order request, we will probably add order cancelled event as the 6th event in the aggregate. And in the mark complete request, we will add marked complete event as the 6th event. And the next step for the both the requests is to save the new events in the event store. At this point one of the request (first one to save) will succeed and the other will fail saying the "expected to find 5 events but found 6"
- So we will be able to perform only one operation at a time for one instance of an aggregate.
Hearing your problem. I believe your aggregate should be the products you hold in the ware house. For example, then your stream ids would become for example product-sony-bravia-32-inch-lcd, product-sony-bravia-32-inch-led, product-samsung-32-inch-lcd, and so on...
I would design the aggregate Product as below in c# (only signatures provided)
public class Product
public string GetProductCode()
throw new NotImplementedException();
public void AddProduct(string serialNumber, string location)
public void Order(int orderId)
//In this aggregate, I will know the number of units of this product that I have in the ware house
//So the possibility to over order is not there
public void PackOrder(int orderId)
public void ShipOrder(int orderId)
public void ChangeLocation(string serialNumber, string newLocation)
For knowing the the pending orders, pending packing, pending shipping, and item wise stock, I would build read models.
Regarding replaying of all the events: Generally any event store should give two numbers for every event that is stored. One is the stream event version number and other is overall version number. I am using SqlStreamStore , and it gives these two numbers for every event. It would be hard if your event store doesn't give these two numbers.