How to implement constraints that are external to a microservice? - microservices

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.

Related

Product price changed while creating order

What is the DDD way of handling the following scenario:
user enters Order Create screen and starts creatingnew Order with OrderItems
user chooses ProductX from products catalog and adds quantity
OrderItem for ProductX is created on Order and user goes on adding another product
in the meantime, before Order is saved, admin changes price for ProductX
Assuming Product and Order/OrderItem are separate aggregates, potentially even separate bounded contexts, how is this handled?
I can think of several options:
optimistic concurrency combined with db transactions, but then if we broaden the question to microservices where each microservice has its own db - what then?
joining everything into one giant AR but that doesn’t seem right.
introduce a business rule that no product prices are updated during the point of sales working hours but that is often not possible (time triggered discounts, e.g.)
What is the proper DDD/microservices way of solving this?
What is the proper DDD/microservices way of solving this?
The general answer is that you make time an explicit part of your pricing model. Price changes made to the product catalog have an effective date, which means that you can, by modeling time in the order, have complete agreement on what price the shopper saw at the time of the order.
This might introduce the concept of a QuotedPrice as something separate from the Catalog price, where the quote is a promise to hold a price for some amount of time.
To address this sort of problem in general, here are three important papers to review:
Memories, Guesses, and Apologies -- Pat Helland, 2007
Data on the Outside vs Data on the Inside -- Pat Helland, 2005
Race Conditions Don't Exist -- Udi Dahan, 2010
I think one way to solve this through is Events. As you said, Product and Order can are very least separate aggregates, I would keep them loosely coupled. Putting them into one single aggregate root would against Open/Close and Single Responsibility Principle.
If a Product changes it can raise a ProductChanged event and likewise of an Order.
Depending on whether these Domain-Objects are within the same service or different service you can create a Domain-Event or an Integration event. Read more about it here.
From the above link:
A domain event is, something that happened in the domain that you want other parts of the same domain (in-process) to be aware of. The notified parts usually react somehow to the events.
I think this fits perfectly to your scenario.

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.

Use DB Relationships in spring boot micro services

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....

Event Sourcing - Aggregate modeling

Two questions
1) How to model aggregate and reference between them
2) How to organise/store events so that they can be retrieved efficiently
Take this typical use case as example, we have Order and LineItem (they are an aggregate, Order is the aggregate root), and Product aggregate.
As LineItem needs to know which Product, so there are two options 1) LineItem has direct reference to Product aggregate (which seems not a best practice, as it violate the idea of aggregate being a consistence boundary because we can update Product aggregate directly from Order aggregate) 2) then LineItem only has ProductId.
It looks like 2nd option is the way to go...What do you think here?
However, another problem arises which is about building a Order read/view model. In this Order view model, it needs to know which Products are in Order (i.e. ProductId, Type, etc.). The typical use case is reporting, and CommandHandler also can use this Product object to perform logic such as whether there are too many particular products, etc. In order to do it, given the fact that those data are in two separate aggregate, then we need 1+ database roundtrips. As we are using events to build model, so the pseudo code looks like below
1) for a given order id (guid, order aggregate id), we load all the events for it; -- 1st database access
2) then build a Order aggregate, then we know which ProductId are referenced in Order;
3) for the list of ProductIds, we load all events for it; -- 2nd database access
If we build a really big graph of objects (a lot of different aggregates), then this may end up with a few more database access (each of which is slow)...What's your idea in here?
Thanks
Take this typical use case as example, we have Order and LineItem (they are an aggregate, Order is the aggregate root), and Product aggregate.
The Order aggregate makes sense the way you have described it. "Product aggregate" is more suspicious; do you ask the model if the product is allowed to change, or are you telling the model that the product has changed?
If Product can change without first consulting with the order, then the LineItem must not include the product. A reference to the product (aka the ProductId) is ok.
If we build a really big graph of objects (a lot of different aggregates), then this may end up with a few more database access (each of which is slow)...What's your idea in here?
For reads, reports, and the like -- where you aren't going to be adding new events to the history -- one possible answer is to do the slow work in advance. An asynchronous process listens for writes in the event store, and then publishes those events to a bus. Subscribers build new versions of the reports when new events are observed, and cache the results. (search keyword: cqrs)
When a client asks for a report, you give them one out of the cache. All the work is done, so it's very quick.
For command handlers, the answer is more complicated. Business rules are supposed to be in the domain model, so having the command handler try to validate the command (as opposed to the domain model) is a bit broken.
The command handler can load the products to see what the state might look like, and pass that information to the aggregate with the command data, but it's not clear that's a good idea -- if the client is going to send a command to be run, and you need to flesh out the Order command with Product data, why not instead have the command add the product data to the command directly, and skip the middle man.
CommandHandler also can use this Product object to perform logic such as whether there are too many particular products, etc.
This example is a bit vague, but taking a guess: you are thinking about cases where you prevent an order from being placed if the available inventory is insufficient to fulfill the order.
For real world inventory - a physical book in a warehouse - that's probably the wrong approach to take. First, the model itself is wrong; if you want to know how much product is in the warehouse, you should be querying the warehouse, not the product. Second, a physical warehouse is not constrained by your model -- calling the addProduct method on a warehouse aggregate doesn't cause the product to magically appear there.
Third, it probably doesn't match very well with what your domain experts want anyway. If the model says that the warehouse doesn't have enough product, do you think the stake holders want the system to
tell the shopper to buy the product somewhere else, or...
accept the order, and contact the supplier for a new delivery.
Hint: when in doubt, carefully review how amazon.com does it.

Which objects are responsible for maintaining references between aggregates?

Suppose I have one aggregate, Ticket. A Ticket will have one assigned Department and one or more assigned Employee.
When instantiating a Ticket, should a TicketFactory be responsible for ensuring that a Ticket is created with a valid/existent Department and Employee?
Likewise, when decommissioning a Department or Employee, what is responsible for ensuring that a new Department or Employee is assigned to a Ticket so as to maintain its invariants? Could there be a service in the domain responsible for decommissioning, or is this a case where eventual consistency or some form of event listening should be adopted?
The TicketFactory would be declare that in order to create a Ticket you need references to both a Department and an Employee. It would not verify that those actually exist. It would be the responsibility of the calling code to obtain the appropriate references.
If using eventual consistency, the decommissioning of a Department and Employee would publish events indicating the decommission. There would be a handler associated with a Ticket which would subscribe to that event and either assign a new department and employee or send some sort of warning to task.
Take a look at Effective Aggregate Design for more on this.
I've recently started exploring DDD, so I have ran into some of the issues you mention.
I think that TicketFactory should always return validated/properly built Ticket instances. If you model is complex, you can have a domain service that validates that a given Department or Employee can be attached to it and then the factory uses it. Otherwise, you can just put it all in the factory. But what comes out of the factory should be a proper ticket.
I'd say that if e.g. only Ticket knows about the other two, a domain service that uses the Department and Employee repos would get the job done. If the relationship is bidirectional, then you can utilize event sourcing. Also, if it's really a event that should be captured in your domain model, and has other consequences other than reshuffling tickets, you can attach one of the handlers to this event to be InvalidTicketHandler. But if it's a small scale thing, keep it simple, just have a domain service that maintains the invariants.
Sidenote: If the Department and/or Employee are aggregates themselves, then you can reference them within Ticket via their identifier (e.g. employee's company ID or ID-code of the department). In that way you'll achieve consistency easier as you will not cross consistency boundaries between different aggregates.
A FACTORY is responsible for ensuring that all invariants are met for the object or AGGREGATE it creates; yet you should always think twice before removing the rules applying to an object outside that object. The FACTORY can delegate invariant checking to the product, and this is often best. [Domain-Driven Design: Tackling Complexity at the Heart of Software]
A depends on question type, but from the look of it it seems like a great candidate for an application layer functionality, i wouldn't go for the event solution though cause i find it only suitable in between layers and not between objects in the same layer.

Resources