API versionada em /api/v1 para gestão de clientes e propostas, com idempotência, optimistic lock, auditoria e exclusão lógica.
Stack: Slim 4, PHP-DI, Eloquent (Illuminate Database), SQLite, PHPUnit.
- PHP 8.2+
- Composer
- Extensões PHP:
pdo_sqlite,json,mbstring
cd TestePratico
composer install
copy .env.example .env # Windows
# cp .env.example .env # Linux/macOS
php database/migrate.php
php database/seed.phpphp -S localhost:8080 -t publicBase URL: http://localhost:8080/api/v1
vendor/bin/phpunitCobertura obrigatória:
- Transição válida e inválida de status
- Idempotência (
Idempotency-Key) - Conflito de versão (optimistic lock)
- Busca com filtros e paginação
{
"error": {
"code": "VALIDATION_ERROR",
"message": "Dados inválidos",
"details": [{ "field": "documento", "message": "CPF inválido" }]
}
}Códigos: VALIDATION_ERROR (422), NOT_FOUND (404), CONFLICT (409), VERSION_CONFLICT (409), IDEMPOTENCY_CONFLICT (409), UNPROCESSABLE (422).
| Header | Uso |
|---|---|
Content-Type: application/json |
Corpo JSON |
Idempotency-Key |
Obrigatório em POST /propostas e POST /propostas/{id}/submit |
X-Actor |
Identificador do ator (ex: user:123). Default: system |
POST /api/v1/clientes
curl -X POST http://localhost:8080/api/v1/clientes \
-H "Content-Type: application/json" \
-d "{\"nome\":\"João Silva\",\"email\":\"joao@example.com\",\"documento\":\"529.982.247-25\"}"GET /api/v1/clientes/{id}
curl http://localhost:8080/api/v1/clientes/1POST /api/v1/propostas (requer Idempotency-Key)
curl -X POST http://localhost:8080/api/v1/propostas \
-H "Content-Type: application/json" \
-H "Idempotency-Key: 550e8400-e29b-41d4-a716-446655440000" \
-H "X-Actor: user:1" \
-d "{\"cliente_id\":1,\"produto\":\"Plano Pro\",\"valor_mensal\":199.90,\"origem\":\"API\"}"PATCH /api/v1/propostas/{id} (somente status DRAFT, exige versao)
curl -X PATCH http://localhost:8080/api/v1/propostas/1 \
-H "Content-Type: application/json" \
-d "{\"versao\":1,\"produto\":\"Plano Enterprise\",\"valor_mensal\":299.90}"POST /api/v1/propostas/{id}/submit (requer Idempotency-Key)
curl -X POST http://localhost:8080/api/v1/propostas/1/submit \
-H "Idempotency-Key: 660e8400-e29b-41d4-a716-446655440001"POST /api/v1/propostas/{id}/approve
curl -X POST http://localhost:8080/api/v1/propostas/1/approve \
-H "Content-Type: application/json" \
-d "{\"actor\":\"user:admin\"}"POST /api/v1/propostas/{id}/reject
curl -X POST http://localhost:8080/api/v1/propostas/1/rejectPOST /api/v1/propostas/{id}/cancel (soft delete + auditoria)
curl -X POST http://localhost:8080/api/v1/propostas/1/cancelGET /api/v1/propostas/{id}
curl http://localhost:8080/api/v1/propostas/1GET /api/v1/propostas (filtros e paginação obrigatórios)
Query params: page, per_page, status, cliente_id, origem, produto, valor_min, valor_max, created_from, created_to, sort, order
curl "http://localhost:8080/api/v1/propostas?status=DRAFT&per_page=10&page=1&sort=created_at&order=desc"GET /api/v1/propostas/{id}/auditoria
curl "http://localhost:8080/api/v1/propostas/1/auditoria?page=1&per_page=15"DRAFT → submit → SUBMITTED → approve → APPROVED
→ reject → REJECTED
DRAFT/SUBMITTED → cancel → CANCELED (+ exclusão lógica)
Estados finais (APPROVED, REJECTED, CANCELED) são imutáveis.
POST /propostas e POST /propostas/{id}/submit exigem header Idempotency-Key. Requisições repetidas com a mesma chave e mesmo payload retornam a resposta original sem duplicar registros.
Campo versao incrementado a cada mutação. Envie a versão atual no PATCH ou no body das transições. Conflito retorna 409 VERSION_CONFLICT.
Eventos: CREATED, UPDATED_FIELDS, STATUS_CHANGED, DELETED_LOGICAL. Registrados automaticamente em mudanças de status ou campos sensíveis (produto, valor_mensal, cliente_id).
src/
Application/Services/ # Regras de negócio
Domain/ # Enums, máquina de estados, validadores
Http/Actions/ # Controllers
Http/Middleware/ # Erros, JSON, idempotência
Infrastructure/ # Models e repositories
database/migrations/ # Schema SQLite
tests/Feature/ # Testes de integração
tests/Unit/ # Testes unitários
- Slim 4 — framework leve, ideal para API REST sem overhead de MVC completo.
- Eloquent standalone — migrations, relations e eager loading sem instalar Laravel inteiro.
- SQLite — zero configuração para execução local e testes em memória.
- Idempotência via middleware — centraliza lógica e persiste resposta para replay seguro.
- PropostaStatusMachine — transições de status em único ponto, facilitando testes e manutenção.