2

I've been thinking about a problem for a while and I don't really feel that I've nailed how to approach this.

I'm trying to work with DDD as much as possible but some presentation scenarios do not really fit with this in terms of performance.

Let's say that we have a system for rental cars, and there we have a view that should display all cars and the number of times they have been rented out.

In a DDD-world I guess I would have a repository for my rental cars and some repository for "rent outs". I also guess that there might be a service that handles the interaction between them, creating agreements etc.

But when it comes to presenting the view with rental cars and the number of rent outs, I'm not really sure where to put this. Since the requirements are very specific to this view, I guess that the most performant version would be to have some kind of "RentalCarsOverview"-object returned by the repository. But this would "pollute" the repositories with queries that are very view-specific.

So I'm wondering if anyone have faced the same issues and how it was solved? I've been looking at CQRS to separate this, using queries for these kind of views and my domain model for updates. Is this a good approach?

Also, would it be "ok" to just have raw SQL in these query-handlers or would one also abstract these with a underlying repository?

3

These days, the usual answer is that your query code paths will bypass the "domain model", and work directly with copies of your information.

The argument here being that a query doesn't change the information stored in the domain model, and therefore we don't benefit from a bunch of ceremony that puts that information into domain objects just to take them out again.

For example, if all of your information is stored in the same relational database, you are much likely to query the database, and the transform the information in the result sets to the correct shape, and send that back to the client.

When the information you need to answer the query is distributed across multiple systems, then there is more bespoke work to generate the report. When the time required to generate the report exceeds your latency targets, then you start looking into designs where you can cache the expensive parts of the report. In some cases, this will mean that the entire report is cached.

2

CQRS and DDD are a perfect match.

Commands go via your repositories and DDD models. Queries can be raw SQL or a lightweight ORM, with separate models specifically for each view.

Architecting your system this way gives you a solid way to handle complex business rules, with the best possible performance for reads.

2

Let's start pointing that having smart queries is a DDD code smell. Most of times, all you have to do is to

  • retrieve an aggregate by its ID (talking RESTful: GET /users/xyz)
  • retrieve all aggregates with a given root ( /users?page=0&limit=10)
  • retrieve a subset of aggregates with some simple filter (talking RESTful: GET /users?username_startsWith=johnd&page=0&limit=10 )

Complex applications need to do complex things, clear, so you may end up needing to build projections out of your aggregate. Obviously, your aggregate shouldn't have any clue of what a projection is, but there you can find all needed data to build them, because you are designing your domain not only to be consistently mutated, but also to be obeservated

This is a reasonable approach even without introducing CQRS concepts. Most of our applications are CRUD applications + some domain validation rules. If you meet words like "overview" "report" "statistics" "summary" "history" just few times in you requirements/stories/code, you know that you can manage them just loading aggregates in memory through specific queries, calling some aggregation method (counts, sums, averages) e do the mapping against some projection DTO

When the aforementioned cases are prominent in your project, when loading aggregates to retrieve just some aggregate info is a huge burden for your system, so yes, you are meeting one of the two use cases in which CQRS is helpful

You'll provide your aggregate (namely, the write-side-aggregate) the ability to trigger domain events everytime a remarkable state mutation has been committed

You will build a different module where you'll have projection specific repositories and event handlers (in your case something like onEvent(CarRentedOutEvt evt) ) to update and save your projections.

You will have (again restfully speaking) a GET /rents/overview admin api that will behave very dumbly.All the complexity is offloaded at updating-time

  • "Most of our applications are CRUD applications..." - This is a fallacy. Just a paragraph earlier you write that your architecture prefers that everything is CRUD. It seems to me that developers prefer a technical architecture and feel uneasy and "unclean" when actual business logic wants to enter their code. My point is an architecture should be the exact opposite. Business logic at the center, and CRUD and everything else hidden in private classes and methods. – Robert Bräutigam Aug 20 '20 at 13:40
  • Being CRUD doesn't necessarly mean lack of business logic. CRUD simply means that application exposes a language that is based on resources and a closed set of action to be applied on them. These resources are "submitted" and "retrieved" from application, but in the most cases, the representation used in both phases look very similar. When you're app doesn't meet this descrption, well, separating representations worths and CQRS explains us how to do it. Nothing "technical", just patterns to decide how enclose the core logic, as you point – Carmine Ingaldi Aug 20 '20 at 17:18
0

You are allowed to have more than one read model and, in this case, a denormalised "Reporting" read model that just subscribes to the events needed to update itself when it receives a "Rented Out" event.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.