Este projeto é uma API de gerenciamento de usuários construída com Java, utilizando Arquitetura Hexagonal (Ports & Adapters) e princípios de Domain-Driven Design (DDD).
A API permite:
- Cadastro de usuário
- Alteração de senha
- Alteração de email
⚠️ A operação de exclusão (delete) não está disponível, seguindo uma proposta mais controlada e auditável do ciclo de vida do usuário.
- Controller (adapter inbound) recebe a requisição.
- Um caso de uso (ex:
InsertUserUseCaseImpl) é acionado. - O domínio é manipulado via Value Objects e entidades imutáveis.
- Os adapters (outbound) realizam a persistência e codificação de senha.
Exemplo da entidade User com métodos que retornam novas instâncias em vez de modificar o estado:
public User changePasswordTo(String newPassword) {
return new User(this.id, this.name, this.email, new Password(newPassword));
}Organizado em três camadas principais:
application, domain e infra
.
└── com.sirkaue.hexagonalarchitecture/
├── application/
│ ├── helper/
│ │ └── EntityFinderHelper.java
│ ├── ports/
│ │ ├── in/
│ │ │ ├── FindUserByIdUseCase.java
│ │ │ ├── InsertUserUseCase.java
│ │ │ ├── UpdateUserEmailUseCase.java
│ │ │ └── UpdateUserPasswordUseCase.java
│ │ └── out/
│ │ ├── FindUserByIdPort.java
│ │ ├── InsertUserPort.java
│ │ ├── PasswordEncoderPort.java
│ │ ├── UpdateUserPort.java
│ │ └── UserExistsByEmailPort.java
│ └── usecase/
│ ├── FindUserByIdUseCaseImpl.java
│ ├── InsertUserUseCaseImpl.java
│ ├── UpdateUserEmailUseCaseImpl.java
│ └── UpdateUserPasswordUseCaseImpl.java
├── domain/
│ ├── exception
│ ├── model
│ └── valueobjects
└── infra/
├── adapters/
│ ├── in/
│ │ ├── dto/
│ │ │ ├── request/
│ │ │ │ ├── UpdateEmailRequest.java
│ │ │ │ ├── UpdatePasswordRequest.java
│ │ │ │ └── UserRequest.java
│ │ │ └── response/
│ │ │ ├── ErrorResponse.java
│ │ │ ├── FieldErrorResponse.java
│ │ │ ├── UserResponse.java
│ │ │ └── ValidationErrorResponse.java
│ │ ├── mapper/
│ │ │ ├── impl/
│ │ │ │ └── UserMapperImpl.java
│ │ │ └── UserMapper.java
│ │ └── web/
│ │ ├── controller/
│ │ │ └── UserController.java
│ │ └── exception/
│ │ └── RestExceptionHandler.java
│ └── out/
│ ├── persistence/
│ │ ├── entity/
│ │ │ └── UserEntity.java
│ │ └── mapper/
│ │ ├── impl/
│ │ │ └── UserEntityMapperImpl.java
│ │ └── UserEntityMapper.java
│ ├── repository/
│ │ └── UserJpaRepository.java
│ ├── FindUserByIdAdapter.java
│ ├── InsertUserAdapter.java
│ ├── PasswordEncoderAdapter.java
│ ├── UpdateUserEmailAdapter.java
│ ├── UpdateUserPasswordAdapter.java
│ └── UserExistsByEmailAdapter.java
└── config/
├── EntityFinderHelperConfig.java
├── FindUserByIdConfig.java
├── InsertUserConfig.java
├── PasswordEncoderConfig.java
├── SpringDocOpenApiConfig.java
├── UpdateUserEmailConfig.java
└── UpdateUserPasswordConfig.java
- Java 17+
- Spring Boot
- Arquitetura Hexagonal (Ports & Adapters)
- Domain-Driven Design (DDD)
- Modelo de domínio rico e imutável
- Separação em camadas:
application,domain,infra - Testes com JUnit 5 e Mockito
- Testes de integração com Spring Boot
- Docker e Docker Compose
- Documentação com Swagger/OpenAPI
- Application: camadas que orquestram os fluxos de negócio.
- Domain: lógica de negócio rica, imutável e independente de frameworks.
- Infra: adaptações técnicas, como persistência e criptografia.
Usercomo entidade imutável.Name,EmailePasswordcomo Value Objects com responsabilidade própria.- Exceções de domínio claras (
EmailAlreadyExistsException, etc).
Foi configurado um PasswordEncoder para garantir que as senhas sejam armazenadas com segurança:
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}O projeto já possui:
- Testes unitários para lógica de domínio e casos de uso.
- Testes de integração utilizando Spring Boot.
public User execute(User user) {
if (userExistsByEmailPort.existsByEmail(user.getEmail().value())) {
throw new EmailAlreadyExistsException("Email already exists");
}
String hashedPassword = passwordEncoderPort.encode(user.getPassword());
user = user.changePasswordTo(hashedPassword);
return insertUserPort.insert(user);
}POST /users
{
"name": "Jose",
"email": "jose@email.com",
"password": "jose123321"
}Resposta
- Status:
201 Created - Location Header:
http://localhost:8080/users/1
PATCH /users/{id}/email
{
"email": "jose1@email.com"
}Resposta
- Status:
204 No Content
PATCH /users/{id}/email
{
"currentPassword": "jose123321",
"newPassword": "jose123",
"confirmPassword": "jose123"
}Resposta
- Status:
204 No Content
- Java 17+
- Maven
git clone https://github.com/sirkaue/hexagonal-architecture.gitEste projeto utiliza um Dockerfile multi-stage, separando as etapas de build e runtime para gerar uma imagem leve e eficiente.
git clone https://github.com/sirkaue/hexagonal-architecture.git
cd hexagonal-architectureExecute o comando abaixo na raiz do projeto (onde está o Dockerfile e o docker-compose.yml):
docker compose up --build- A aplicação será executada em
http://localhost:8080 - O MySQL estará disponível internamente como
mysql-dbna rede do Docker. - O banco estará acessível externamente na porta
3307
Este projeto é um exemplo prático de como aplicar a Arquitetura Hexagonal (Ports & Adapters) em uma aplicação com Spring Boot, aliada aos princípios de Domain-Driven Design (DDD).
Ele demonstra como construir um serviço de usuários com domínio rico, imutável e desacoplado de frameworks, promovendo flexibilidade, testabilidade e evolução sustentável da aplicação.