2

I read the discussion Should CQRS Command perform a Query?

But I don't understand in which database the query is performed, because I have two distinct databases - one for reads (queries) and one for writes (commands).

If I need to take the customer info, I need to read from the Write Database? Is this way correct? I think yes because if I read from the Read Database, I need to inject the reference to ReadModel and ReadDatabase and I loose the SEGREGATION, right?

1
  • Command and Query are separate concerns. Creating former you should not get around the latter. Basically Command is something that from my experience should be transactional. And within single command you must be sure that the data you're updating is up to date. Asking readonly database for entity that you're about to edit is not feasible, since it might have already been updated in your Write database by someone else. Think of the read database as of a source of presentation data. – yoger Sep 2 '20 at 10:23
4

If I need to take the customer info I need to read from the Write Database? It's a correct way?

Short answer: yes. But you're missing a few steps in your reasoning.

  1. Commands and queries are separated both for general cleanliness and the ability to independently scale the command or query sides.
  2. Independent scaling means that your commands (write) live on another physical machine than your queries (read).
  3. Different physical machines means the changes (made via commands) need to be synchronized from one (write) to the other (read)
  4. Such a synchronisation takes a non-zero amount of time, the read database is always behind on the write database (how much it is behind is a matter of specs).
  5. The main thing to take away here is that while the query database may be better suited to servicing large volumes of queries, the write database can actually be assumed to be more reliably correct since it is always the most up to date.

During a write action, you want the data that is most current. Therefore, you should get it from the write database, which is guaranteed to have the most current state of the data.

Just to take the train of thought to its final station:

  1. Separating your commands and queries entails accepting that there is lag between performing an action (write) and seeing its outcomes (read), because of the needed synchronization. That lag can be mitigated by upgrading the infrastructure and running more frequent replication, that is a cost-benefit analysis on its own.

Should CQRS Command perform a Query?

How you interpret "query" in that question very much changes the answer.

If by query you mean "getting data from a database", then the answer is "yes, you can perform a query (and you should run that query on the write database)".

But the "Query" is capitalized in that question, suggesting that it's referring to the CQRS Query (which inherently would connect to the read database), at which point the answer is "no, you should not run a Query (since it would fetch data from the read database)".

I think yes because if I read from Read Database I need to inject the reference to ReadModel and ReadDatabase and I lost the SEGREGATION, right?

Separate models or database contexts are not an inherent requirement of CQRS. It's perfectly possible to define a single context (think of a basic EF DbContext) and use it in both the command and query side of things. The only thing you'd really need to change is the connection string, not the shape of the model.

Note that this does mean you have no easy way to enforce only write or only read logic, but whether you need that check to keep your development in line is your call to make.

I'm not saying that you shouldn't separate your model. What you're doing is perfectly fine. What I'm trying to point out here that "needing to reference your read model" isn't a valid justification for your current question, as CQRS can exist without having a split model to begin with.

4
  • "The only thing you'd really need to change is the connection string, not the shape of the model." Nothing prevents you from doing that, but this would suggest that CQRS is an unnecessary overengineering in this domain. If you don't have event sourcing and you need to rehydrate your aggregate, what you are querying is the "write representation" of the model, that would be just used to execute commands, since clients need a bunch of aggregate representation that don't fit with previously mentioned one – Carmine Ingaldi Sep 2 '20 at 14:26
  • @CarmineIngaldi: It's not unnecessary overengineering if you still separate your read/write servers so they can be scaled independently. The point is more that the physical separation does not have to entail a code separation as well. Functionally speaking, a given context instance would only be used for either the write or the read intention (i.e. server runtime), but the same context class could be used for creating instances for both read and write scenarios. – Flater Sep 2 '20 at 14:35
  • Yes I see you point, but if the read/write imbalance is your ONLY concern, you can just scale the same non-CQRS application. It's equivalent from a cost perspective and you don't need to introduce eventual write/read consistency – Carmine Ingaldi Sep 9 '20 at 12:28
  • @CarmineIngaldi: Scaling up your writes is much more of an issue due to race conditions and concurrency faults. Reads are generally concurrency friendly. The ability to only scale up the reads is much safer (in terms of issues popping up) than scaling both reads and writes. And more often than not, it's the reads that need higher availability. Most data is read many more times than it is written. – Flater Sep 9 '20 at 13:30
0

The only reason a CQRS application should fetch some information (I don't call it a "Query", it would be misleading) in the middle of the command path, is that a use case needs to retrieve information from the outside world before to validate a command. The outisde world typically is another bounded context, so this means that, when you do that, you are calling a microservice, a 3rd party api, or even a method defined in a different module of you monolith

public void purchase(String orderId , String promocode) {
    boolean isValidPromocode = promocodeDomainService.check(promocode);

    commandGateway.send(new PurchaseOrderCommand(orderId , isValidPromocode));
}

This assumes that promocodes are managed from another service. After all, purchases are managed from sales/inventory/shimpent functions, while promocodes are a marketing concept, so it makes sense that are not part of the aggregate that performs the PurchaseOrderCommand

What happens behind the curtains is not our business. The domain service here is just an abstraction

Aside from this case, you aggregate must have everything it needs to validate its invariants

if (eventSourcing == false) In this case, the only statement that your (write) DB will process, would be that one which loads the aggregate. Before saving back your aggregate

public void purchase(String orderId , String promocode) {
    Order = repository.find(orderId);
    boolean isValidPromocode = promocodeDomainService.check(promocode);

    order.apply(new PurchaseOrderCommand(orderId , isValidPromocode));
    
    repository.save(order);
}

else The in-memory representation of the aggregate is updated as domain messages are published or retrived from the event store

In any case, on the query side, if you need some data that orginal aggregate doesn't own, you can still query external services or provide an aggregation layer in front of your projection

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.