CQRS – not a complicated thing
CQRS has some reputation issues. Mainly, people will feel that it’s too complicated to apply in their current projects. It will often be considered over-engineering. I think CQRS is simply misunderstood, which is the reason many people will not choose it as a design technique. One of the common misconceptions is that CQRS always goes together with event sourcing, which is indeed more costly and risky to implement.
CQRS alone simply means that you’re making a distinction between a model that is used for changing state, and a model that is used for querying state. In fact, there’s often one model that accepts “write” operations (called “write model” or “command model”) and multiple models that can be used to “read” information from (called “read models”, or “query models”).
Most projects out there don’t use CQRS, since they combine the write and read operations in one model. Whether you use the data mapper or active record pattern, you’ll often have one object (“entity”) for each domain concept. This object can be created, it has methods that allow for state modifications, and it has methods that will give you information about the object’s state.
All the legacy projects I’ve encountered so far use this style of storing and retrieving state. It comes with a certain programming style that is not quite beneficial for the maintainability of the application. When writing a new feature for such an application you will start with getting all the ingredients in place. You need some information, and you need some dependencies. If you’re unlucky, you still fetch your dependencies from some global static place like good old
sfContext. Equally bad, you’ll fetch your information from the central database. This database contains tables with dozens of columns. It’s the single source of truth. “Where is this piece of information? Ah, in table
XYZ“. So now you use the ORM to fetch a record and automatically turn it into a useful entity for you.
Except… the entity you get isn’t useful at all, since it doesn’t give you the information you need — it doesn’t answer your specific question. It gives you either not enough or way too much information. Sometimes your entity comes with a nifty feature to load more entities (e.g.
XYZ->getABCs()), which may help you collect some more information. But that will issue another database query, and again, will load not enough or way more than you need. And so on, and so on.
Reduce the level of coupling
You should realize that by fetching all this information, you’re introducing coupling issues to your code. By loading all these classes, by using all these methods, by relying on all these fields, you’re increasing the contact surface of your code with the rest of the application. It’s really the same issue as with fetching dependencies, instead of having them injected. You’re reaching out, and you start relying on parts of the application you shouldn’t even be worrying about. These coupling issues will fly back to you in a couple of years, when you want to replace or upgrade dependencies, and have to make changes everywhere. Start out with dependency injection and this will be much easier for you.
The same goes for your model. If multiple parts of the application start relying on these entities, it will be more and more difficult to change them, to let the model evolve. This is reminiscent of the “Stable dependencies principle” (one of the “Package design principles”): if a lot of packages depend on a package, this package becomes hard to change, because a change will break all those dependents. If multiple clients depend on a number of entities, these will be very hard to change too, because you will break all the clients.
Being hard to change is not good for your model. The model should be adaptable in the first place, since it represents a core concept of the domain you’re working in. And this domain is by nature something that changes, and evolves. You want to be able to improve the quality and usefulness of your domain model whenever necessary. Hence, it’s a good idea not to let all those clients depend on one and the same model.
The solution is to introduce multiple models, for each of those clients. Exactly as CQRS proposes; one write model, which you can use to change the state of your domain objects. Then multiple read models, one for each client. That way, there will be less coupling, and it will be easy to evolve the model in any direction. You can safely add, remove, or transform any piece of information in any one
Truncated by Planet PHP, read more at the original (another 4746 bytes)