API em Java 21 com Spring Boot para upload, processamento assíncrono e consulta de resultado de arquivos.
Esta aplicacao implementa o fluxo pedido no teste tecnico:
- receber upload de arquivo texto
- validar cabecalho antes do processamento
- processar o arquivo em background
- persistir historico e resumo em banco relacional
- permitir consulta de status e resultado com controle de acesso por role
- Docker
- O script
mvn-dev-dockerincluido no repositorio
Fluxo mais curto para validar a API localmente:
docker compose -f docker-compose.dev.yml up -d
./mvn-dev-docker spring-boot:runDepois disso:
- API:
http://localhost:8082 - Swagger UI:
http://localhost:8082/swagger-ui/index.html - OpenAPI JSON:
http://localhost:8082/v3/api-docs - banco:
localhost:5432 - arquivo de exemplo para upload manual:
exemplo-registros.txt
docker-compose.dev.yml: sobe apenas o PostgreSQL para desenvolvimentodocker-compose.prod.yml: sobe PostgreSQL + API empacotada em Dockermvn-dev-docker: wrapper para rodar Maven em container no fluxo de desenvolvimento
- Java 21
- Spring Boot 3
- Spring Web
- Spring Security
- Spring Data JPA
- PostgreSQL
- H2 para testes automatizados
- Docker e Docker Compose
Subindo o banco:
docker compose -f docker-compose.dev.yml up -dSe a porta 5432 ja estiver ocupada no host:
POSTGRES_PORT=5434 docker compose -f docker-compose.dev.yml up -dRodando a aplicacao:
./mvn-dev-docker spring-boot:runEsse comando publica a porta da API no host. Por padrao:
- container:
8082 - host:
8082
Se estiver rodando o Postgres do projeto em outra porta, a aplicacao precisa apontar para o host:
DB_URL=jdbc:postgresql://host.docker.internal:5434/materimperium ./mvn-dev-docker spring-boot:runPor padrao a API sobe na porta 8082.
Se quiser mudar a porta publicada no host:
APP_PORT=8083 ./mvn-dev-docker spring-boot:runRodando a suite de testes:
./mvn-dev-docker testSubindo a stack completa:
docker compose -f docker-compose.prod.yml up --build -dSe quiser mudar as portas publicadas no host:
POSTGRES_PORT=5434 APP_PORT=8083 docker compose -f docker-compose.prod.yml up --build -dVariaveis mais relevantes:
DB_URL: URL JDBC do PostgreSQL. Default:jdbc:postgresql://localhost:5432/materimperiumDB_USERNAME: usuario do banco. Default:materimperiumDB_PASSWORD: senha do banco. Default:materimperiumSERVER_PORT: porta interna da aplicacao. Default:8082APP_PORT: porta publicada no host ao usarmvn-dev-dockeroudocker-compose.prod.yml. Default:8082APP_TEMP_DIR: diretorio temporario para armazenar uploads antes do processamento assincrono. Default:/tmp/materimperium
Header opcional:
X-Correlation-Id: identificador de rastreamento por request. Se nao for enviado, a API gera um valor automaticamente e devolve no response header.
token-envio-> roleENVIOtoken-consulta-> roleCONSULTAtoken-full-> rolesENVIOeCONSULTA
O projeto foi mantido propositalmente simples para priorizar clareza e aderencia ao escopo do teste.
controller: exposicao dos endpoints RESTservice: validacao, orquestracao de upload, processamento e consultarepository: persistencia com Spring Data JPAdomain: entidades e enum de statussecurity: autenticacao Bearer simples com tokens fixos em memoriaexception: padronizacao das respostas de erro
Fluxo resumido:
- o upload recebe um
multipart/form-data - as duas primeiras linhas do arquivo sao validadas
- o arquivo e salvo temporariamente
- um registro de processamento e criado com status
EM_PROCESSAMENTO - o processamento e executado em background
- o resumo por codigo de registro e persistido no banco
- o status final fica disponivel para consulta
Arquivos disponiveis:
- collection: postman/Test-RafyWP.postman_collection.json
- environment: postman/Test-RafyWP-Local.postman_environment.json
Como usar:
- Importe a collection e o environment no Postman.
- Selecione o environment
Test-RafyWP Local. - Ajuste
baseUrlse a API nao estiver emhttp://localhost:8082. - No request
Upload valido, escolha um arquivo manualmente no campofile. - Execute o upload. O
processingIdsera salvo automaticamente para uso nos requests seguintes.
Documentacao interativa:
- Swagger UI:
http://localhost:8082/swagger-ui/index.html - OpenAPI JSON:
http://localhost:8082/v3/api-docs
Os endpoints de documentacao ficam liberados sem autenticacao para facilitar a avaliacao manual da API.
curl -X POST http://localhost:8082/api/uploads \
-H "Authorization: Bearer token-envio" \
-F "file=@/caminho/arquivo.txt"Resposta esperada:
202 Accepted- body:
{
"id": "uuid-do-processamento"
}curl http://localhost:8082/api/uploads/{id}/status \
-H "Authorization: Bearer token-consulta"Resposta esperada:
200 OK- body:
{
"id": "uuid-do-processamento",
"status": "EM_PROCESSAMENTO"
}curl http://localhost:8082/api/uploads/{id}/resultado \
-H "Authorization: Bearer token-consulta"Se finalizado com sucesso:
200 OK
{
"status": "FINALIZADO_COM_SUCESSO",
"resumo": [
{ "registro": "0000", "total": 1 },
{ "registro": "0001", "total": 1 },
{ "registro": "C170", "total": 2 }
]
}Se ainda estiver processando:
409 Conflict
{
"id": null,
"status": "EM_PROCESSAMENTO"
}Regras de validacao inicial:
- a primeira linha deve comecar com
|0000|017|ou|0000|006| - a segunda linha deve ser exatamente
|0001|0|
Exemplo valido:
|0000|017|...
|0001|0|
|C100|...
|C170|...
POST /api/uploads: requer token com roleENVIOGET /api/uploads/{id}/status: requer token com roleENVIOouCONSULTAGET /api/uploads/{id}/resultado: requer token com roleCONSULTA
400 Bad Request: arquivo vazio, arquivo ausente, cabecalho invalido ouidmal formatado401 Unauthorized: token ausente ou invalido403 Forbidden: token sem role suficiente para a rota404 Not Found: upload inexistente500 Internal Server Error: falha ao armazenar o arquivo temporario ou erro interno inesperado
-
autenticacao simplificada por token fixo O enunciado nao exige armazenamento seguro real de token, apenas validacao de roles.
-
processamento assincrono dentro da aplicacao Foi escolhida uma abordagem sem fila externa para manter o escopo objetivo e demonstrar o fluxo de background sem dependencias adicionais.
-
leitura em streaming O arquivo e lido linha a linha com
BufferedReader, evitando carregar todo o conteudo em memoria. -
persistencia do historico por upload Cada envio gera um novo
ArquivoProcessamento, preservando historico e permitindo multiplos resumos independentes. -
arquivo temporario em disco O arquivo recebido e copiado para um diretorio temporario antes do processamento assincrono. Isso desacopla o ciclo de vida da requisicao HTTP do processamento em background.
-
memoria A solucao privilegia baixo consumo de memoria, porque o processamento e streaming.
-
disco O uso de arquivo temporario nao e o minimo absoluto de disco, mas foi uma escolha pragmatica para garantir confiabilidade no processamento assincrono.
-
complexidade Nao foram adicionados componentes como mensageria, cache ou object storage para evitar overengineering no contexto do teste.
- fluxo manual de upload, status e resultado validado localmente
- testes automatizados para fluxo feliz
- testes automatizados para autenticacao e autorizacao
- testes automatizados para validacao de cabecalho
- testes automatizados para erros operacionais
- testes de contrato para validar o formato exato das respostas JSON
Resumo atual da suite:
23 testes0 failures0 errors
Arquivos principais para avaliacao rapida:
src/main/java/com/materimperium/backendtest/controller/UploadController.javasrc/main/java/com/materimperium/backendtest/service/UploadService.javasrc/main/java/com/materimperium/backendtest/service/ProcessamentoAsyncService.javasrc/main/java/com/materimperium/backendtest/config/SecurityConfig.javasrc/main/java/com/materimperium/backendtest/exception/GlobalExceptionHandler.javasrc/test/java/com/materimperium/backendtest/UploadApiContractTest.java
Principais entidades:
-
ArquivoProcessamentoArmazena identificador, nome do arquivo, status, mensagem de erro e timestamps do ciclo de processamento. -
ResumoRegistroArmazena o total de ocorrencias por codigo de registro para cada upload processado.
- O upload valida as duas primeiras linhas antes de aceitar o arquivo.
- O arquivo e copiado para diretorio temporario e processado em background.
- A leitura e feita em streaming, linha a linha, evitando carregar arquivos grandes em memoria.
- O historico de cada upload e mantido em banco relacional.
- A API propaga
correlationIdpor request e registrauploadIdnos logs de processamento e consulta.
Se a aplicacao precisasse evoluir para um ambiente produtivo com volume maior, os proximos passos mais naturais seriam:
- mover o arquivo temporario para storage externo, como S3
- desacoplar o processamento usando fila, como SQS ou Kafka
- adicionar observabilidade com correlation id, metricas e logs estruturados
- usar autenticacao real com JWT ou provedor de identidade
- adicionar migrations versionadas de banco
- reforcar health checks e readiness checks