Skip to content

Entity Relationships Between Microservices

mmd

Introduction

In the context of software engineering and microservices architecture, the way entities relate to each other is crucial for ensuring the system's integrity and efficiency. This document addresses how to manage relationships between microservices that use separate databases, discusses the use of a single database for multiple microservices, and covers data replication through domain events.

1. Entity Relationships Between Microservices

1.1. Identification by External ID

In a scenario where microservices have their own databases, one of the challenges is the need to relate entities belonging to different services. The recommended practice is to use external IDs. This means that instead of maintaining a direct reference to another entity (e.g., a foreign key), a microservice will store the ID of the entity from another microservice.

Example:

  • Service A: Manages Users
  • Service B: Manages Orders

In Service B's database, instead of having a foreign key that directly references a User from Service A, Service B only stores the userId of the User. This creates a decoupling between the services and allows each to be scalable and independent.

1.2. Advantages

  • Decoupling: Microservices can be changed or scaled independently.
  • Resilience: If one microservice is down, the other can still function, provided it has the logic to handle the absence of data.
  • Ease of maintenance: Changes in one microservice do not directly impact the other.

2. Microservices with the Same Database

2.1. Shared Use of a Database

In some cases, it may be desirable for multiple microservices to access the same database. This can be done when the microservices are tightly coupled or when there is a high maintenance cost in splitting the database.

Remapping the Domain as a Subdomain

When two or more microservices use the same database, one can consider implementing a subdomain. The subdomain is a part of the business logic that is grouped according to a specific context. For example:

  • Service A may be responsible for managing Users
  • Service B may be responsible for managing Authentication

Both services access the same database, but each focuses on a different aspect of the domain.

2.2. Advantages

  • Data consistency: Easy management of data that needs to be accessed by multiple services.
  • Simplicity: Reduces architectural complexity by avoiding the need for inter-service communication for simple operations.

3. Data Replication in Subdomains

3.1. Mapping Entities in Subdomains

When we have the same entity mapped in different subdomains, data replication is an important issue. To ensure that information is consistent across subdomains, we can use the concept of domain events.

Replication Example:

  • Service A: Manages Users
  • Service B: Manages Notifications, which also needs information about Users.

When a User is created or updated in Service A, it can publish a domain event, such as UserCreated or UserUpdated. Service B can listen to these events and update its own database with the necessary information.

3.2. Advantages of Replication via Domain Events

  • Decoupling: Services do not need to know about each other; they just listen for events.
  • Scalability: Each service can scale independently.
  • Eventual consistency: Data is kept up-to-date without the need for direct calls between services.

Examples

Scenario

  • Collaborator: Represents a user in the system.
  • Feedback Request: A request made by one collaborator to receive feedback from another.
  • Service A: Manages collaborators.
  • Service B: Manages feedback requests.

1. Entity Relationships Between Microservices

1.1. Identification by External ID

Collaborator Entity (Service A)

@Entity
public class Colaborador {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String nome;
    private String email;

    // Getters and Setters
}

Feedback Request Entity (Service B)

@Entity
public class RequisicaoFeedback {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private Long colaboradorId; // ID of the collaborator making the request
    private Long colaboradorFeedbackId; // ID of the collaborator who will receive the feedback
    private String mensagem;

    // Getters and Setters
}

1.2. Example of Creating a Feedback Request

@RestController
@RequestMapping("/feedback")
public class RequisicaoFeedbackController {

    @Autowired
    private RequisicaoFeedbackService feedbackService;

    @PostMapping("/request")
    public ResponseEntity<RequisicaoFeedback> solicitarFeedback(@RequestBody RequisicaoFeedbackDTO dto) {
        RequisicaoFeedback feedback = feedbackService.solicitarFeedback(dto);
        return ResponseEntity.status(HttpStatus.CREATED).body(feedback);
    }
}

DTO for Feedback Request

public class RequisicaoFeedbackDTO {
    private Long colaboradorId;
    private Long colaboradorFeedbackId;
    private String mensagem;

    // Getters and Setters
}

2. Microservices with the Same Database

2.1. Shared Use of a Database

Collaborator Entity (Service A and B)

@Entity
public class Colaborador {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String nome;
    private String email;

    // Getters and Setters
}

Feedback Request Entity (Service B)

@Entity
public class RequisicaoFeedback {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private Long colaboradorId; // ID of the collaborator making the request
    private Long colaboradorFeedbackId; // ID of the collaborator who will receive the feedback
    private String mensagem;

    // Getters and Setters
}

2.2. Example of Creating a Feedback Request

@RestController
@RequestMapping("/feedback")
public class RequisicaoFeedbackController {

    @Autowired
    private RequisicaoFeedbackService feedbackService;

    @PostMapping("/request")
    public ResponseEntity<RequisicaoFeedback> solicitarFeedback(@RequestBody RequisicaoFeedbackDTO dto) {
        RequisicaoFeedback feedback = feedbackService.solicitarFeedback(dto);
        return ResponseEntity.status(HttpStatus.CREATED).body(feedback);
    }
}

In this scenario, both services (Service A and Service B) access the same database. Collaborators are managed in the same database, but the feedback logic is isolated in Service B.


3. Data Replication in Subdomains

3.1. Mapping Entities in Subdomains

Publishing a Domain Event when Creating a Feedback Request

When a collaborator requests feedback, an event is published to inform other services.

public class RequisicaoFeedbackService {

    @Autowired
    private ApplicationEventPublisher eventPublisher;

    public RequisicaoFeedback solicitarFeedback(RequisicaoFeedbackDTO dto) {
        RequisicaoFeedback feedback = new RequisicaoFeedback();
        feedback.setColaboradorId(dto.getColaboradorId());
        feedback.setColaboradorFeedbackId(dto.getColaboradorFeedbackId());
        feedback.setMensagem(dto.getMensagem());

        // Save the request to the database
        // ...

        // Publish event
        eventPublisher.publishEvent(new FeedbackRequestedEvent(feedback));

        return feedback;
    }
}

Domain Event

public class FeedbackRequestedEvent {
    private final RequisicaoFeedback feedback;

    public FeedbackRequestedEvent(RequisicaoFeedback feedback) {
        this.feedback = feedback;
    }

    public RequisicaoFeedback getFeedback() {
        return feedback;
    }
}

3.2. Consuming the Domain Event

In Service B, a listener can be implemented to handle the event and update the database.

@Component
public class FeedbackRequestedListener {

    @Autowired
    private ColaboradorRepository colaboradorRepository;

    @EventListener
    public void handleFeedbackRequested(FeedbackRequestedEvent event) {
        RequisicaoFeedback feedback = event.getFeedback();
        // Logic to update or store data related to the feedback
        // ...
    }
}