e. Repository
Agora, com o controller implementado, podemos passar para a camada de serviço do microsserviço Account. A camada de serviço é responsável pela lógica de negócio do microsserviço e interage com a camada de repositório para tratar a persistência dos dados da entidade Account. A camada de serviço é implementada na classe AccountService, que contém os métodos para criar, buscar (todas, ou uma pelo id) e excluir contas. A classe AccountService usa o AccountRepository para tratar a persistência dos dados da entidade Account e o AccountParser para tratar a conversão da entrada e da saída dos endpoints da API.
No hands-on anterior, já vimos que este microsserviço usa arquitetura limpa (clean architecture), o que significa que a camada de serviço é independente da camada de controller e da camada de repositório. Isso nos permite testar facilmente a lógica de negócio da camada de serviço sem nos preocuparmos com os detalhes da camada de controller ou da camada de repositório. O diagrama a seguir ilustra a arquitetura do microsserviço Account, mostrando as relações entre as diferentes camadas e classes:
sequenceDiagram
title Abordagem da arquitetura limpa
Actor Request
participant Controller as Controller<br>Resource
participant Service@{ "type": "control" }
participant Repository@{ "type" : "entity" }
participant Database@{ "type" : "database" }
Request ->>+ Controller: Rest API (JSON)
Controller ->>+ Service: parser (AccountIn -> Account)
Service ->>+ Repository: parser (Account -> AccountModel)
Repository ->>+ Database:
Database ->>- Repository:
Repository ->>- Service: parser (Account <- AccountModel)
Service ->>- Controller: parser (AccountOut <- Account)
Controller ->>- Request: Rest API (JSON) A relação entre as diferentes camadas é feita por meio de diferentes DTOs (Data Transfer Objects — objetos de transferência de dados). Para traduzir entre as camadas, usamos parsers, responsáveis por converter os DTOs em entidades e vice-versa.
-
A classe
AccountParseré responsável por essa tradução e contém os métodos que convertem o DTOAccountInna entidadeAccounte a entidadeAccountno DTOAccountOut. -
Além disso,
AccountModelé a classe que representa o modelo de banco de dados da entidade Account, usado peloAccountRepositorypara tratar a persistência dos dados da entidade Account. A classeAccountModelcontém os atributos da entidade Account e é anotada com anotações JPA, que definem o mapeamento entre a classe e a tabela do banco de dados.
1. O mapeamento objeto-relacional (ORM)
Este projeto usa um banco de dados relacional (PostgreSQL) para armazenar os dados da entidade Account. Porém, Java é uma linguagem de programação orientada a objetos, o que significa que precisamos de uma forma de mapear os dados relacionais para dados orientados a objetos.
O mapeamento objeto-relacional (ORM, Object-Relational Mapping) é uma técnica de programação que permite interagir com um banco de dados relacional usando uma linguagem de programação orientada a objetos. Ele mapeia tabelas do banco de dados para classes e registros para objetos, permitindo trabalhar com os dados de forma mais intuitiva e natural. No contexto do microsserviço Account, vamos usar um framework ORM, o JPA, para tratar a persistência dos dados da entidade Account, o que nos permitirá criar, ler, atualizar e excluir contas no banco de dados com facilidade.
Aqui podemos ver o código que resolve as questões de persistência de dados do microsserviço Account, incluindo a classe AccountModel, que representa o modelo de banco de dados da entidade Account, e a interface AccountRepository, responsável pela persistência dos dados da entidade Account usando JPA.
2. Migrações de banco de dados
Com o ORM configurado, precisamos gerenciar as mudanças no esquema do banco de dados. À medida que a aplicação evolui, podemos precisar adicionar novas tabelas, modificar tabelas existentes ou mudar os tipos de dados das colunas. Para gerenciar essas mudanças, vamos usar uma ferramenta de migração de banco de dados chamada Flyway1.
O Flyway é uma ferramenta de migração de banco de dados que permite gerenciar e versionar as mudanças no esquema do banco. Ele oferece uma forma de definir e executar migrações, que são scripts que modificam o esquema do banco de dados, como criar tabelas, adicionar colunas ou mudar tipos de dados. No contexto do microsserviço Account, vamos usar o Flyway para gerenciar as migrações, garantindo que o esquema do banco de dados esteja sempre atualizado e consistente entre os diferentes ambientes.
No código, temos um diretório db/migration, que contém os scripts de migração do microsserviço Account. Cada script de migração segue um formato específico de nome, começando por um número de versão (p. ex., V2026.08.24.001) seguido de uma descrição da migração (p. ex., create_schema). Esses scripts são executados em ordem pelo Flyway, garantindo que o esquema do banco de dados esteja sempre atualizado.
3. Código
Vamos codificar a implementação do microsserviço Account, que é composta por várias classes. A estrutura de diretórios resultante ficará assim:
📁 api/
├── 📁 account/
├── 📁 account-service/
│ ├── 📁 src/
│ │ └── 📁 main/
│ │ ├── 📁 java/
│ │ │ └── 📁 store/
│ │ │ └── 📁 account/
│ │ │ ├── Account.java
│ │ │ ├── AccountServiceApplication.java
│ │ │ ├── AccountModel.java
│ │ │ ├── AccountParser.java
│ │ │ ├── AccountRepository.java
│ │ │ ├── AccountResource.java
│ │ │ └── AccountService.java
│ │ └── 📁 resources/
│ │ ├── 📁 db/
│ │ │ └── 📁 migration/
│ │ │ ├── V2026.08.24.001__create_schema.sql
│ │ │ └── V2026.08.24.002__create_table.sql
│ │ └── application.yaml
│ └── pom.xml
└── compose.yaml
Respeitando a arquitetura limpa, temos as seguintes classes:
| Classe | Descrição |
|---|---|
Account | Esta classe representa a entidade Account, a entidade principal do microsserviço Account. Ela contém os atributos da entidade, como id, name, email, password (o valor original, só na entrada) e hashPassword. |
AccountModel | Esta classe é a entidade JPA mapeada para a tabela accounts.accounts (id, name, email, hash_password). Seu construtor AccountModel(Account) e seu método to() fazem a conversão entre a entidade e o modelo. |
AccountParser | Esta classe é responsável por converter a entrada e a saída dos endpoints da API, transformando os DTOs AccountIn e AccountOut na entidade Account e vice-versa. |
AccountRepository | Esta interface é responsável pela persistência dos dados da entidade Account, usando um framework de mapeamento objeto-relacional (ORM) para interagir com o banco de dados. Ela estende CrudRepository e redeclara findAll() para devolver uma List em vez de um Iterable. |
AccountResource | Esta classe é responsável pelos endpoints da API do microsserviço Account: ela implementa a interface AccountController definida no módulo account e usa o AccountService para tratar a lógica de negócio dos endpoints. |
AccountService | Esta classe é responsável pela lógica de negócio do microsserviço Account, usando o AccountRepository para tratar a persistência dos dados da entidade Account e o AccountParser para tratar a conversão da entrada e da saída dos endpoints da API. |
Código-fonte
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 | |
As regras de negócio ficam no AccountService:
createresponde400 Bad Requestquando a senha está ausente ou tem menos de 6 caracteres, ou quando o email está vazio; caso contrário, guarda apenas o hash SHA-256 (em Base64) da senha, emhash_password— nunca a própria senha.findByIddevolvenullpara um id inexistente, e oAccountResourcetransforma isso em404 Not Found.
4. Execução
Para executar o microsserviço Account, podemos usar o comando docker-compose para iniciar o microsserviço e as suas dependências (p. ex., o banco de dados PostgreSQL). Certifique-se de estar no diretório raiz do projeto, onde fica o arquivo compose.yaml, e execute o seguinte comando:
Observe que a flag --build é usada para gerar as imagens Docker antes de iniciar os containers, o que é necessário se você fez mudanças no código ou no Dockerfile.
Também podemos monitorar os logs do microsserviço para ver se ele está rodando corretamente. Observe que as migrações do Flyway estão sendo executadas e que o microsserviço está subindo sem erros. Você deve ver logs indicando que o microsserviço está no ar e conectado ao banco de dados. Além disso, você também pode verificar o banco de dados para conferir se as migrações foram executadas corretamente e se as tabelas foram criadas como esperado.
-
Entrando no banco de dados PostgreSQL. O
psqlfica no containerdb(a imagempostgres), não no do account; por isso, abra-o lá, com o nome do serviço docompose.yamle o usuário/banco definidos porDB_USEReDB_NAME(ambosstore): -
Verificando as migrações:
E, se você quiser monitorar os logs do microsserviço sem entrar no container, pode usar o comando docker compose logs com o nome do serviço (account) para ver os logs do container. Assim você acompanha os logs em tempo real e pode monitorar a inicialização do microsserviço, bem como quaisquer erros ou problemas que surjam.
Pronto!