In the context of a Microservice architecture, a single business operation can require collaboration between two or more services.
Suppose we have an Order Management Service and a Product Catalog Service.
When the user adds an order item to an order, the Order Management Service will persist a OrderItem object which have the following attributes among many others :
OrderItem
+ Id
+ ProductId
+ ProductName
In order for the Order Management Service to fill the ProductName attribute, we have 4 choices as I see it :
Choice 1 : ProductName is given by the client app as it probably already has this data from previous requests
Choice 2 : If the architecture uses an Api Gateway, the gateway will be responsible for retrieving the ProductName from the Product Catalog Service then provide it to the Order Management Service.
Choice 3 : The Order Management Service will call directly the Product Catalog Service and asks him for the ProductName givent a product id.
Choice 4 : The Order Management Service has a duplicate (but not exhaustive) product informations in its own database and these datas will be updated each time an update event is received from the Product Catalog Service.
Among these 4 choices, the n°1 seems not ok to me as we can't trust the client to give us a correct and updated value of ProductName.
I would like to have your opinion about what you think the best choice is and why !
Thanks !
Riana
Choice 1 : ProductName is given by the client app as it probably already has this data from previous requests
Like you said, it is not the best idea because the client may have stale information. Maybe acceptable if the product information changes infrequently and/or you have a secondary verification at order processing.
Choice 2 : If the architecture uses an Api Gateway, the gateway will be responsible for retrieving the ProductName from the Product Catalog Service then provide it to the Order Management Service.
IMHO, this is not a good idea. By doing so your domain/business logic will leak into the API Gateway. The gateway now knows the relationship between Orders and Products. This API gateway configuration/scripting will need to be maintained and introduces additional coupling.
Choice 3 : The Order Management Service will call directly the Product Catalog Service and asks him for the ProductName givent a product id.
This is my preferred choice. Though I do not recommend "direct" synchronous calls. Perhaps a retrieval of the ProductName via a messaging mechanism (message queue, event bus). Chained synchronous calls will reduce the availability of your services. You can find more information at Microservice Messaging Pattern.
Choice 4 : The Order Management Service has a duplicate (but not exhaustive) product informations in its own database and these datas will be updated each time an update event is received from the Product Catalog Service.
Data duplication is generally frowned upon unless there is a really good reason for it. In this case I don't see any. Why bother splitting the databases into two for each of the services yet duplicate the data between them? Also, to have the data updated each time an update event is received indicates that some kind of event/messaging infrastructure is available, in that case, why not just use messaging?
This duplication may be justifiable for high volume, low latency look ups, but it is a slippery slope that may end up with duplicated data all over your services. Imagine the repercussions of a length or type/format change of the ProductName string...
Related
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?
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.
When looking at aggregate design in each bounded context (BC), you have to include only the data required to provide the functionality that belongs to that BC. The fact that the restaurant endpoint needs to return some extra data is not a good enough reason to add that data to the order aggregate in that BC.
You can resolve the need for more data in different ways:
The API client can call multiple endpoints to fetch all the data it needs
The API can implement Data Aggregation, by internally querying multiple BCs/microservices and combining them to produce a single more complete response object
Create Read models, which store data from multiple sources into a single "table" in a way that simplifies querying and returning this data. This approach is more complex, but it's very useful when you need to filter and sort by fields belonging to multiple BCs, which is not possible with the previous two approaches.
Another consideration to make is double-checking if your boundaries are correct. Do you really need a Client BC? What business logic does it implement? Maybe Orders are created directly into Restaurant and there is no Client order? Client order could just be a "façade" providing all Restaurant orders belonging to a single client Id?
As a final note, I completely agree with Levi Ramsey's answer that events are the right way to coordinate the different aggregates. They would also be used to create the read models I mentioned above.
Let’s assume I’m in the context of an admin panel for a webshop. I have a list of orders. Those orders are payed for and are ready to ship. The (admin) user would like to start making shipments based on the items ordered.
Imagine there are 2 microservices. One for orders and one for shipments. In order to create a shipment, i will send a request with a couple of items to be shipped and an order ID to the shipment service. The shipment service will then check whether the items are present in the order by querying the order service. Because i don’t want to create a shipment with items that are not present in the order.
I’d like to have immediate consistency because the shipment data will be send to a third-party application after creation. Thereby it also feels weird to allow shipments to be created if the data is not correct.
I’m also using GraphQL mutations. Which means i have to return the updated state to the user, which also makes eventual consistency a lot harder.
What is the recommended approach for these situations? Could this be a sign that these 2 microservices need to be merged? I can imagine this situation can occur multiple times.
I appreciate a straight forward answer.
Scenario 1: I need to display product information.
Product Service (PS) : Holds information about products
Pricing Service (XS) : Holds pricing information of each product
Aggregator service (AS) : Combines both product info and price.
Question: 'AS' make a call to 'PS'(With product ID) to get product info. Now, how does 'AS' fetch that specific product price, from the 'XS'?
The confusion here is, does PS Table have a DB column with ID to corresponding price in XS Table? Can we call it a foreign key? If so, does it introduce tight coupling between two services? Which is against principles of micro services.
Well you may call that field as a "foreign key" but as long as you don't enforce any constraint on it then it should be fine.
From my view it is just a reference. It doesn't bring any tight coupling it provides some binding. Which is totally fine as far as microservices architecture concerned. If you think about a little bit broader in terms of possible implementation you may see that for example you can use a totally different database like relational one for pricing service and maybe
a document database for product service. And in that case it would be more clear that what you called as "foreign key" is just the reference for the actual record.
Having said that I may still question your solution like
Why do you need a separate Aggregator service instead of directly using Product service?
And same way why do you want to have a separate Pricing Service
Would it be better if you keep the reference to product on pricing service than keeping the reference to price on product service?
But anyway foreign key part is just fine from my point of view.
I want to use the many to one and other DB Relationship in micro-service architecture. In monolithic architecture we can create the entity relationship easily as they belongs to same project but in micro-service architecture how we can achieve the same.
Example:
There is one userDeatil service and other is productDetail service.Now there is third service called orderDetail and an order will have userID and ProductIDs associated with it. So how can we manage the relationship between 'user and order' and 'order and product'.
I have searched over net but didn't able to get the fair idea.There is another thread having same query but not having the clear answer. Link
In my opinion your case is about how you specify your services especially how you define the bounded context of each service !!
According to the situation above-mentioned I don't see any reason why the product service should know anythings about orders (even if it s just the order-id) and backwards. One more problem I see in this case: your services will not work if one service is not available (E.g when the product service it not online, your order service will not be able to work, because he needs the product details from the product service...).
I Think you should rethink the bounded contexts of your microservices. You should keep in mind:
ensure a loose coupling of the microservices
a microservice has always to work even other Microservices are not available (Resilience / Reliability).
The DDD (domain-driven-design) paradigm with its tools provides her a great help to assist you, during the definition process of your services, you encourage these qualities.
So, the following is JUST an idea (it s not a recommendation and you should review whether it matters for your business case) :
It seems like the "order" process is your core business domain. So you have to focus on it.
The user service (i hope you mean here the customer and not a user in terms of authentication/authorization) has only the responsibility to manage the customers, and may be their adresses, bank-Accountings etc.. It should not know anything about your orders or products.
The same is valid for the product service. It owns only data about products. It has no relation either to the customer nor to the order-service.
The order service by itself deals only with orders and should own only data that belong to an order (like ship Adress and some information about the product-items ordered). I think the customer-Id is also important here to keep the relation between the order and the customer. This way you can e.g get all orders made by a certain customer-id....
We are planning to migrate our product to micro service architecture using spring boot.
I have few doubts/concerns for "each service should have separate database".
If I created separate database for each service, then how to handle the table dependency.
For example: I have 2 micro services.
1. Customer Service: it has "customer" table (fields: customerId, customerName, etc...)
2. Order Service: it has "order" table (fields: orderId, orderName, customerId, etc...)
Note: for simplicity I mentioned only 1 table in each service here.
When I tried to access the order details based on orderId, I may want to retrieve the details of customer also.
In this case, I have two options in my mind:
Using RestTemplate call the "customer service" GET of customerById API and fetch the required details and set it in the response of OrderDetails.
Create customer table also in Order Service and using database tool replicate the data from customer service to order service's customer table. i.e., any changes done to customer table in customer service database will reflect on order service's customer table.
So, I can easily fetch relevant data from order service itself.
Please suggest which one is preferable. If both doesn't make sense, please suggest best way to handle this.
Thanks.