TL;DR — The request is the event
Part of a series
Previous article: Time is money
Next article: Know your stuff
Engineers on both sides of the stack usually know what an event is. Frontend engineers think change events, blur events, input events (I only learnt about this recently), focus events, etc. Backend engineers think web-hooks, WebSocket events, server-sent events, I can’t think of anything more. So an event is basically a unit of change. Is there a limit to how big the unit of change should be? If a change can be broken down into smaller units of change do we? More importantly, how do we arrange events in an event sourced system?
The best way to answer what an event should be for event sourced systems would be to understand why Command Query Responsibility Segregation (CQRS) works so well with it. There is also Command Query Separation (CQS) which came first. In CQS we keep methods which mutate state separate from methods which read state — it’s a set of guidelines on how we should design services. CQRS takes that idea and goes pro, the read side of a service and the write side of a service are separated — it’s a set of guidelines on how every service in the system should be designed. You can read more about both here. Sometimes I feel like I’m trying to tell the difference between tiffany blue and turquoise. All of it boils down to a simple principle which is more important than knowing the difference between the two: keep write separated from read. This works well with event sourcing because it’s exactly what it’s trying to achieve. This means that we have a write model and a read model.
When it comes to web services, the write model is the request body, the data you send to an API for processing. The read model is the response body, the data the API sends back. And another read model exists between services in a system. That’s it. Most of the time all three models will contain the exact same data. Sometimes, we don’t send a field or two out in the response. I think this kind of distinction between data models is more important in a nominally typed language than it is in a structurally typed language because in nominally typed languages we would have to use the same family of objects for the three use cases which can result in sending out information that we don’t need to or we need to pull private protected gymnastics to use the same model which can result in implementations which are unnecessarily complex. In structurally typed languages the family of the object does not matter as long as the shape of the object is the same — the idea of families of objects does not really exist.
So what does this mean for event sourcing?
It means that the request body becomes the event data since it is the unit of change. And the response body is what we aggregate to. Occasionally we need to take the request body and add or remove some information before turning it into an event (this happens when we need to integrate with external services).
What if the request is really big?
The more important question is: does it make sense to split the event up. If the event needs to be split up then its a sign that the endpoint does too much and should be split up as well. Domain driven design provides good guidelines, each write operation should correspond to a something that happens in the domain. The classic example is:
OrderCreated.OrderPrepared is created.