4

I have been doing a lot of reading/watching on event sourcing but some things still do not quite make sense. My application is a warehouse management system where we have:

  • Deliveries of stock into the system
  • Movement of stock around stock locations (pallets and shelving bays)
  • Orders which contain order items (i.e. products with qty)
  • Picking and packing of orders (more stock movements)
  • Shipping of orders (stock leaving the system).

The idea of what my aggregate roots are in this case are a little confusing. For example, I could have:

  • A product Inventory aggregate root. This could contain information about all stock movements of the product around the warehouse as well as information on what has come in on what deliveries and what has gone out of orders.

Or alternatively (which I feel is probably more appropriate) split down each process into its own aggregate root:

  • Delivery (who it has come from, what stock & how much).
  • Stock Movements (when its in the warehouse, how does the stock move around)
  • Orders (what someone has ordered)
  • Shipments (how it leaves)

However, by splitting down like this the whole process is no longer replayable as there are multiple event streams that would have to be replayed inter-twined with one another.

I assume in the above each separate aggregate will emit events that can be picked up by another process for it to continue.

I know I am missing something here in the general process but can't quite figure it out.

3

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.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.