Entity Relationships Between Microservices
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 aboutUsers.
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
// ...
}
}