Technical Challenge – Backend Developer
Backend desenvolvido para o desafio técnico da Estapar com o objetivo de simular o gerenciamento de um estacionamento, controlando entrada e saída de veículos, ocupação de vagas e cálculo de receita por setor.
O sistema recebe eventos de um simulador externo via webhook, processa regras de negócio relacionadas à ocupação e preços dinâmicos e expõe uma API REST para consulta de faturamento.
O objetivo do desafio é construir um backend capaz de:
- Gerenciar vagas de estacionamento
- Processar eventos de entrada, estacionamento e saída de veículos
- Controlar ocupação por setor
- Calcular faturamento baseado no tempo de permanência
- Aplicar regras de preço dinâmico conforme a lotação do setor
O sistema recebe eventos de um simulador de garagem e deve processá-los corretamente.
Eventos possíveis:
ENTRYPARKEDEXIT
- Java 21
- Spring Boot
- Spring Data JPA
- Hibernate
- MySQL
- JUnit 5
- Mockito
- Docker (simulador da garagem)
- Maven
- SLF4J / Logback
O projeto segue uma arquitetura em camadas típica de aplicações Spring Boot.
controller
↓
service
↓
repository
↓
database
Responsável por expor os endpoints REST da aplicação.
Exemplo:
/webhook/revenue
Camada onde ficam as regras de negócio:
- processamento de eventos do webhook
- controle de vagas ocupadas
- cálculo de preço
- validações
Responsável pela comunicação com o banco de dados através do Spring Data JPA.
Contém:
- entidades do sistema
- enums
- exceções de domínio
parkingSystem
├── application
│ ├── api
│ │ ├── controller
│ │ └── dto
│ └── service
│
├── domain
│ ├── entity
│ ├── enums
│ └── exception
│
├── infrastructure
│ └── repository
Essa separação ajuda a manter:
- responsabilidades claras
- baixo acoplamento
- melhor testabilidade
O sistema recebe eventos do simulador e os processa internamente aplicando regras de negócio antes de persistir os dados.
Garage Simulator
│
│ POST /webhook
▼
WebhookController
▼
WebhookService
▼
Event Handlers
(ENTRY / PARKED / EXIT)
▼
Business Rules
▼
Database
│
▼
Revenue API
(GET /revenue)
Ao iniciar a aplicação:
- O sistema consulta o simulador
- Busca configuração da garagem via:
GET /garage
Os dados recebidos incluem:
- setores
- preço base
- capacidade máxima
- vagas disponíveis
Essas informações são persistidas no banco.
O simulador envia eventos para:
POST /webhook
Indica que um veículo entrou na garagem.
{
"license_plate": "ABC1234",
"entry_time": "2025-01-01T12:00:00.000Z",
"event_type": "ENTRY"
}A aplicação:
- valida disponibilidade de vagas
- calcula o preço dinâmico baseado na ocupação
- registra o evento
Indica que o veículo estacionou em uma vaga específica.
{
"license_plate": "ABC1234",
"lat": -23.561684,
"lng": -46.655981,
"event_type": "PARKED"
}A aplicação:
- associa o veículo à vaga
- registra a ocupação do setor
Indica que o veículo saiu do estacionamento.
{
"license_plate": "ABC1234",
"exit_time": "2025-01-01T12:00:00.000Z",
"event_type": "EXIT"
}A aplicação:
- libera a vaga
- calcula o valor da estadia
- registra o pagamento
- Um veículo não pode ocupar mais de uma vaga
- Um setor não pode ultrapassar sua capacidade
- Se o setor atingir 100% de ocupação, novas entradas são bloqueadas até que uma vaga seja liberada
Regras implementadas:
-
Primeiros 30 minutos são gratuitos
-
Após 30 minutos:
- cobrança por hora
- utilizando
basePrice - arredondamento para cima
Exemplo:
Tempo estacionado: 1h10
Cobrança: 2 horas
O preço base sofre variação conforme a ocupação do setor no momento da entrada:
| Ocupação | Regra |
|---|---|
| < 25% | desconto de 10% |
| 25% – 50% | preço normal |
| 50% – 75% | aumento de 10% |
| 75% – 100% | aumento de 25% |
A regra é aplicada no momento da entrada do veículo.
Endpoint:
GET /revenue
Request:
{
"date": "2025-01-01",
"sector": "A"
}Response:
{
"amount": 120.00,
"currency": "BRL",
"timestamp": "2025-01-01T12:00:00.000Z"
}Esse endpoint retorna o faturamento total de um setor em uma data específica.
O sistema utiliza MySQL para persistência dos dados.
Principais entidades:
GarageParkingSpotParkingEvent
A aplicação utiliza MySQL e espera que as credenciais do banco sejam fornecidas
através das variáveis de ambiente MYSQL_USERNAME e MYSQL_PASSWORD.
Para subir rapidamente um banco de dados local usando Docker, execute:
docker run -d \
--name estapar_mysql \
-p 3306:3306 \
-e MYSQL_ROOT_PASSWORD=root \
-e MYSQL_DATABASE=estaparparkingsystem \
-e MYSQL_USER=estapar \
-e MYSQL_PASSWORD=estapar \
mysql:8Depois defina as variáveis de ambiente usadas pela aplicação:
export MYSQL_USERNAME=estapar export MYSQL_PASSWORD=estapar
Em Windows (PowerShell):
$env:MYSQL_USERNAME="estapar" $env:MYSQL_PASSWORD="estapar"
A aplicação se conectará ao banco utilizando a seguinte configuração:
spring.datasource.url=jdbc:mysql://localhost:3306/estaparparkingsystem
As tabelas são criadas automaticamente pelo Hibernate ao iniciar a aplicação (spring.jpa.hibernate.ddl-auto=update).
git clone https://github.com/LucasSSV-Dev/estapar-parking-system.gitdocker run -d --name estapar_simulator -p 3000:3000 --add-host=localhost:host-gateway cfontes0estapar/garage-sim:1.0.0mvn clean installDepois execute:
run EstaparParkingSystemApplication
A API iniciará em:
http://localhost:3003
Para rodar os testes unitários:
mvn testOs testes utilizam:
- JUnit 5
- Mockito
Os testes unitários foram focados principalmente na camada de serviço, onde estão concentradas as regras de negócio da aplicação.
A estratégia adotada foi isolar a lógica de negócio utilizando JUnit 5 e Mockito, permitindo validar comportamentos sem dependência direta do banco de dados ou da infraestrutura externa.
Foram testados cenários como:
- processamento de eventos de entrada de veículos
- associação de veículos às vagas
- cálculo de preço baseado no tempo de permanência
- validação de vagas ocupadas
- tratamento de eventos inválidos
A aplicação utiliza SLF4J com Logback para registrar:
- processamento de eventos
- erros de validação
- operações importantes do sistema
A aplicação utiliza exceções de domínio para representar erros relacionados às regras de negócio do estacionamento.
Exemplos de situações tratadas:
- tentativa de entrada quando o estacionamento está cheio
- inconsistência em eventos recebidos pelo webhook
- dados inválidos enviados na requisição
Essas exceções são tratadas por um ExceptionHandler global, garantindo respostas HTTP apropriadas e mensagens de erro claras para o consumidor da API.
Algumas melhorias possíveis:
- documentação com Swagger/OpenAPI
- testes de integração
- cache de consultas de receita
- containerização completa da aplicação
- métricas com Spring Actuator
Lucas de Souza Santos Viana
GitHub https://github.com/LucasSSV-Dev