CQRS (Command Query Responsibility Segregation) com Exemplo em Java
Introdução
O CQRS (Command Query Responsibility Segregation) é um padrão de design de arquitetura de software que trata da separação de operações de consulta (queries) e de comandos (commands) que modificam o estado do sistema. Este padrão é especialmente útil para aumentar a escalabilidade, o desempenho e a manutenibilidade de sistemas complexos.
Vantagens do CQRS
- Escalabilidade: Permite otimizar a leitura e a escrita de dados separadamente.
- Desacoplamento: Separa as responsabilidades de leitura e escrita.
- Simplificação de Código: Facilita a manutenção ao isolar a complexidade.
Estrutura Básica
Em um cenário onde se deseja aplicar CQRS (Command Query Responsibility Segregation) em uma aplicação Java, é possível fazê-lo a nível de serviço. Vamos estender o exemplo anterior de um sistema de gerenciamento de estudantes, agora dividindo as operações de comando e consulta em serviços separados.
A estrutura principal será dividida em duas partes:
- Command Service: Responsável por todas as operações de comando.
- Query Service: Responsável por todas as operações de consulta
Código Fonte
Modelo de Domínio (Student.java)
@Entity
public class Student {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private String email;
// Getters e Setters
}
Repositório (StudentRepository.java)
@Repository
public interface StudentRepository extends JpaRepository<Student, Long> {
List<Student> findByNameContaining(String name);
}
Serviços
Command Service (StudentCommandService.java)
@Service
public class StudentCommandService {
@Autowired
private StudentRepository repository;
@Transactional
public Student createOrUpdateStudent(Student student) {
return repository.save(student);
}
@Transactional
public void deleteStudent(Long id) {
repository.deleteById(id);
}
}
Query Service (StudentQueryService.java)
@Service
public class StudentQueryService {
@Autowired
private StudentRepository repository;
public Student findStudentById(Long id) {
return repository.findById(id).orElse(null);
}
public List<Student> findStudentsByName(String name) {
return repository.findByNameContaining(name);
}
}
Controladores
Aqui, dividimos as operações de comandos e consultas em métodos separados no controlador.
@RestController
public class StudentController {
@Autowired
private StudentCommandService commandService;
@Autowired
private StudentQueryService queryService;
@PostMapping("/students")
public Student createStudent(@RequestBody Student student) {
return commandService.createOrUpdateStudent(student);
}
@DeleteMapping("/students/{id}")
public void deleteStudent(@PathVariable Long id) {
commandService.deleteStudent(id);
}
@GetMapping("/students/{id}")
public Student findStudentById(@PathVariable Long id) {
return queryService.findStudentById(id);
}
@GetMapping("/students")
public List<Student> findStudentsByName(@RequestParam String name) {
return queryService.findStudentsByName(name);
}
}
Conclusão
Neste exemplo, aplicamos o padrão CQRS separando as operações de comandos e consultas em serviços distintos. Isso traz uma série de benefícios, incluindo melhor manutenibilidade e a possibilidade de otimizar cada tipo de operação de forma independente.
Esta é uma abordagem simplificada e serve como um ponto de partida. Em cenários mais complexos, você pode querer adotar outras práticas, como Event Sourcing, para tornar o sistema ainda mais robusto e escalável.