Coupling in microservices architecture - microservices

When working on an application in microservices architecture I stumbled upon issues concerning coupling between services.
This a typical application for ordering products. It seams reasonable to have a service that will operate as a product catalog. Our JavaScript client can ask this service for available products and show them in browser. When user clicks on a product we have to create an order. We would like to manage orders in another service. So an order is created - it means that user X ordered product Y. On the technical level we are simply persisting user id and product id in a database.
To sum up we have:
Products service
Product class:
Product ID, Product Name
Orders service
Order class:
Order ID, Product ID, User ID, Order date
Now let's imagine following scenario - in JavaScript client we would like to list all products that user have ordered. Orders service provides a list of products ids for a given user. But user cares about product name, not the id. Unfortunately Orders service doesn't know anything about products names.
We could tackle this in couple of ways:
Simply assume that the client is responsible for getting the information it needs. So after it calls Orders service and gets a list of products ids, it performs another call to Products service, passing products ids and getting corresponding products names in response. Then the client assembles both responses into something useful. This approach keeps our services clean, without leaking of domain knowledge from one service to another. But it requires more work on the client side.
Orders service when asked for ordered products makes a call on the backend to the Products service. It retrieves product names, assembles a response that contains orderDate and productName and sends that to client. All that's left for client to do is to present the data. Downside of this approach is that Orders service now gains more knowledge about products than neccessary.
Duplicate information about product name in Orders service. When an order is created, we pass not only product id but also product name. That means that Order class will look like this:
Order class:
Order ID, Product ID, Product name, User ID, Order date
Now we can easly provide full information about order without additional call to Products service. Also this time Orders service has too much knowledge about products. What's beneficial tough is that Orders service can provide relevant data even if Products service is down.
Could any of these approaches be considered best practice? Or are there different solutions?

In the eShopOnContainers example( https://github.com/dotnet-architecture/eShopOnContainers ), they add the following fields at the time an order item is created:
public void AddOrderItem(int productId, string productName, decimal unitPrice, decimal discount, string pictureUrl, int units = 1)
This duplicates the catalog information, however as this is a point in time snapshot of the record. This is far safer than linking to the original data.
At this point in the user journey you are in the domain of an order, if a user is viewing an order you can provide a link to the catalogue. However, the order service should be able to operate independently. Linking back to the catalogue for the information on products prevents the Order service owning it's own data.
A nightmare scenario being that a price changes in the catalogue... What would happen to the historic orders?

Related

How do i satisfy business requirements across microservices with immediate consistenc?

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.

DDD how to Communicate for search service

I work on eCommerce app. I use micro-services with ddd. I have product service. It store title description etc. Pricing service store product prices and promotions. Category service store categories and product category. Shipment service store all about shipment data and product shipment details dweight cargo firm isFreeCargo etc.
I dont Want to use composition pattern for searching. I create search service which store all datas of services. Pricing, product information, shipment information, category information etc.
I concat all data with events. Like cQRS
So all services communicate each other with events.
my problem is;
when CreateProductRequest come To product service . It includes categoryId, price, shipmentFirm etc. I mean not only product service information.
I only save title description etc in product service. There is no data like Price or category in product service. because They dont belong this domain. When product created and event raise productCreatedEvent that includes created product id and other service informations come with request.price , category etc.
Other services listen this event. And consume. Pricing save productId and price. Cagegory save productId and categoryId ...
And search service consume event,too. But there is no data about price or category because they just published.
Okey I can prove consistency with events when price change or category After saving. but at first maybe there is not going to be price or category for 2-3 sec.
how canI save category and price for search service immediately. because there is no price and category denormalize and caculated data in productCreatedEvent .There is just reference Ids like categoryId or not caculated price.They are going to be exist on search service after own services save and publish events like ProductCategoryUpdated or ProductPriceCaculeted
.
First of all. What I can see is that price and category services are very related to Product and they dont have strong functionality on its own so I suggest you to merge the three of them in one service. If the price service is used to create promotions, I would make a promotion service wich communicates with product service asking for the price of a product or a category of products, and apply them the corresponding discount.
The first problem has to do with what I have just said, because price, category and product are so related you have to pass all the data when you create a product, but because they are different services, product service should not be aware of this information. In case you want to do this way, you should create prices and categories independently of the product and when these are created each service should send a priceCreated or categoryCreated event that will be consume by anyone interested in. For example the search service.
For the second question, the best approach I think is the first one, first because It wont couple services. The cart service only have the ids no other information. And when you want to query another information you only have to make the query to the proper service as usual, so no coupling is created.
And second, the main drawback of the second option is that you are including logic related to cart service in all the other service. For each one you have to listen events and instead of make logic related to the service like update some info, they are sending info to another service. It is like a hidden command message

How to implement constraints that are external to a microservice?

Suppose we have two microservices, Customers and Orders, with no dependencies between them, i.e. they don't call each other, they don't know each other. Every order, though, has a reference to a customer by means of a customer id. In other words one customer may have zero or more orders, and one order belongs to exactly one customer.
For the sake of the example, it's totally fine to delete a customer unless there are orders belonging to that customer. What is the most appropriate way to implement a constraint on Customers that prevents a customer from being deleted if one or more orders have a reference to that customer? Analogous to referential integrity in a relational database.
These are the approaches I can think of:
Let Customers ask Orders if a given customer has any orders, e.g. via API call.
Let Customers keep track of which orders are assigned to every customer, e.g. by having each customer record maintain a list of order ids.
Merge Customers and Orders into a single microservice and manage it internally.
I'm not sure which approach is the best for the given example in a microservices context. I can see pros and cons in all three approaches but I won't list them here because I'd like to hear other people's thoughts on the problem, including approaches not listed above. Thanks.
Probably the second approach would help if you're going to decouple through events, either tracking a list of ids or a counter just telling how many orders are stored for such a Customer.
On the Order microservice you will emit an event when there is a creation/deletion that will be captured by the Customer (or any other microservice interested) who will take care of updating the list of order ids (or increment/reduce the counter).
If customer order counter is 0 then you may delete the customer.
Let's start with your third approach: This will not work in a Microservice world, because you will always have those constraints between some Services. And if you want to solve all of them this way, you'll end up with a Monolith - and that's the end of your Microservice story.
The first and second approach have both the same "problem": These are async operations, that may return false positive (or false negative) results: It's possible to make api requests for delete customer and create order (or delete order) at the same time.
Though this can happen:
For your first approach: Customer Service asks Order Service if there are any Orders for this Customer. Order Service returns 0. And at the same time Order Service creates a new Order for that Customer in another thread. So you end up with a deleted Customer and still created an Order.
The same applies for your second approach: The messaging between those services is async. Though it's possible that Customer Service knows of 0 Orders, and permits the delete. But at the same time the Order Service creates a new Order for this Customer. And the OrderCreated message will hit the Customer Service after the Customer has already been deleted.
If you try to do it the other way around, you'll end up with the same situation: Your Order Service could listen to CustomerDeleted messages, and then disallow creating new Orders for this Customer. But again: This message can arrive while there are still Orders in the database for this Customer.
Of course this is very unlikely to happen, but it still is possible and you cannot prevent it in an async Microservice world without transactions (which of course you want to avoid).
You should better ask yourself: How should the system handle Orders where the corresponding Customer has been deleted?
The answer to this question is most likely dependent on your business rules. For example: If the Order Service receives a CustomerDeleted message, it may be okay to simply delete all Orders from this Customer. Or maybe the behavior depends on the Order's state property: It's okay to delete all Orders with state = draft, but every other Order from this Customer should still be processed and shipped as usual.

Why are transactions missing data?

I am retrieving transactions via the Connect API v2, however, none of the returned records include order_id, customer_id, or cardholder_name.
All of this data is linked and viewable within the dashboard.
Am I missing something?
order_id - This field would only be filled if you used the CreateOrder endpoint and passed that order to the Charge endpoint for an itemized e-commerce transaction. It would not be there if you created an order in the Point of Sale.
customer_id - This is only filled in if the merchant (or app/site) explicitly attached a customer to the transaction. Some transactions get associated to automatically created customers that you don't have the data for, if you don't explicitly create the customer.
cardholder_name - You would only see this if the customer paid with Card on File. If you look at the data model in the documentation The cardholder name. This value is present only if this object represents a customer's card on file.

Creating a Relationship Between Order Product (salesorderdetail) and Service Activity (serviceappointment)

We are using Microsoft CRM 4.0 to run a consulting business. Its working pretty well but we want to simplify the way we are doing some things. What we want to do is create an Order (salesorder) with multiple Order Products (salesorderdetal). So good so far.
Next I want to be able associate each Order Product (salesorderdetail) with a Service Activity (serviceappointment), this representing that this billable line item in the order is actually going to be fulfilled as a consuting engagement.
The problem is, I can't seem to be able to create an association between the Order Product (salesorderdetail) and Service Activiy (serviceappointment). It simply doesn't appear in the drop downlist.
Can anyone think of a reason for this? I've seen some posts about relating field mapping between Quote Product, Order Product, Opportunity Product and Invoice Product, but that isn't quite what I am after.
Any suggestions gratefully received - even if it is an explaination of why its not possible.
I created a simple 1:N mapping from Case to Invoice. The Case records its ID and Title in custom fields in the Invoice. Unfortunately this does not allow for product creation as children of the Invoice, so that should be created as a custom code workflow.

Resources