Home > Back-end >  How to translate @ManyToOne relation in Monolith app into a Microservice App?
How to translate @ManyToOne relation in Monolith app into a Microservice App?

Time:11-01

I have a monolith app where its models are joined to each others(OnetOne, ManyToMany..).

I was able to create the different Microservices, but I got stuck on how to transition these relationships into Microservices.

Here is my first Class:

@Entity
@Table
public class A {
    
    @Id 
    @GeneratedValue(strategy=GenerationType.AUTO)
    private int id;
    private String name;
    
    @ManyToOne
    @JoinColumn(name = "ID",referencedColumnName="ID")
    private B b;
    
    //getters and setters
}

@Entity
@Table
public class B{
    
    @Id 
    @GeneratedValue(strategy=GenerationType.AUTO)
    private int id;
    private String name;
    
    //getters and setters
}

I also Created a microservice for A (controller,repository, service...) and a separate microservice for B.

I am trying to call the Class Model B from the microservice B. But I am not sure how to do it?

I also wonder if it is write to link two classes by joint in microservices or not ?

Thanks

CodePudding user response:

The join relations such as @OneToOne or @ManyToMany are JPA specific and there is no straightforward way to make them work in microservice world.

In general, in microservice world you give up the ACID transactions for cross-service relations and replace them with BASE transactions (eventual consistency behaviour).

In your example, you can achieve this by implementing one of the following strategies.

Fetch the required entity using rest API from the other service. As you divide your domain into different bounded contexts (services), you will eventually create two different stores with the following entities:

Service A

@Entity
@Table
public class A {
    
    @Id 
    @GeneratedValue(strategy=GenerationType.AUTO)
    private int id;
    private String name;
    
    @Column(name = "B_REFERENCE_ID")
    private Integer bId;
    
    //getters and setters
}

And Service B:

@Entity
@Table
public class B{
    
    @Id 
    @GeneratedValue(strategy=GenerationType.AUTO)
    private int id;
    private String name;
    
    @ElementCollection
    private List<Integer> aIds;
    //getters and setters
}

Then, you create your target DTO in the the service (example for service B):

public class BDto{

    private int id;
    private String name;

    private List<ADto> aIds;
    //getters and setters
}

Then, you need to fetch the dto you want to expose/consume yourself:

@RestController
public class BController {
    private final WebClient webClient;

    public BController() {
        this.webClient = WebClient.builder()
                .baseUrl(SERVICE_A_URL)
                .build();
    }

    @GetMapping(path = "/{id}")
    public Mono<BDto> getB(@PathVariable int id) {
        Optional<B> bEntity = fetchBFromDatabase();
        if (bEntity.isPresent()) {
            var b = bEntity.get();
            var aEntityIds = b.getaIds();
            return webClient
                    .method(HttpMethod.GET)
                    .uri(GET_A_URL, aEntityIds)
                    .exchangeToFlux(response -> response.bodyToFlux(ADto.class))
                    .collect(Collectors.toList()).map(aDtos -> new BDto(b.getId(), b.getName(), aDtos));
        }
        return Mono.empty();
    }
}

If you are unfimiliar with WebClient and reactive concepts, reference spring boot docs https://docs.spring.io/spring-framework/docs/current/reference/html/web-reactive.html

Of course, the good old RestTemplate can be used here as well.

In order to provide data consistency, you will need to implements eventing system with a message broker in between such as Kafka, Apache Pulsar or RabbitMQ.

As an alternative approach, you can keep both A and B entities in both microservices. In service A, you store only the information of B entity that is required in the service A domain and vice versa. In microservice world it is rare that you will require all the B data in service A.

Then, you can keep your join relations as they are in A and B services for fetching purposes.

Remember that you will still require only single source of truth. So, if data changes in service B, then you will need to update your B_ENTITY data in service A and vice versa. Thus, eventing system will still be required to properly updates states in both your services.

The topic of state management in microservices is a complex one, so I recommend to read more about it to get more comfortable with the topic:

https://phoenixnap.com/kb/acid-vs-base

https://www.amazon.com/Domain-Driven-Design-Tackling-Complexity-Software/dp/0321125215 https://samnewman.io/books/building_microservices_2nd_edition/

CodePudding user response:

Microservices are meant to be indepedent, so that if one service failed, it will not affect the others.

But if you are using multi-module structure, then add the module using the following code to your pom.xml:

<modules>  
<module>module1</module>  
<module>module2</module>  
</modules>  

but I am not sure this will work with Jpa.

  • Related