You would always get a ton of literature on Event Sourcing and CQRS. The key question is WHEN do you use it? Under what circumstances? Is your problem really in need of ES?
I would not go into the details of what Event Sourcing and CQRS is. The industry stalwarts have covered that in adequate detail. This post delves into battle-tested scenarios on where we should have used and otherwise ignored ES.
Reasons to use Event Sourcing
There are always tons of reasons to use event sourcing. Not necessarily all of them hold water. Some of them include
- It is a natural extension of DDD – Well yes, it does find relevance along with the DDD patterns but then we could very well use all the concepts of DDD without event sourcing.
- Helps in separating out write and read models – Hmm, do that with plain CQRS instead.
- Allows building asynchronous and eventually consistent communication models – Well, you could do that with Actors and all the goodness of Akka like framework.
- It helps you answer WHY – This one I would take hands down. If you really need to know the WHY you got to the current state rather than just knowing WHAT the current state is then yes, you would need ES.
- Helps maintain an audit log – You could still maintain an audit log the old-fashioned way without ES but yes, it does make it easy because you have all the events that led to the current state. These audit logs can be used for analytics as well. There would possibly not be a debug problem and possibly no information would be lost.
- Performance – Because events are immutable, you can use an append-only operation when you save them. Events are also simple, standalone objects. Both these factors can lead to better performance and scalability for the system than approaches that use complex relational storage models.
- Deriving additional business value from the event history. By storing events, you have the ability to determine the state of the system at any previous point in time by querying the events associated with a domain object up to that point in time. This enables you to answer historical questions from the business about the system.
- Replayability – You might discover a coding error that results in the system calculating an incorrect value or you want to gather additional statistics from the events which came in 6 months back until now. Rather than performing a manual adjustment on a stored item of data, you can replay the event stream so that the system calculates the value correctly and populates those additional data points based on the new version of the code.
Reasons not to choose Event Sourcing
Now let us look at some of the reasons to avoid ES.
- Performance – Although event sourcing typically improves the performance of updates, you may need to consider the time it takes to load the domain object state by querying the event store for all of the events that relate to the state of an aggregate.
- Versioning – You may find it necessary to change the definition of a particular event type or aggregate at some point in the future. When the event changes or the command changes, so does your way of handling them. Then you would have to deal with multiple versions of the event. One which came in historically and one which is current.
- The development costs are high – Frameworks in this area tend to be heavyweight, overly prescriptive, and inflexible in terms of tech stacks. There is quite a bit of plumbing needed. You have to deal with classes for commands, command handlers, command validators, events, aggregates, AND THEN your projections, those model classes, their access classes, custom materialization code, and so on. The entire process needs quite a bit of scaffolding.
- The UI needs to work heavily with eventual consistency – This is something that bit us real bad in the real world. First, the UI needs to be aware that any command which is sent through would have its own eventual consistency cycle time and it should be prepared for that. It should not expect that the user registration would have actually synchronously registered the user once the command went through. So, the UI needs to play along. It is said that for such scenarios the system should follow the usual CRUD which brings us to then building 2 kinds of systems. One which would follow the usual CRUD and the other which would use ES.
- Projections are expensive as well – That first extra projection you add doubles the amount of code that touches your event stream. You would be writing more than one projection. So now you have N things processing this event stream instead of 1 thing. There’s no more DRY from this point forward.
- An audit log has too much noise – most of it ends up being pure noise that actually needs to be filtered out. Both by end-users and by consuming sub-systems. A lot of information captured by the audit log would not be used 99% of the time.
So how do you decide?
OK, I believe by the time you get to this part, I should have managed to confuse you already. The question that I ask myself and I ask our clients to answer to make a final call is the following
Are you interested in the WHY of the current state? Does the history of collective data records add any value to you?
They would not have a straight answer to this and hence a story works.
Suppose you’re operating an e-commerce system with a CRUD based order module. At some point, you may ask yourself the question. How often does it happen that customers put items in their shopping cart, but remove them again before checking out? In the CRUD system, you ordinarily wouldn’t have the information required to answer the question. Unless it was explicitly known beforehand that this information would be relevant and logging of the removal event has been included. By contrast, in an ES system, you can always answer such questions about the past, even if didn’t know you were going to have that question because all history is available.
Some of our clients say, hmm that is interesting and we might need it in the future. For them, I would skip the ES journey. When they are interested in such events (which 90% of the time they would not be) then we would start writing some of these events in a actor, synchronously to a queue.
Or as Chris put it in his blog
I think you can generally answer it with some alone time, deep introspection, and two questions:
For which core problem is event sourcing the solution?
Is what you actually want just a plain old queue?
If you can’t answer the first question concretely Don’t. Those are not problems exclusively solved by event sourcing. A good ol’ fashion history table gets you 80% of the value of a ledger with essentially none of the cost.
Similarly, CQRS doesn’t require event sourcing. You can have all the power of different projections without putting the ledger at the heart of your system.
The latter question is to understand do those downstream systems care about those intermediate states, or will it just be noise that needs to be filtered out? If the end goal is just decoupled processes that communicate via something, event sourcing is not required. Put a queue between those two bad boys and start enjoying the good life.
How the hell did we get here?
Well these are battle scars from several projects where we had a chance to make a decision on the architecture. In some we did use ES and in others we did not. We might have used them where did not need to and the other way round as well.
We get too consumed by the latest toys in the industry and we forget about why we became software developers and architects in the first place. Software architecture best practices, enterprise architecture patterns, and formalized ways to describe systems are all tools that are useful to know of and might come in handy one day. But when designing systems, start simple and stay as simple as you can. Try to avoid the complexity that more complex architecture and formal tools inherently introduce.
This brings me back to something that I had learned almost 15 years back
The best architects are the ones who can keep the architecture simple without the need for needless complexity unless it is needed.
Talk to us for building beautiful systems with the right architecture.