Reputation: 85
I want to ask about an WEB Api architecture. My projects consists of few DDD, CQRS (no ES) microservices. Each service has Domain, Application, Infrastructure and UI layer. For this example I'll use BikeMicroservice. It has BikeController in the UI layer which is sending a command or query object to the MediatR. In the Application layer I defined Handlers (for every C or Q) which are using the same BikeService object to execute C/Q. Moreover BikeService has BikeRepository which is also performing both commands and queries with EF Core DbContext object.
Is it a proper CQRS appliance?
I'm concerned if service and repository objects should really perform both queries and commands. The only advantage I observed in my project are the thiner controllers (just sending the query or command).
What can I do to upgrade the microservices' architecture?
Should I divide DAL and Services into e.g BikeCommandRepository using EF Core and BikeQueryRepository using Dapper?
Upvotes: 6
Views: 5112
Reputation: 107
As you are already using entity framework I would not recommend adding the repository pattern as the Ef ORM already implement both Repository pattern and Unit of work. MediatR and CQRS should be enough to get you through.
In most case the use of repository pattern would be only when we are using SQL and ado.net directly( to hide the complexity of querying the database). This article here provides more info on how unnecessary it is to use ORM with an abstraction of Repository pattern
Upvotes: 1
Reputation: 3646
Admittedly I'm not a veteran software developer, but I thought a lot about this subject in recent months, and the following are my observations and conclusions.
You're quite right to think that CQRS is more involved than prefixing some MediatR-consumed classes as 'commands', and others as 'queries'.
Unfortunately, for some reason I don't understand, people started to equate MediatR with CQRS recently, even though Jimmy Bogard usually talks about MediatR in the context of vertical slice architecture.
The general benefits come from the fact that you're creating vertical slices instead of using a traditional, layered, service-heavy approach. This decouples the use cases from each other, decreasing the amount of bugs and complications that originate from problematic implicit dependencies between the various operations due to their shared services.
This is a basic pattern you can implement in simple applications without much friction, and arguably provides consistent benefits everywhere.
On the other hand, what CQRS entails is creating a parallel data access structure for queries and commands, plus optionally creating parallel models as well. This inherently adds to the complexity and overhead, and in general, simpler systems it provides...
No performance benefit, since you don't have to scale reads and writes independently.
No structural benefit, since you don't need e.g. two separate dev teams working independently on the read model and write model.
The only benefit that would apply is that it's easier to reason about the operations because they are individually encapsulated, a.k.a. command pattern. But you already got that benefit from using vertical slicing alone.
(I honestly believe that MediatR should have been called CommandR. It implements the command pattern, not the mediator pattern.)
So, what you should ask yourself is what sort of advantage can you derive currently in your specific use case by committing to that added complexity and overhead.
CQRS isn't a fundamental, universal concern in software architecture, like e.g. SOLID and (arguably) vertical slicing is.
If the advantage is not evident, the majority of experienced developers who looked at that codebase would see the CQRS aspect of it as a distraction from code quality, and a bloat.
When using vertical slicing and MediatR, it's easy to implement CQRS concerns just when they are actually needed. This further decreases the justification for adding that plus complexity in a prima facie manner.
So, what I would personally suggest is to:
Keep using the same repository for commands and queries, let go of the idea that you're doing 'CQRS', but as a semantic detail keep calling those IRequest
classes as 'command' and 'query'.
When you encounter a situation where you see that queries on a given entity would need to be scaled independently for the load you're facing, you'd want to use a separate db, or Dapper instead of EF Core, and/or a different model, THEN implement the parallel CQRS structures for that part of your system. And, as you grow your project, extend the CQRS structures strictly as needed.
Of course if you simply want to create a sample project to explore CQRS, then go ahead and implement whatever would be useful for your learning experience.
Upvotes: 5
Reputation: 180898
Martin Fowler describes several scenarios where CQRS might benefit you, and some where it will complicate things:
- CQRS fits well with event-based programming models. It's common to see CQRS system split into separate services communicating with Event Collaboration. This allows these services to easily take advantage of Event Sourcing.
- Having separate models raises questions about how hard to keep those models consistent, which raises the likelihood of using eventual consistency.
- For many domains, much of the logic is needed when you're updating, so it may make sense to use EagerReadDerivation to simplify your query-side models.
- If the write model generates events for all updates, you can structure read models as EventPosters, allowing them to be MemoryImages and thus avoiding a lot of database interactions.
- CQRS is suited to complex domains, the kind that also benefit from Domain-Driven Design.
These are some of the considerations you should evaluate when deciding whether or not to move to a CQRS architectural model.
Upvotes: 2