Orthodoxy of command validation

292 views
Skip to first unread message

Ryan Marsh

unread,
May 7, 2021, 5:12:27 PM5/7/21
to DDD/CQRS
Should command validation (within a command handler) access the read model in order to gather enough information to properly validate the command? Why or why not? What are the tradeoffs?

Scenario:
An airline. We are using CQRS/ES
Scheduling team has a service authoritative for flight schedule and publishes events as new flights are added.
Reservations team has a service authoritative for reservations.

Reservations team can of course maintain a local cache (read model) based on the events from the Scheduling team. This would make Booking more resilient. 

Reservations team has a command "Create Reservation" (on the Reservation aggregate). We obviously don't want to allow reservations to be created for non-existent flights. Should the command validation be able to query a read model to make sure the flight is  valid flight (on the schedule for that day)? I thought the Command + the Aggregate should contain enough information for the command handler to validate the Command. 

Please help me understand the orthodoxy around this and whether accessing read-models from command-handlers is wise or not.

Kind regards,
Ryan

Brandon Piner

unread,
May 8, 2021, 7:37:48 AM5/8/21
to ddd...@googlegroups.com
There are two options that I'm aware of. But the short answer is that it's fine to do. I create a domain service which is responsible for querying a read model to assist command handler domain rules. The implementation of the domain service (how it resolves the query) is injected via DI. 

So the one method is to directly query the other service by making HTTP or GRPC calls. 

The other method is to have your service subscribe to events emitted from the scheduling team. The scheduling team can emit the raw domain events or it can emit more public facing events. Within the service you can build a local readmodel or cache from those events. Then your domain service will query the local read model instead of a remote service. 

Each has its own pros and cons so I would say it's up to your organisation to choose. I'm sure there are other methods as well



--
You received this message because you are subscribed to the Google Groups "DDD/CQRS" group.
To unsubscribe from this group and stop receiving emails from it, send an email to dddcqrs+u...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/dddcqrs/e2e97c62-13b8-47ff-bcde-5280f27794e3n%40googlegroups.com.

Yordis Prieto

unread,
May 8, 2021, 12:56:39 PM5/8/21
to DDD/CQRS
Answer your question directly, call some read-model that answers the question that your flight exists from the Command Handler, use dependency injection for passing the service that responds to such question (In the best case, use Functions as dependencies instead of Objects).

CQRS is not about the Command side can't call the Query side, the segregation is so you don't end up with **one class/service/object** doing Commands and Queries. Instead, you have two classes/services/objects.

Aside from that, here is my thought about the topic in general so please understand I will twist your domain to fit what I am trying to explain.

> We obviously don't want to allow reservations to be created for non-existent flights.

Totally understandable but we need to agree about what is "non-existent flights".

The flight could get cancel right after I made the reservation, or something else could happen that would make it a "non-existent flight". So the flight exists at the moment I did. In other cases (this was my case a few weeks ago), people could change the request and send garbage flights ID or the Frontend clients did something wrong with the read-models.

So, what should I do?

1. Trying to mitigate the issue by checking that at least the flight at a given time exists (your case) at the cost of slowing down the reservation system and creating coupling.
2. Take garbage data and do the invariants checking where it matters at the moment it matters.

It may be the case that although somebody made a reservation with the wrong flight because the flight was canceled/removed/whatever, you can propose the customer a new for the exact same price but different time or maybe the exact same time but different flight, I don't know (react to it, think about the business opportunity).

Before you put dependencies for checking such invariant that was done by database foreign keys before (probably), maybe take "garbage" information, do the business invariant closer to where it really matters and react to it from the business.

Greg Young

unread,
May 8, 2021, 1:11:25 PM5/8/21
to ddd...@googlegroups.com
So wouldn't I end up with a read model of available flights?

Then when I try to book a flight ... it would go to a flight object.

That flight object would then say whether or not that flight was available (and in a consistent manner)

--
You received this message because you are subscribed to the Google Groups "DDD/CQRS" group.
To unsubscribe from this group and stop receiving emails from it, send an email to dddcqrs+u...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/dddcqrs/e2e97c62-13b8-47ff-bcde-5280f27794e3n%40googlegroups.com.


--
Studying for the Turing test

Sam Hatoum

unread,
May 11, 2021, 3:01:34 PM5/11/21
to ddd...@googlegroups.com
I would argue that receiving events from the remote service is a better option as it allows you to have policies to respond to flights schedule changes.



Raoul Duke

unread,
May 11, 2021, 3:06:47 PM5/11/21
to ddd...@googlegroups.com
> I would argue that receiving events from the remote service is a better option as it allows you to have policies to respond to flights schedule changes.

i guess there would be a need still for some kind of big read model
query when the local service restarts, perhaps?

Harrison Brown

unread,
May 11, 2021, 3:12:53 PM5/11/21
to ddd...@googlegroups.com
> i guess there would be a need still for some kind of big read model
> query when the local service restarts, perhaps?

Not if you’re using a persistent message broker (e.g., Kafka). If an
event-producing service restarts then it’ll just stop sending events
for a while, and if an event-consuming service restarts it’ll just
pick up where it left off.

Ben Kloosterman

unread,
Jun 2, 2021, 9:36:23 AM6/2/21
to ddd...@googlegroups.com
The #1 issue is eventual consistency of the read model .. .this can get really nasty with loops to wait for the data etc .,. For things that rarely change its probably fine ..  

Best to do this sort of validation BEFORE the command is even created eg the UI . 




Ben  

Ryan Marsh

unread,
Jun 9, 2021, 2:21:34 AM6/9/21
to DDD/CQRS
Thanks all for your input. This was very helpful. 

If anyone is interested I decided (for now) to treat Commands as we would any SQL mutation (i.e. not exposed directly to untrusted clients). This puts the onus of data the command handler cannot reasonably validate (such as "stringly-typed" references to other aggregates), on the caller (a domain layer of sorts). We will see how it turns out. The benefit I perceive is that the command handler will not be temporally bound to a read model or have to know about a different aggregate.
Reply all
Reply to author
Forward
0 new messages