Aplicação Full Stack para controle de empréstimos pessoais, construída como teste técnico.
O sistema permite:
- Cadastrar clientes
- Simular empréstimos com juros compostos
- Aprovar empréstimos com regras de negócio
- Gerar e persistir parcelas automaticamente
- Autenticar via Laravel Sanctum (token)
- Visualizar um dashboard com estatísticas e gráfico
- Bloquear novos empréstimos para clientes com parcelas em atraso
- Consultar um serviço externo de score de crédito (mock em rota Next.js)
Backend
- PHP 8.4+ (desenvolvido e testado em ambiente local com PHP 8.2.12, totalmente compatível com 8.4+)
- Laravel 11
- Laravel Sanctum (API + token)
- MySQL 8 (via Docker)
- Composer
Frontend
- Next.js 16 (App Router)
- TypeScript
- Tailwind CSS
- Recharts (gráfico do dashboard)
Infra
- Docker + docker-compose (somente para o banco de dados)
- MySQL rodando na porta 3307 (a 3306 já está ocupada por outra instância)
Estrutura de pastas
/backend → API Laravel
/frontend → Frontend Next.js (App Router)
/docker-compose.yml → banco MySQLVocê precisa ter instalado na sua máquina:
- Git
- Docker e Docker Compose
- PHP 8.2+
- Composer
- Node.js 20+ e npm (ou yarn/pnpm)
git clone https://github.com/seu-usuario/credfranco.git
cd credfranco(Substitua seu-usuario pelo usuário correto do GitHub.)
O projeto já vem com um docker-compose.yml configurado para o MySQL na porta 3307:
services:
db:
image: mysql:8.0
container_name: credfranco_db
restart: unless-stopped
environment:
MYSQL_DATABASE: credfranco
MYSQL_USER: creduser
MYSQL_PASSWORD: credpass
MYSQL_ROOT_PASSWORD: root
ports:
- "3307:3306"
volumes:
- db_data:/var/lib/mysql
volumes:
db_data:Para subir o banco:
docker-compose up -dVerifique se o container está rodando:
docker psBanco disponível em:
- Host:
127.0.0.1 - Porta:
3307 - Banco:
credfranco - Usuário:
creduser - Senha:
credpass
No diretório do projeto, entre na pasta backend:
cd backendcp .env.example .envEdite o .env para bater com o docker-compose:
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3307
DB_DATABASE=credfranco
DB_USERNAME=creduser
DB_PASSWORD=credpasscomposer installphp artisan key:generatephp artisan migrateIsso criará, entre outras, as tabelas:
usersclientsloansinstallmentspersonal_access_tokens
Caso prefira, você também pode criar o usuário manualmente via
php artisan tinker.
Utilize as credenciais definidas nos seeds (ou criadas manualmente). Exemplo:
- E-mail:
admin@credfranco.test - Senha:
secret123
php artisan servePor padrão, a API ficará disponível em:
http://127.0.0.1:8000- Endpoints da API em
http://127.0.0.1:8000/api/...
Em outro terminal, volte para a raiz do projeto e entre na pasta frontend:
cd ../frontendcp .env.example .env.localNEXT_PUBLIC_API_URL=http://127.0.0.1:8000/apinpm install
# ou
yarn
# ou
pnpm installnpm run dev
# ou
yarn devPor padrão, o frontend ficará disponível em:
http://localhost:3000
Deixe os dois servidores rodando ao mesmo tempo:
- Backend:
php artisan serve- Frontend:
npm run dev
- Acesse
http://localhost:3000. - Informe e-mail e senha do usuário criado (ex.:
admin@credfranco.test/secret123). - O frontend obtém um token da API e passa a enviar esse token no header
Authorization.
- Tela para registro dos dados básicos do cliente, que serão utilizados nas simulações e contratos de empréstimo.
- Campos principais: nome, CPF, data de nascimento, renda mensal, telefone.
- Funcionalidades: listagem paginada, busca por nome/CPF, máscaras de CPF e telefone.
- Formulário com: valor solicitado, taxa de juros mensal (%), quantidade de parcelas (6–36).
- O backend calcula:
- valor da parcela (juros compostos)
- valor total do empréstimo
- data da primeira parcela (D+30)
- lista de parcelas com data de vencimento e valor.
Na aprovação, o backend aplica as regras de negócio:
- Renda mensal ≥ 4× o valor da parcela
- CPF válido
- Consulta a serviço externo (mockado) de score (0–1000)
- Score mínimo para aprovação: 600
- Bloqueio caso o cliente possua parcelas em atraso (vencimento + 5 dias)
Se aprovado:
- O empréstimo é salvo em
loans - As parcelas são salvas em
installments - Status do empréstimo:
approved
- Total emprestado
- Ticket médio
- Quantidade de empréstimos ativos
- Gráfico com evolução dos valores emprestados (Recharts)
- Projeto dividido em dois módulos independentes (
/backende/frontend). - Comunicação via API REST em JSON, facilitando eventual troca de frontend (ex.: app mobile).
- API REST construída em Laravel 11, usando controllers organizados por contexto (clientes, empréstimos).
- Autenticação via Laravel Sanctum, utilizando tokens pessoais.
- Rotas protegidas exigem header
Authorization: Bearer {token}.
- Entidades principais:
Client: dados cadastrais do cliente.Loan: contrato de empréstimo, com valor principal, taxa, quantidade de parcelas, score externo, status, etc.Installment: parcelas geradas a partir do empréstimo, com número, valor, data de vencimento e status.
- Relacionamentos:
Client1:NLoanLoan1:NInstallment
- A simulação e a aprovação são separadas:
- Simulação não persiste dados, apenas calcula valores.
- Aprovação valida as regras e persiste empréstimo + parcelas dentro de uma
DB::transaction().
- As regras (renda mínima, CPF, parcelas em atraso, score) estão centralizadas no fluxo de aprovação para facilitar manutenção.
- Em vez de chamar diretamente um terceiro real, o frontend utiliza uma rota Next.js que simula um serviço externo de score.
- Essa rota retorna um score aleatório/determinístico de 0 a 1000.
- O backend consome esse score na aprovação, como se fosse um serviço externo real.
- Ao aprovar um empréstimo, é disparado o evento
LoanCreated. - Um listener (
LogLoanCreated) registra nolaravel.logum resumo do empréstimo criado. - Isso permite adicionar comportamentos assíncronos (ex.: disparar e-mail, enviar mensagem para fila) sem acoplar a lógica diretamente ao controller.
- Foram adicionados testes de Feature para garantir o comportamento das principais regras de negócio.
- Exemplos:
- Bloqueio de aprovação quando o cliente possui parcelas em atraso.
- Simulação de empréstimo para cliente existente.
- Execução dos testes:
cd backend
php artisan testAbaixo alguns exemplos em curl para facilitar a validação da API.
curl -X POST http://127.0.0.1:8000/api/login \
-H "Content-Type: application/json" \
-d '{
"email": "admin@credfranco.test",
"password": "secret123"
}'Resposta (exemplo):
{
"user": {
"id": 1,
"name": "Admin",
"email": "admin@credfranco.test"
},
"token": "1|abcdef..."
}Guarde o token para usar nas próximas requisições.
curl -X GET http://127.0.0.1:8000/api/clients \
-H "Accept: application/json" \
-H "Authorization: Bearer 1|abcdef..."curl -X POST http://127.0.0.1:8000/api/clients \
-H "Content-Type: application/json" \
-H "Authorization: Bearer 1|abcdef..." \
-d '{
"name": "João Silva",
"cpf": "12345678909",
"birth_date": "1990-01-01",
"monthly_income": 5000,
"phone": "11999999999"
}'Resposta (exemplo):
{
"id": 1,
"name": "João Silva",
"cpf": "12345678909",
"monthly_income": 5000,
"phone": "11999999999",
"created_at": "2025-12-05T14:13:41.000000Z"
}curl -X POST http://127.0.0.1:8000/api/loans/simulate \
-H "Content-Type: application/json" \
-H "Authorization: Bearer 1|abcdef..." \
-d '{
"client_id": 1,
"principal_amount": 5632.00,
"installments_count": 12,
"interest_rate_monthly": 2.5
}'Resposta (exemplo):
{
"principal_amount": 5632.0,
"interest_rate_monthly": 2.5,
"installments_count": 12,
"monthly_installment": 493.29,
"total_amount": 5919.48,
"first_due_date": "2025-01-05",
"installments": [
{
"number": 1,
"due_date": "2025-01-05",
"amount": 493.29
}
// ... demais parcelas
]
}curl -X POST http://127.0.0.1:8000/api/loans/approve \
-H "Content-Type: application/json" \
-H "Authorization: Bearer 1|abcdef..." \
-d '{
"client_id": 1,
"principal_amount": 5632.00,
"installments_count": 12,
"interest_rate_monthly": 2.5
}'Resposta de sucesso (exemplo):
{
"loan": {
"id": 35,
"client_id": 1,
"principal_amount": 5632.0,
"interest_rate_monthly": 2.5,
"installments_count": 12,
"monthly_installment": 493.29,
"total_amount": 5919.48,
"status": "approved",
"external_score": 750,
"approved_at": "2025-12-05T14:13:41.000000Z",
"installments": [
{
"number": 1,
"due_date": "2025-01-05",
"amount": 493.29,
"status": "pending"
}
// ... demais parcelas
]
},
"external_score": 750
}Renda insuficiente:
{
"error": "Renda insuficiente para aprovação",
"client_monthly_income": 2500,
"required_minimum": 3899.48
}Parcelas em atraso:
{
"error": "Cliente possui parcelas em atraso",
"late_installments": [
{
"id": 123,
"due_date": "2025-02-01",
"days_late": 14
}
]
}Score abaixo do mínimo:
{
"error": "Score abaixo do mínimo para aprovação",
"score": 524
}- Implementar autenticação com roles (admin/operador)
- Histórico de alterações nos empréstimos
- Exportação de dados (CSV/Excel) de clientes e contratos
- Integração real com serviço externo de score de crédito
- Envio de e-mails para clientes (aprovação, vencimento próximo, atraso)
- Jobs/fila para geração de parcelas e/ou consulta de score
Qualquer dúvida sobre configuração ou fluxo da aplicação, consulte este README ou abra uma issue no repositório.