Read Model in the domain
Asked Answered
U

1

6

I'm struggling with my Read Model as it is kind of a mix between domain logic and read model. Imagine getting quotes for a hotel or airline. In my instance it's shipping. To get a quote you need to read existing rate tables and then calculate the rate. You might log this as quote, to eventually turn into an order, but the getting quotes part is essentially a read, with some logic (i.e. service to get current fuel rate) to factor into the rate. The aggregate would be a Quote.

So would you use the read model to get the contract/rate table, and map that to the domain? Keep in mind the read is going to be optimized, it's not just a simple GetByID... and preferably coming from the read store for performance.

Ussery answered 3/10, 2016 at 15:20 Comment(3)
If quotes are first class entities in your domain model -- and without extraordinary evidence I would expect them to be -- then producing a quote is a write.Momentous
Yes producing them is a write, but get the info needed to produce them is the question. This could go for anything that is a write, where your logic depends on other aggregates (i.e. user/accounts settings) to drive the domain logicUssery
How quotes are invalidated? What if you read a rate that changes soon after? If that doesn't matter then just declare a RateService interface in the domain and the implementation can then fetch data from anywhere, it becomes irrelevant.Sisyphean
M
6

So would you use the read model to get the contract/rate table, and map that to the domain? Keep in mind the read is going to be optimized, it's not just a simple GetByID... and preferably coming from the read store for performance.

By design, aggregates shouldn't need to be immediately consistent with any model state outside of the aggregate boundary -- the "next" state is a function of the current state, and the arguments passed in. In other words, the idealized aggregate doesn't depend on the read model at all, and doesn't care where the state of the arguments came from.

Which means if you are struggling with "how do I get the current Rate from that aggregate when I'm writing a Quote in this aggregate", then something is badly wrong.

But if you don't need immediate consistency (in most cases, you don't), then there are a number of possibilities.

The most straight forward is that the client gets the state that it needs from the read model, and then passes that state along to the write model. Loading the state into the command avoids "confusion", which is one of the reasons that REST has been so successful.

In some cases, you'll want the "recent" state from the other parts of the model to be more timely. In that case, you might prefer that the application query the model on behalf of the client prior to submitting the change to the aggregate. Perfectly reasonable.

In some cases, you'll want the aggregate itself to perform the query. The most common way to achieve this is via a domain service: you pass to the aggregate a query object, the aggregate invokes the query with whatever state is appropriate, gets an answer back, and then chooses for itself how to apply the result to its current work.

In all of these, the state that you are getting back from the model is recent, without necessarily being current. For instance, there are no guarantees that the other parts of the model aren't currently being changed in such a way that the query results would change.

Note that in all of these cases, the caller (in particular, the aggregate) is completely insulated from the details of the calculation provided by the domain service -- the domain service is the only piece that needs to know if the rate returned is calculated from the write model, calculated from the read model, or just pulled from a cache.

My question is should I access the read model from within the domain, and then map those to domain objects.

No. The write model should only interact with its own state, and its parameters. If you need to lookup data in the read model to process a command, then one of the parameters should be a domain service interface, where the implementation of the domain service performs the lookup and transforms the result into domain objects.

Momentous answered 4/10, 2016 at 4:21 Comment(2)
I see what you're saying, but it's not practical to pass the state along in a REST post. In this case I'm not worried about consistency with other state. My question is should I access the read model from within the domain, and then map those to domain objects. This is for performance, as typically you get an aggregate by id, which is slower that coming from an optimized read only store.Ussery
Another example would be operations and user settings. You wouldn't pass these along in the post, you need to get these from the database/cache (repo/read model) and then perform business logic based on what those are. I get that your aggregate should have a property on it, say a list of operations, but how that is loaded is the question.Ussery

© 2022 - 2024 — McMap. All rights reserved.