The reality our our profession (mo matter whether you think it’s a craft or a trade or whatever) is that we are not always allowed to make the best and optimal decisions from technical perspective. There are situations when other factors are more important. These factors include things like:
- what skills are there on the team mean to build particular piece of software
- what assumptions and constraints are inherited from previous team who worked on that project
- what client thinks about the solution you propose (yes, unfortunately from time to time clients do influent your technical decisions)
So, lest get to the point. Say you want to pursue the newest and hottest trends in software architecture and want to implement a CQRS/Event Sourcing solution, but one (or more) of things I’ve just listed is preventing you from doing it. You are suck with your favorite ORM for this project (hope it’s not for life). What do you do? Do you give up and build a CRUD solution? I hope you’re not. I’d like to show you one of the alternatives which helped me solve one particular problem.
Let’s start by asking why do you wanted to use CQRS/Event Sourcing in the first place. Probably because of increased testability, cleaner domain model code and more focused classes. We can achieve pretty good results in this categories by building a CQRS solution with 2 ORM-based models on top of single database. Let me explain the details.
Roles and data flow
The following image depicts the fundamental roles and the data flow in proposed architecture.
User interface is commands to invoke behavior. Commands are processed (since it is a compromise-driven version, command processing is probably synchronous) in the domain model. Because we are using ORM as a persistence engine, I don’t see anything particularly wrong in using Unit of Work pattern to implement collaboration between aggregate roots. After command completes, the whole Unit of Work is persisted by the ORM.
When user interface demands for data, the ORM is used to hydrate (fill with data) a different set of classes, the read model. In pure CQRS scenario, retrieving a data for a view would be a
SELECT * operation. Here, since we have only one shared database, we have to create more advanced projections and joins. But that’s the stuff you would have to write anyway if you decided to create a CRUD solution.
The following diagram shows various roles present on the command side of the equation.
First, there is a command which is sent (as I said, probably synchronously) to the Command Executor object. This guy is responsible for finding a Command Handler for particular command. This is where you could happily use Service Locator and even @kkozmic won’t yell at you:) If he does however, please contact me. But finding a handler is not enough. Command Executor is also responsible for transaction semantics of command execution. It is his job to create a Unit of Work (depending on ORM you’ve chosen, it would be either
ISession or some
ObjectContext-derived class). Next, he executes the command by passing it to the Command Handler.
First thing that guy has to do is to find domain objects somehow before he’ll be able to trigger their behavior. He does it via Repositories.
This is way easier. We have some DTOs here that are optimized to transport data to the UI. There DTOs are directly mapped to the database tables. Because there is one shared set of tables for both command and query side, mapping of tables to DTOs is more complicated than in typical CQRS solution. In might involve joins for example.
Finders are for the query side what Repositories are for the command side. Finders, however, doesn’t allow to modify any data.
Quick case study
Here you can download source code of small sample application demonstrating this approach. All you have to do to run it is set up a database and adjust query string in app.config file.
This is a simple banking application. There are two kinds of accounts: internal bank accounts (these does not need to have a balance as they can always be debited or credited) and normal customer accounts (no overdrawn allowed).
The command side uses inheritance to model the nuances in account behavior, while the model on the query side contains one big DTO class for both types of account. The latter decision allows easy binding to the UI controls. It is a quite common pattern to flatten the inheritance structure and represent actual types using enumerated values.
While I am sure that full-blown CQRS it the approach for solving complex domain problems, it is often very hard to deploy, especially in case of legacy systems. The compromise solution I described may help you in those cases. Once people start seeing real benefits of having some of CQRS principles implemented, it could be easier to convince them to give Event Sourcing a try in the next iteration…