1

We are planning a complete rewrite of a very complex project (10+ years, ~15 different application modules) and we would like to adhere to DDD and CQRS as much as possible but we are struggling to fit our needs.

The context

The app is a data-analysis tool that basically stores data and allows the user to configure custom analysis\reportings. The key point is that writing logic (validation, business rules, etc...) is as much important as reading logic.

Many features are based on the flow: "retrieve data (from db) -> perform different processing on that data -> display it". This is a super common pattern we have throughout the whole application. Processing logic must happen in memory and cannot be stored.

The key point here is that processing logic is not just DTOs conversions. As we handle historical data, it evaluates many different engineering and mathematical models. For the business, this "computing logic" is core, as it is the writing side (i.e making sure the data stored is consistent, valid, and doesn't break any rule).

We work with .NET (C#), but I don't think that matters too much.

The problem

What we are failing to understand is how (if at all) DDD can help us here. We all know (correct me if I'm wrong) DDD focuses on writes and not reads. Queries don't follow the same flow and are simply executed against a DB and should not encapsulate any business logic.

Our concerns are about the fact that queries have to perform a lot of "business logic". They aren't plain and simple "get those entities paged". Splitting the business logic (reads and writes) into two different approaches (DDD on one side and something else for the queries) seems very odd to us.

The Question

Is there any literature\patterns for scenarios where write logics is as important as read logic and contains the same amout of "logic"? Is there a known way to make this work with DDD without having to write logic in different classes?

1
  • "We all know (correct me if I'm wrong) DDD focuses on writes and not reads" - no, not really. "Splitting the business logic (reads and writes) into two different approaches [...] seems very odd to us." - sounds like CQRS 1, 2. Historically, this idea did emerge out of the DDD community; Greg Young said it could be viewed as a way to transition to Event Sourcing. Commented Oct 26, 2022 at 21:54

2 Answers 2

1

we would like to adhere to DDD and CQRS as much as possible but we are struggling to fit our needs

Re-read what you've written here. You struggle to apply some pattern, because you're not sure it'll fit? Maybe don't do it then :) Seriously.

I guess your question could be understood to mean, how do you "follow the domain" as closely as possible in this environment?

My main point would be to stop thinking about data, storage and queries and start thinking about things and the behavior these things need.

For the process you described, you could have:

verifiedEvents.FilterConcerning(someCompany).Display()

or

severityJudge.TrainWith(verifiedEvents).Display()

As you see, I'm naming things instead of just handling buckets of data, and then I'm adding functionality (behavior) to those things. I'm not concerned with what those things actually have inside, whether they are data in memory, whether they only hold a single id and go straight to the database to complete their function. Whatever it is, they'll do the right thing.

Depending on how you interpret DDD, it is about following the domain as closely as possible. It is not about data. CQRS is about data, so there's a clash there. You'll have to figure out what makes sense for you ultimately, but don't follow some pattern blindly, however awesome you think it is, it will not end well.

0

Many features are based on the flow: "retrieve data (from db) -> perform different processing on that data -> display it".

To avoid logic in queries, the ‘perform different processing’ part should be moved to the domain model.

The asynchronous Api pattern is often used for long-running processes, but has the side effect that it can transform a query into a command.

When the user makes a request, instead of immediately performing the flow as described, return an Id or Url where the results will be available later. Before returning the Id or Url, start an asynchronous task (with a queue for example). The handler for the asynchronous task fetches the data, runs it thru the domain model and stores the results somewhere so that it’s available with the Id or Url.

Meanwhile the client has been polling and now the results are available.

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.