Hibernate Join table in microservice architecture
I have two micro-services, one is user service and other is product service, both has its own database
Product service db tables
user_products (id, user_id, product_id)
products (id, name)
User service db tables
User(id, name, ..)
User and and product have many to many relation. Since user_products is join table which is only present in product service. I am not sure how to create hibernate model objects for product service tables, so that I can get:
list of all products which belongs to particular user
list of all owners of product
Not sure how to define @ManyToMany relationship with just above two tables
A @ManyToMany in conceptual terms is just two @OneToMany’s – one from each of the two domain’s perspectives:
- Users can have many products
- Products can have many users.
To maintain separation between our domains, we’ll need 4 tables instead of the usual 3 in a monolithic, 2 in each domain:
user (user_id, name, ...) user_product (user_id, product_d) product (product_id, name, ...) product_user (product_id, user_id)
Say we’re in the user domain and we wish to add a couple of existing products to them that are already identified by product_id. Your user service simply adds the records to the user_products table, and for each record publishes the event (including the product_id and user_id) to a topic or queue or whatever asynchronous method is available. A listener in the product service picks up these events, and from each inserts records into the product_user table. You can publish remove events for when you want to remove a product(s) from a user too of course.
You do exactly the same thing for the other direction but from the user perspective. Now you can get all the product objects assigned to any user_id, and all the user objects assigned to any product_id.
Physically speaking, you’re duplicating information in regards to your many-to-many join table by putting it in two domains. It’s a bit more work because you need to keep things synchronized using event publishing, but unfortunately you don’t get isolation for free.
Analyzing to your problem, don’t you think Product and Owner cannot have many to many relationship.
How can be one product could be owned by two different owners (users)?
Generally, product ID in the e-commerce system identifies 1 unique physical entity. Even if there are more than 1 number of similar products (which is an actual production scenario) IMO each product will have different product ID.
But still, let’s consider some different case where two entities are dependent on each other. In here the concepts that come in a picture is bounded context.
Every service should own its data and should be responsible for its integrity and mutability. Each service should exist independently i.e. with the ability to be changed and moved in and out of a runtime environment without side effects to other services.
Let’s take a clearer example of company which needs to manage Project among their employees. We could see two different contexts here (right now ignoring all other company related handling)
As depicted in the picture, we cannot have a direct reference of Employee entity in the Project entity.
The graceful solution is: assuming the object models are mapped to a relational data source, instead of the Project Service having to map an Employee type, it only must map the employee id attribute.
The underlying mapped table for the Project_Resource model will not materialize an Employee object from the database. In order to obtain or mutate an Employee, you will utilize the employee service API calls from employee context.
With this, there are some more mechanisms that must be introduced to support this isolation. Specifically, when an employee is deleted through the employee service, how are other services made aware of this deletion? Employee service synchronously calling Product service will result in the high coupling. The asynchronous pattern should be used in this case to decouple other services (more towards publishing the event and another service could decide if they have to take action or not)
The following blog explains very nicely other use case and other solution depicting the use of Bounded context taking care of low coupling and promoting cohesion.