You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Hi @kgrzybek,
I have doubts about some elements of CQRS + DDD.
It is mainly about the read part of the CQRS approach with DDD (without event sourcing). Queries in most of the samples I've seen are very simple and do not contain any business logic except simple filtering/aggregating data which is stored in database. The problem appears when queries depends on some "external" parameters (eg the current date and time) and additionally consumes data from multiple external sources.
To the point: Let's assume I have a domain that consists of reserving a service (ReservationRequest). The service is reserved by employees for themselves or for other people (ReservationRequester -> aggregate). The possibility of reserving the service and the variety of available options as part of the booked service are defined by the employer of a given employee (benefits), and the available services depend on a large number of reservation parameters, including the current date and time.
The problem is that the "current" and "eligible" benefits for a given employee are fetched from several external APIs
(unfortunately we were not allowed to keep the local data copy in our database in our microservice...)
There is no problem when creating a new reservation, we can use our own domain models within the domain and use providers interfaces to gather this data from external sources. Simplified command code would look like:
// for the sake of simplicity, the only parameter is the current date and time // await all skipped var checkDate = _dateProvider.UtcNow; var benefits = _benefitsProvider.GetActiveBenefitsAsync(checkDate); var billingStatuses = _billingStatusProvider.GetBillingStatusesAsync(checkDate); var eligibilities = _billingStatusProvider.GetEligibilitiesAsync(checkDate) // multiple similliar calls _reservationRequester.CreateNewReservationRequest(recipient, benefits, billingStatuses, eligibilities);
The aggregate has a business rules that checks if reservation is allowed (which uses the logic implemented inside rich domain models). It's cool here. The problem is what to do when we need a query, let's assume CanRequestReservation (checkDate, recipientId) with a very simple result (true or false). The query needs to be calculated "on the fly" which forces to put business logic to the query. Is it a good practice putting some business logic to the query? It is about using the same providers in the query which are used in the command. The logic that checks if reservation is allowed is encapsulated inside domain models. It would only be about retrieving domain models using providers and running business logic on the domain models inside the query (the logic depends on the current date and time, because the benefits have a FROM: TO duration).
Or maybe a better approach is to have a background job that uses these "providers" and precalculates some FROM:TO period combinations for different booking options with information if reservation is allowed in this specific period (job would be ran every 1 hours or so, it could be confirmed with business).
Then the query itself would just read the data from database.
I can see a problem related to architecture - if you need several external calls to make a Reservation, that is, you cannot make a reservation without external services. So your service is not autonomous. Since you wrote that you cannot have this data earlier (e.g. from events), it will be impossible to have this information for the time of the request. The background job is a solution, but here comes the performance and the question of how often to pool this data.
You may have "query" logic in the domain. An example of such something is the Policy, which tells you whether you can make a reservation or not. You create a ReservationPolicy which has one pure function method - you provide it with all the data and it returns you true / false. You use this policy in the entity as well as in the query.
reacted with thumbs up emoji reacted with thumbs down emoji reacted with laugh emoji reacted with hooray emoji reacted with confused emoji reacted with heart emoji reacted with rocket emoji reacted with eyes emoji
-
Hi @kgrzybek,
I have doubts about some elements of CQRS + DDD.
It is mainly about the read part of the CQRS approach with DDD (without event sourcing). Queries in most of the samples I've seen are very simple and do not contain any business logic except simple filtering/aggregating data which is stored in database. The problem appears when queries depends on some "external" parameters (eg the current date and time) and additionally consumes data from multiple external sources.
To the point: Let's assume I have a domain that consists of reserving a service (ReservationRequest). The service is reserved by employees for themselves or for other people (ReservationRequester -> aggregate). The possibility of reserving the service and the variety of available options as part of the booked service are defined by the employer of a given employee (benefits), and the available services depend on a large number of reservation parameters, including the current date and time.
The problem is that the "current" and "eligible" benefits for a given employee are fetched from several external APIs
(unfortunately we were not allowed to keep the local data copy in our database in our microservice...)
There is no problem when creating a new reservation, we can use our own domain models within the domain and use providers interfaces to gather this data from external sources. Simplified command code would look like:
// for the sake of simplicity, the only parameter is the current date and time // await all skipped var checkDate = _dateProvider.UtcNow; var benefits = _benefitsProvider.GetActiveBenefitsAsync(checkDate); var billingStatuses = _billingStatusProvider.GetBillingStatusesAsync(checkDate); var eligibilities = _billingStatusProvider.GetEligibilitiesAsync(checkDate) // multiple similliar calls _reservationRequester.CreateNewReservationRequest(recipient, benefits, billingStatuses, eligibilities);
The aggregate has a business rules that checks if reservation is allowed (which uses the logic implemented inside rich domain models). It's cool here. The problem is what to do when we need a query, let's assume CanRequestReservation (checkDate, recipientId) with a very simple result (true or false). The query needs to be calculated "on the fly" which forces to put business logic to the query. Is it a good practice putting some business logic to the query? It is about using the same providers in the query which are used in the command. The logic that checks if reservation is allowed is encapsulated inside domain models. It would only be about retrieving domain models using providers and running business logic on the domain models inside the query (the logic depends on the current date and time, because the benefits have a FROM: TO duration).
Or maybe a better approach is to have a background job that uses these "providers" and precalculates some FROM:TO period combinations for different booking options with information if reservation is allowed in this specific period (job would be ran every 1 hours or so, it could be confirmed with business).
Then the query itself would just read the data from database.
Beta Was this translation helpful? Give feedback.
All reactions