Why I Still Love CQRS (and Messaging and Event Sourcing)
Let’s rewind several years. At the time I was struggling through a large project with ever increasing complexity in my “domain model” (if you could really call it that) along with an explosion of dependencies—from NHibernate (including Fluent NHibernate, NHibernate.LINQ), log4net, unit tests, and web services all over the place. It was crazy—crazy big, crazy hard to debug, and crazy hard to figure out what was happening through the rats nest of dependencies. And this wasn’t even legacy code—we were in the middle of the project. Crazy. We were fighting an uphill battle and in a very real danger of losing despite us being a bunch of really smart guys.
Then I discovered event sourcing and it was as if a light went on. I could literally see how adoption of event sourcing could shed a massive amount of incidental and technical complexity from my project. Remember that at this point in time there was no CQRS—it hadn’t been officially defined. Nor had the term “event sourcing” really been refined according to Greg Young’s definition, as compared with Martin Fowler’s. Eric Evans had not yet done his presentation on what he learned since the book. All I could find were a few small videos of Greg talking about events and how powerful they were. I started blogging like crazy—perhaps about 20 posts or so—on “DDDD” or “Distributed Domain-Driven Design” as Greg called it. The primary reason was I wanted to capture what I was learning, or at least my incomplete understanding of it.
Fast forward to today. I’ve now have a number of systems in production with several more that are only weeks away and I literally could not be happier. I have significantly more confidence in my software than I had in the past. The code is dramatically cleaner and infinitely more explicit than it would have been otherwise. But that’s only the starting point. Our ability to expand, adapt, and scale—to be agile from a business perspective—is infinitely greater than it ever has been, even with each application being significantly larger and each associated domain exponentially more complex than before—all with a smaller team. Why?
Among the most important factors are (not necessarily in order):
- Event sourcing
Because CQRS is now an overloaded term, we should take a moment to properly define it according to its official definition:
CQRS is simply the creation of two objects where there was previously only one. The separation occurs based upon whether the methods are a command or a query.
Although there has been a small disagreement of late between the co-authors of the CQRS pattern and its typical usage, I’m choosing to define it according to Greg’s terms because he was the first to really bring this concept out and talk about it.
For me, CQRS (according to its official definition) brings one very significant and critical benefit to the table—that of making it so that each object has a single responsibility. No longer do I have massive domain aggregates that have a mountain of nested properties, e.g. Users.Orders.OrderLines.Product.Prices.PriceHistory.Amount. That kind of complexity just screams at you such that you can’t get anything else done. By introducing CQRS all of that went away and all of a sudden the domain code only contained domain complexity instead of accidental and technical complexity.
Reads were far easier too. Now I could have a simple object (or read/view model) that was purely focused on the needs of the UI. I can’t tell you how many times prior to this pattern I would have to hack my domain object model to accommodate the needs of a particular screen—especially when business pranced in the door and said, we need to display this value here. Then I’d go modify the database, modify the domain object, it associated DTOs, all of the mapping code, HTML, etc. Yuck. Now all of my getters and setters on the domain are gone and my views are targeted to the needs of the screen in question. Beautiful. Simple.
Having experienced event sourcing and the need it fills, I’ll be hard pressed to give it up—not because it’s a panacea or a silver bullet—but because storage is cheap and data is valuable. As of about 3+ years ago, my specialty was temporal databases—storing a series of changes to data and *how* it changed across time in such a way that it could be queried and interpreted by the application. The schemas for those databases were so incredibly nasty that it made NHibernate look like “Hello, World!”—no joke. Although I could see when and how data changed, I still couldn’t see why. Event sourcing solves that problem.
With event sourcing I know exactly when, how, and most importantly why the data changed. Not only that, I can now build new and interesting reports from the data that we never knew we needed previously. For example, it would be trivial to find out how long it took for the average user to re-add a particular item to their shopping cart after it had been removed an a coupon code had been emailed. How could we have known to plan for that kind of report? But with event sourcing, it’s just a matter of writing a few message handlers, iterating through the event store, and pushing it into the handlers.
Not all of my code uses event sourcing—for example, trivial systems with little to know competitive value can be put together using whatever pattern we see fit. But for us, the systems that bring us competitive advantage always use event sourcing.
I do have to say that messaging has had by far the largest impact on the software I’ve written, more so than anything else. There are a number of significant reasons for this, but perhaps the most important one actually recaptures the essence of CQRS—that of having a single responsibility. By breaking the system apart into various pieces such that each can focus on a particular area of concern, we can more easily make better technology and implementation choices. For example, all of a sudden our website doesn’t have to be written in C#. Instead, we could leverage some open source CMS and expose simple JSON endpoints on the server that would receive RPC calls from the browser and then asynchronously dispatch messages for processing. Furthermore we are now able to evaluate storage engines based upon their technical merits and applicability to the problems at hand for a particular endpoint and business need.
The bottom line for messaging is about choice—we can now make well-informed choices at each messaging endpoint and select a piece of technology not on marketing and hype, but upon its ability to solve the problems at hand in a simple and elegant manner.
I have found each of these patterns to be extremely valuable. Each has vastly simplified my code. The primary reason that I can see for developers to dismiss these patterns for their “increased complexity” is familiarity. In many ways we have been burned so often by some new technology that is going to save the world only to have it fall short. We have invested mountains of time in learning a new technology only to find support pulled for the product or to have some other piece of technology strike our fancy. After far too many cycles of this, it’s no wonder that there are so many skeptics among us.
The establishment has long pushed us in this direction claiming that technology alone can solve problems—usually a technology that they sell. The establishment is wrong. These patterns work because they transcend technology. They make it so that technology and software serves us, not the reverse. In short, by understanding and appropriately leveraging these patterns, life is good. Very, very good.