Search code examples
microservicesdomain-driven-design

DDD deal with distributed status accross domains


Let's say we have a simple food delivery app. Where client order the food, then restaurant start preparing the food and gives it to the courier who delivery it to the client.

So here we have three different domains and each of this domain have their own order:

  • client - here client order the food and have the status of the food in preparation | in delivery | delivered

  • restaurant - here restaurant got its order and has their own status in queue | in preparation | ready to pick up

  • courier - courier has only two status delivering | delivered

Moreover each of this domain has their own price and other attribute about order:

  • client - total price (food price + delivery cost + fee)

  • restaurant - price of food, time of production to give a hind to the client when food will be delivery

  • courier - cost of delivery

All I want to highlight is that each of the domain has its own order aggregate, so according to DDD we have to keep it in different aggregates even in different microservices:

  • client - /orders/:id provides the general status of the order and total price to the client.
  • restaurant - /restaurants/:restaurantId/orders/:id provides the status of the food in restaurant domain and cost.
  • courier - /couriers/:courierId/orders/:id provides information how much courier earn from this order and how long it took to delivier

But now I met another problem, because client order combines information from other domains (is food still in restaurant or it's being delivery) so I have to gather this information when client asks about its order, but it means that client doesn't have its domain (its own aggregate, total price, discount etc), but if I create order aggregate for the client then I will not keep all information about order in one place (when restaurant give the food to the courier it should also change status of the order in client domain) what is not really according to microservices, because we keep information about the same order in different microservices.

Should I just create one order domain or should I split it to different domain and make these domains communicate between, when something will change in one domain?


Solution

  • One useful approach is to leverage domain events. When the restaurant's view of the state of the order changes, an event describing that change is published. The other services can then update their model of the event (assuming that that change is relevant to that service).

    So for instance, we might have:

    • user creates order via the client service => OrderCreated event emitted
    • restaurant service consumes OrderCreated event, translates the order for the restaurant (e.g. uses the prices which the delivery app pays the restaurant vs. the prices the delivery app charges the user) => OrderSentToRestaurant event emitted
    • courier service consumes OrderCreated and begins trying to figure out which courier will be assigned the order and the approximate transport time from pickup to delivery => DeliveryLatencyEstimateMade event emitted
    • client service consumes OrderSentToRestaurant and updates its order status (for presentation to the user) to in preparation
    • courier service ignores OrderSentToRestaurant
    • restaurant service ignores DeliveryLatencyEstimateMade event
    • client service consumes DeliveryEstimateLatencyEstimateMade and updates its model (delivery time remains unknown)
    • restaurant informs restaurant service of expected completion time => OrderReadyForPickupAt event emitted
    • courier service consumes OrderReadyForPickup, refines courier assignment decisions
    • client service consumes OrderReadyForPickupAt event, combines with the latest latency estimate to present a predicted delivery time to the user

    and so forth. Each service is autonomous and in control of its data representation and free to ignore or interpret the events as it sees fit. Note that this implies eventual consistency (the restaurant service will know about when the order is expected to be ready for pickup before the courier or client services know about that), though microservice autonomy already effectively ruled out strong consistency.