Microservices: how to handle foreign key relationships

DatabaseMicroservices

Database Problem Overview


Microservices architecture suggest that each service should handle it's own data. Hence any service (Service A) dependent on data owned by other service (service B) should access such data not by making direct DB calls but through the api provided by the second service (service B).

So what does microservices best practices suggest on checking foreign key constrains.

Example: I am developing a delivery feature (microservice 1) for products and certain products are deliverable to only certain locations as mentioned in the products table accessible to only products micro service (mircoservice 2).

How do I make sure that microservice 1 (i.e delivery feature) does not take an order to a unserviced location. I have this question because delivery feature can not directly access products database, so there is no constraints applicable at DB level when a delivery order is place in to delivery data base (no check is possible to see if a foreign key match exists in products database or table).

Database Solutions


Solution 1 - Database

It is possible to use a shared database for multiple microservices. You can find the patterns for data management of microservices in this link: http://microservices.io/patterns/data/database-per-service.html. By the way, it is a very useful blog for microservices architecture.

In your case, you prefer to use database per service pattern. This make microservices more autonomous. In this situation, you should duplicate some of your data among multiple microservices. You can share the data with api calls between microservices or you can share it with async messaging. It depends on your infrastructure and frequency of change of the data. If it is not changing often, you should duplicate the data with async events.

In your example, Delivery service can duplicate delivery locations and product information. Product service manage the products and locations. Then the required data is copied to Delivery service's database with async messages (for example you can use rabbit mq or apache kafka). Delivery service does not change the product and location data but it uses the data when it is doing its job. If the part of the product data which is used by Delivery service is changing often, data duplication with async messaging will be very costly. In this case you should make api calls between Product and Delivery service. Delivery service asks Product service to check whether a product is deliverable to a specific location or not. Delivery service asks Products service with an identifier (name, id etc.) of a product and location. These identifiers can be taken from end user or it is shared between microservices. Because the databases of microservices are different here, we cannot define foreign keys between the data of these microservices.

Api calls maybe easier to implement but network cost is higher in this option. Also your services are less autonomous when you are doing api calls. Because, in your example when Product service is down, Delivery service cannot do its job. If you duplicate the data with async messaging, the required data to make delivery is located in the database of Delivery microservice. When Product service is not working you will be able to make delivery.

Solution 2 - Database

When distributing your code to achieve reduced coupling, you want to avoid resource sharing, and data is a resource you want to to avoid sharing.

Another point is that only one component in your system owns the data (for state changing operations), other components can READ but NOT WRITE, they can have copies of the data or you can share a view model they can use to get the latest state of an object.

Introducing referential integrity will reintroduce coupling, instead you want to use something like Guids for your primary keys, they will be created by the creator of the object, the rest is all about managing eventual consistency.

Take a look at Udi Dahan's talk in NDC Oslo for a more details

Hope this helps

Solution 3 - Database

first solution: API Composition

 Implement a query by defining an API Composer, which invoking the
 services that own the data and performs an in-memory join of the
 results

enter image description here

second solution: CQRS

Define a view database, which is a read-only replica that is designed to support that 
query. The application keeps the replica up to data by subscribing to Domain events 
published by the service that own the data.

enter image description here

Solution 4 - Database

A 2020 update to this answer is to use a Change Data Capture tool like Debezium. Debezium will monitor your database tables for changes and stream them to Kafka/Pulsar (other pipes) and your subscribers can then capture the changes and synchronize them.

Solution 5 - Database

> ...How do I make sure that microservice 1 (i.e delivery feature) does not take an order to a unserviced location...

You don't do it online, but in a deferred way.

Your service #1 receives the order, perform all validations it can do by itself, and saves it. A deferred service, processes the order and validates the other aspects of it later on. It may come back as rejected, once the location is found to be non-serviceable. Your service will need to gracefully inform that to the user, and maybe even cancel the order.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
Questionuser8205906View Question on Stackoverflow
Solution 1 - DatabaseAli SağlamView Answer on Stackoverflow
Solution 2 - DatabaseSean FarmarView Answer on Stackoverflow
Solution 3 - DatabaseAli_HrView Answer on Stackoverflow
Solution 4 - Databaseuser521990View Answer on Stackoverflow
Solution 5 - DatabaseThe ImpalerView Answer on Stackoverflow