Projeto simplificado de um autenticador de usuários utilizando JWT, Spring Boot, Spring Security, JPA, MySQL, SOLID e Clean Architecture.
src
└── main
└── java
└── com
└── seu
└── projeto
├── application
│ ├── dto // Data Transfer Objects
│ ├── exceptions // Exceções especĂficas da aplicação
│ ├── services // Serviços da camada de aplicação
│ ├── usecases // Casos de uso (interações da aplicação)
│ └── mappers // Mapeadores entre entidades e DTOs
│
├── domain
│ ├── entities // Entidades de domĂnio
│ ├── repositories // Interfaces de repositórios
│ ├── services // Lógica de negócios
│ ├── specifications // Especificações de domĂnio (predicados)
│ └── events // Eventos de domĂnio
| └── valueobjects // Objetos de valor
| └── factories // Fábricas de entidades
│
├── adapters
│ ├── controllers // Controladores (REST, gRPC, etc.)
│ ├── gateways // Interfaces para sistemas externos
│ ├── presenters // Formatação de dados para saĂda
│ └── mappers // Mapeadores de entidades para DTOs (separados dos da aplicação)
│
└── infra
├── configuration // Configurações gerais (Spring, segurança, etc.)
├── database // Configuração de banco de dados (JPA, JDBC)
│ ├── entities // Entidades especĂficas para o banco de dados
│ ├── repositories // Implementações dos repositórios
│ └── migrations // Scripts de migração do banco de dados
│
├── messaging // Configuração de mensageria (RabbitMQ, Kafka, etc.)
├── security // Configuração de segurança (Spring Security, autenticação)
├── logging // Configuração de logging (Logback, SLF4J, etc.)
├── cache // Configuração de cache (Redis, Memcached)
└── external // Integrações com serviços externos (APIs, SDKs)
- dto: Contém os Data Transfer Objects, que são objetos usados para transportar dados entre diferentes camadas da aplicação, especialmente entre a camada de apresentação (adaptadores) e a camada de aplicação. Eles geralmente não contêm lógica de negócios, mas sim estruturas de dados simples.
- exceptions: Exceções especĂficas que podem ser lançadas durante a execução dos casos de uso ou serviços. Isso ajuda a centralizar o tratamento de erros.
- services: Serviços da camada de aplicação que orquestram a lógica de negócios e a interação entre os casos de uso. Eles podem chamar múltiplos casos de uso e coordenar suas operações.
- usecases: Casos de uso que representam interações especĂficas que os usuários podem ter com a aplicação. Cada caso de uso Ă© responsável por uma ação ou tarefa, encapsulando a lĂłgica necessária para completá-la.
- mappers: Mapeadores que transformam entidades de domĂnio em DTOs e vice-versa. Eles sĂŁo Ăşteis para separar a lĂłgica de conversĂŁo e manter os casos de uso mais limpos.
- entities: ContĂ©m as entidades de domĂnio, que sĂŁo representações dos objetos de negĂłcio e contĂŞm a lĂłgica relacionada ao estado e ao comportamento desses objetos.
- repositories: Interfaces que definem operações de acesso a dados para as entidades de domĂnio. Os repositĂłrios abstraem a lĂłgica de persistĂŞncia e permitem que a aplicação interaja com a camada de dados.
- services: LĂłgica de negĂłcios que nĂŁo se encaixa em uma entidade especĂfica. Por exemplo, serviços que realizam operações complexas envolvendo mĂşltiplas entidades. Esses serviços podem incluir regras de negĂłcios que sĂŁo fundamentais para a lĂłgica da aplicação.
- specifications: Predicados ou critĂ©rios que podem ser usados para consultar entidades. Eles ajudam a separar a lĂłgica de consulta da lĂłgica de domĂnio.
- events: Representam eventos que ocorrem no domĂnio, como alterações de estado. Eles podem ser usados para implementar padrões como Event Sourcing ou CQRS.
- valueobjects: Objetos que representam valores (como moeda ou endereço) e não têm identidade própria. Eles são imutáveis e geralmente são usados para encapsular regras de validação e lógica.
- factories: Classes responsáveis pela criação de entidades ou objetos complexos, encapsulando a lógica de construção.
- controllers: Controladores que recebem as solicitações do usuário e orquestram as chamadas aos casos de uso apropriados. Eles também formatam as respostas a serem enviadas de volta ao cliente.
- gateways: Interfaces que abstraem a comunicação com sistemas externos, como APIs ou serviços de terceiros. Eles permitem que a aplicação se integre com serviços externos sem acoplamento forte.
- presenters: Formatação de dados para saĂda. Eles preparam os dados que serĂŁo retornados ao usuário, possivelmente convertendo dados de domĂnio em formatos mais amigáveis.
- mappers: Mapeadores especĂficos para conversĂŁo de entidades em DTOs (separados da aplicação). Esses mapeadores sĂŁo usados para transformar os dados que vĂŞm do banco ou de outras fontes em estruturas que a camada de apresentação pode usar.
- configuration: Configurações gerais da aplicação, como as configurações do Spring, segurança, etc. Esse pacote pode conter classes de configuração e inicialização.
- database: Configuração especĂfica de banco de dados. Pode conter entidades JPA, repositĂłrios concretos que implementam as interfaces definidas na camada de domĂnio, e scripts de migração.
- messaging: Configuração de sistemas de mensageria, como RabbitMQ ou Kafka. Isso pode incluir tópicos, filas e configurações para a publicação e assinatura de mensagens.
- security: Configurações de segurança, incluindo autenticação e autorização usando frameworks como o Spring Security.
- logging: Configuração de logging, como integração com Logback ou SLF4J, permitindo a captura de logs da aplicação.
- cache: Configuração de sistemas de cache, como Redis ou Memcached, para otimizar o desempenho da aplicação.
- external: Integrações com serviços externos, como APIs ou SDKs. Esse pacote pode conter classes e configurações para se conectar e interagir com serviços fora da aplicação.
-
Services da Application:
- Responsáveis por orquestrar a lógica de interação entre os casos de uso. Eles lidam com a lógica de aplicação, como controle de fluxo, validação de entrada e coordenação de chamadas a múltiplos casos de uso.
- Exemplo: Um serviço de aplicação pode ser responsável por validar um usuário, chamar o caso de uso de registro e, em seguida, gerar um token.
-
Services da Domain:
- ContĂŞm a lĂłgica de negĂłcios que está intrinsecamente relacionada Ă s entidades de domĂnio. Eles podem incluir operações complexas que envolvem regras de negĂłcio, mas nĂŁo estĂŁo diretamente ligadas ao controle de fluxo da aplicação.
- Exemplo: Um serviço de domĂnio pode ter a responsabilidade de calcular o preço total de um pedido, considerando regras especĂficas de negĂłcio, como descontos ou promoções.
- Responsabilidade: Os mappers da camada application sĂŁo responsáveis por transformar entidades de domĂnio em Data Transfer Objects (DTOs) e vice-versa. Eles atuam na conversĂŁo de dados que serĂŁo usados internamente pela aplicação, garantindo que as entidades de domĂnio sejam representadas adequadamente nas interações de uso dos casos.
- Objetivo: O objetivo principal é facilitar a comunicação entre a lógica de negócios (casos de uso) e a apresentação (controladores), mantendo a separação de preocupações e a clareza na manipulação dos dados.
- Exemplo: Se um caso de uso precisa de um DTO para receber dados de entrada de um controlador, o mapper da aplicação converte esse DTO em uma entidade de domĂnio.
public class UserMapper {
public static User toEntity(UserDTO userDTO) {
return new User(userDTO.getUsername(), userDTO.getPassword());
}
public static UserDTO toDTO(User user) {
return new UserDTO(user.getUsername());
}
}
- Responsabilidade: Os mappers da camada adapters são encarregados de adaptar os dados entre a camada de apresentação (como uma API REST ou um serviço gRPC) e a estrutura interna da aplicação. Eles garantem que as informações sejam formatadas corretamente para serem enviadas ou recebidas de clientes ou sistemas externos.
- Objetivo: O foco está na integração com o mundo exterior, ajustando a forma como os dados são apresentados e recebidos, garantindo que as interfaces externas se comuniquem efetivamente com a lógica interna da aplicação.
- Exemplo: Quando um controlador precisa responder a uma requisição com dados de um usuário, o mapper da adapters converte a entidade de domĂnio em um DTO apropriado para a resposta da API.
public class UserAdapterMapper {
public static UserDTO toDTO(User user) {
return new UserDTO(user.getUsername(), user.getPermissions());
}
}
- Definição: Os use cases representam ações ou tarefas especĂficas que os usuários podem executar na aplicação. Cada caso de uso encapsula a lĂłgica necessária para realizar essa ação e possui um ponto de entrada e saĂda bem definidos.
- Responsabilidade: Eles se concentram em um fluxo especĂfico e contĂŞm a lĂłgica que orquestra a interação entre diferentes componentes da aplicação, como repositĂłrios e serviços de domĂnio.
- Exemplo: Um caso de uso
RegisterUser
pode ser responsável por registrar um novo usuário no sistema.
@Component
public class RegisterUser {
private final UserRepository userRepository;
public RegisterUser(UserRepository userRepository) {
this.userRepository = userRepository;
}
public void execute(UserDTO userDTO) {
User user = UserMapper.toEntity(userDTO);
userRepository.save(user);
}
}
- Definição: Os services na camada de application atuam como orquestradores que integram e gerenciam a lógica de aplicação. Eles podem chamar múltiplos casos de uso e também podem incluir lógica adicional que não se encaixa em um único caso de uso.
- Responsabilidade: Eles facilitam a reutilização de lógica comum entre diferentes casos de uso e gerenciam o fluxo de dados através de diferentes componentes da aplicação.
- Exemplo: Um serviço
UserService
pode gerenciar a lógica de registro e também enviar um e-mail de boas-vindas após o registro de um usuário.
@Service
public class UserService {
private final RegisterUser registerUser;
private final EmailService emailService;
public UserService(RegisterUser registerUser, EmailService emailService) {
this.registerUser = registerUser;
this.emailService = emailService;
}
public void register(UserDTO userDTO) {
registerUser.execute(userDTO);
emailService.sendWelcomeEmail(userDTO.getEmail());
}
}
-
Mappers:
- Application Mappers: Transformam entidades de domĂnio em DTOs e vice-versa, focando na comunicação entre lĂłgica de negĂłcios e controladores.
- Adapters Mappers: Adaptam dados entre a estrutura interna da aplicação e o formato esperado por interfaces externas, como APIs.
-
Use Cases vs. Services:
- Use Cases: Representam ações especĂficas que o usuário pode realizar e encapsulam a lĂłgica para essa ação. Eles sĂŁo chamados diretamente pelos controladores.
- Services: Integram e orquestram a lógica de múltiplos casos de uso e podem incluir lógica adicional. Eles facilitam a reutilização e a coordenação entre diferentes partes da aplicação.
openssl genpkey -algorithm RSA -out private_key.pem -pkeyopt rsa_keygen_bits:2048
openssl rsa -pubout -in private_key.pem -out public_key.pem