5

Premise

A web application that has a frontend / backend separation. The frontend can be the JS framework of the month, which talks to a REST API in the backend.

The backend part of the system accepts commands, which are delegated via application services to a Rich Domain Model created by applying Domain Driven Design principles.

CQRS is applied, so queries don't use the domain model.

Consider the following fabricated scenario

We're building a system to ship orders. We have two date controls: a shipment departure date and a preferred shipment arrival date. We also have a dropdown menu with carriers. The user selects a carrier and sets the preferred shipment arrival date. Based on various factors like weight, country, container size, etc. some complex calculation determines the departure date.

So changing the dropdown should update the departure date, but only in the UI. The actual state of the shipment hasn't changed yet. The user can still decide that it's not possible to have the shipment ready at the calculated departure date, so (s)he could change the carrier and see what the departure date would be with that carrier.

Finally there's a Save button, that sends a command to update the shipment with the selected carrier, departure date and arrival date. The domain will have to perform the calculation again, to validate the combination of carrier and dates.

The problem

When applying CQRS, a request is either a command or a query. Typically the models are clearly separated.

A command is supposed to do something; do calculations, update and persist the aggregate's state and publish events. It typically doesn't return anything.

A query is much simpler; it returns the current state of a specific part of the system. A query has no side-effects.

In the above shipment example, how should we handle the part that updates the UI? It doesn't feel right to treat is as a command, because it has no side effects and we need a response. It also doesn't feel right to treat is as a query because of the complex calculations. Furthermore the same business logic is required twice, so if we treat it as a query, our cleanly separated models suddenly have code duplication or our query model gets a dependency on our domain model.

Proposed solution

I'm leaning towards creating a domain service that can perform the calculation and then have a dependency on this service from the query model. The implication is that I have to change the domain model; instead of having the calculation nicely encapsulated within an entity, a separate class is required leading to a more anemic domain model.

I'm curious how other who faced similar challenges solved it.

3
  • 1
    seems like if update is done solely on UI it's neither a command nor a query because it's not a backend buisiness at all but the responsibility of the ui side Commented Dec 11, 2020 at 14:00
  • @BohdanStupak Thanks for your comment. It's an interesting thought and imo perfectly valid for simpler validations. But when complexity increases, duplicating business logic in the frontend becomes more and more awkward.
    – Rik D
    Commented Dec 11, 2020 at 14:52
  • @RikD So you'll be doing these calculations on the back-end and not just with UI logic? From your example, I got the impression it was being done locally within the UI but from your comment, it sounds like that will not be the case in your scenario. Just making sure I'm on the same page, as those are two different scenarios IMO.
    – jdewerth
    Commented Dec 12, 2020 at 2:26

1 Answer 1

3

It's common to have "duplicate" logic that is used for queries and handling commands.

The real difference between what happens in the command and what happens in the query is that the command is using authoritative copies of information as inputs, where the query model is using stale or speculative values.

Especially in the case where the answer depends on information that can be changing elsewhere, the calculation of the departure date in the UI is a guess. Changes out of the control of the UI (including changes to the way the authoritative date is calculated) can mean that the guess doesn't work out.

Welcome to distributed systems.

On the web, you'll normally choose one of two implementations - either you'll have the client download a copy of the algorithm and run it locally (but of course the authoritative algorithm could change after the download), or you'll have the client send data to the web server, and perform the calculation there (more latency, but higher assurance that you are using the latest algorithm).

You probably aren't going to find a single answer that satisfies all of your expectations (unless you are well familiar with distributed systems, and have already sufficiently lowered your expectations). What you are going to find are different combinations of trade offs, and the job will be to find the best compromise for your circumstances.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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