# StockMaster.API API REST em **.NET 8** para gestão de operações comerciais: controle de estoque, vendas, compras, fornecedores, clientes e trilha de auditoria por entidade. Construída em **Arquitetura Onion**, com segurança de nível de produção e uma demonstração pública ao vivo. [![CI](https://github.com/eduardocvalente/StockMaster.API/actions/workflows/ci.yml/badge.svg)](https://github.com/eduardocvalente/StockMaster.API/actions/workflows/ci.yml) [![.NET](https://img.shields.io/badge/.NET-8.0-512BD4)](https://dotnet.microsoft.com/) [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE) --- ## Demonstração **Swagger / OpenAPI ao vivo:** A API está publicada no [Fly.io](https://fly.io) (região São Paulo) com PostgreSQL gerenciado no [Neon](https://neon.tech). Para testar: 1. Abra o Swagger no link acima. 2. Execute `POST /api/v1/Auth/login` com o usuário de demonstração: ```json { "email": "edu@hotmail.com", "password": "StockMaster@2026" } ``` 3. Copie o `accessToken` da resposta, clique em **Authorize** (cadeado) e cole. Os demais endpoints exigem esse token. > A instância usa suspensão automática para economia — a primeira requisição após inatividade pode levar ~2s enquanto o banco "acorda". É um sandbox com dados fictícios: qualquer pessoa pode criar e apagar registros. --- ## Objetivo Sistema de gestão de estoque (inventory management) que modela o ciclo completo de uma operação comercial — do cadastro de produtos à venda com baixa automática de estoque —, servindo como referência de **API corporativa** com autenticação, autorização, auditoria e observabilidade prontas para produção. ## Funcionalidades - **Autenticação JWT** com senha protegida por PBKDF2-HMAC-SHA512 - **Autorização RBAC** — papéis, claims e policies; `FallbackPolicy` protege todo endpoint por padrão - **Controle de estoque** com baixa automática na venda e bloqueio de saldo negativo - **Cálculo de totais** de venda a partir dos itens (o cliente não define o total) - **Trilha de auditoria** automática de criação, alteração e exclusão, capturando valores antigo/novo, usuário, IP, sessão e correlação - **Eventos de autenticação** — login, logout e falhas registrados para análise forense - Cadastro de empresas, usuários, grupos, produtos, categorias, fornecedores, clientes, contatos e endereços - Pedidos de compra e vendas - **Rate limiting**, health checks (`liveness`/`readiness`) e logs estruturados em JSON ## Tecnologias | Camada | Stack | |---|---| | Runtime | .NET 8, C# 12 | | Persistência | Entity Framework Core 9, PostgreSQL (Npgsql) | | Segurança | JWT Bearer (HS256), ASP.NET Core Identity `PasswordHasher` | | Observabilidade | Serilog (JSON estruturado), health checks | | Testes | xUnit, FluentAssertions, EF Core InMemory, NetArchTest | | Contêiner | Docker (multi-stage, Alpine, non-root) | | IaC | Terraform (arquitetura-alvo AWS) | | CI/CD | GitHub Actions (build, testes, CodeQL, Trivy) | | Deploy da demo | Fly.io + Neon | ## Arquitetura **Onion Architecture** com fronteiras verificadas pelo compilador — o projeto de domínio não referencia nenhum outro: ```mermaid flowchart RL API["StockMaster.API
controllers, DTOs, middleware, filtros"] INF["StockMaster.Infrastructure
EF Core, repositórios, JWT, auditoria"] DOM["StockMaster.Domain
entidades, portas, validação, Result"] API --> INF INF --> DOM API -.-> DOM style DOM fill:#ddffdd style INF fill:#ddeeff style API fill:#ffe8cc ``` | Projeto | Referencia | Responsabilidade | |---|---|---| | `StockMaster.Domain` | **nada** | Entidades, regras de negócio, portas (interfaces), Notification e Result patterns | | `StockMaster.Infrastructure` | Domain | Adaptadores: EF Core, PostgreSQL, hash, JWT, interceptor de auditoria | | `StockMaster.API` | Infrastructure | Entrega HTTP: controllers, DTOs, middleware, composição | A regra de dependência é garantida por **testes de arquitetura** (NetArchTest) que falham o build se o domínio importar infraestrutura. Decisões arquiteturais detalhadas em [docs/ARQUITETURA.md](docs/ARQUITETURA.md) e [docs/SEGURANCA.md](docs/SEGURANCA.md). ## Modelo de dados **33 tabelas** com UUID como chave primária, integridade referencial em `Restrict` (excluir um operador não apaga o histórico) e exclusão lógica via `deleted_at` com filtro global de consulta. O diagrama abaixo mostra as entidades de negócio e seus relacionamentos principais (renderizado nativamente pelo GitHub): ```mermaid erDiagram companies ||--o{ products : cadastra companies ||--o{ categories : classifica companies ||--o{ stocks : possui companies ||--o{ sales : registra companies ||--o{ purchase_orders : emite categories ||--o{ products : agrupa suppliers ||--o{ products : fornece suppliers ||--o{ purchase_orders : atende customers ||--o{ sales : realiza products ||--o{ stocks : "controla saldo" products ||--o{ movements : movimenta products ||--o{ sale_items : compõe products ||--o{ purchase_order_items : compõe sales ||--o{ sale_items : contém purchase_orders ||--o{ purchase_order_items : contém users }o--o{ groups : "via user_groups" companies }o--o{ groups : "via company_groups" companies { uuid id PK string cnpj string name } products { uuid id PK string sku string name decimal price decimal unit_cost uuid category_id FK uuid supplier_id FK } stocks { uuid id PK decimal quantity string location uuid product_id FK } movements { uuid id PK string type decimal quantity uuid product_id FK } sales { uuid id PK decimal total_amount timestamptz sale_date uuid customer_id FK } sale_items { uuid sale_item_id PK decimal quantity decimal unit_price uuid sale_id FK uuid product_id FK } ``` **Detalhes que o diagrama omite para legibilidade:** - Toda tabela de negócio carrega `operator_user_id` (FK para `users`) — o operador responsável pela ação, base da rastreabilidade. - Cada entidade crítica tem uma tabela-espelho de auditoria (`products_audit`, `sales_audit`, `stocks_audit`, …) — 15 no total. - Duas tabelas transversais registram a trilha completa: `audit_log` (criação/alteração/exclusão de qualquer entidade, com valores antigo/novo em `jsonb`) e `authentication_events` (login, logout e falhas). ## Estrutura de pastas ``` StockMaster.API ├── src │ ├── StockMaster.Domain # Núcleo (entidades, portas, regras) │ ├── StockMaster.Infrastructure # Adaptadores (EF Core, segurança, auditoria) │ │ └── Migrations # Migrations do EF Core │ └── StockMaster.API # Camada de entrega (controllers, middleware) ├── test │ └── StockMaster.API.UnitTest # Testes unitários e de arquitetura ├── terraform # IaC — arquitetura-alvo de produção (AWS) ├── docs # Arquitetura, segurança, runbook, deploy ├── .github/workflows # CI e CD ├── Dockerfile # Build multi-stage, non-root ├── docker-compose.yml # Stack local (API + PostgreSQL) └── fly.toml # Configuração do deploy (Fly.io) ``` ## Pré-requisitos - [.NET SDK 8.0+](https://dotnet.microsoft.com/download) - [PostgreSQL 14+](https://www.postgresql.org/) — ou Docker - (Opcional) [Docker](https://www.docker.com/) para rodar via contêiner ## Como executar localmente ### Com Docker (recomendado) ```bash cp .env.example .env ``` Gere a chave JWT (mínimo 32 bytes) e coloque no `.env`: ```bash openssl rand -base64 48 ``` Suba a stack (API + PostgreSQL): ```bash docker compose up --build ``` A API sobe em `http://localhost:8080` e o Swagger em `/swagger`. ### Sem Docker ```bash dotnet restore dotnet build ``` Configure as variáveis (ver seção abaixo), aplique as migrations e rode: ```bash dotnet ef database update -p src/StockMaster.Infrastructure -s src/StockMaster.API dotnet run --project src/StockMaster.API ``` ## Configuração das variáveis de ambiente Todos os segredos vêm de variáveis de ambiente — **nada sensível fica no código**. A aplicação **não inicia** sem uma `JWT_SECRET_KEY` de 32+ bytes (validação no startup). | Variável | Obrigatória | Padrão | Descrição | |---|:---:|---|---| | `JWT_SECRET_KEY` | **Sim** | — | Chave de assinatura HS256, mínimo 32 bytes | | `POSTGRESS_HOST` | **Sim** | — | Host do PostgreSQL | | `POSTGRESS_DATABASE` | **Sim** | — | Nome do banco | | `POSTGRESS_USERNAME` | **Sim** | — | Usuário | | `POSTGRESS_PASSWORD` | **Sim** | — | Senha | | `POSTGRESS_PORT` | Não | `5432` | Porta | | `POSTGRESS_SSLMODE` | Não | `Prefer` | `Require` para banco gerenciado | | `APP_TIMEZONE` | Não | `America/Sao_Paulo` | Fuso de negócio (id IANA) | | `JWT_ISSUER` | Não | `StockMaster.API` | Emissor do token | | `JWT_AUDIENCE` | Não | `StockMaster.Clients` | Audiência do token | | `JWT_EXPIRATION_MINUTES` | Não | `60` | Validade do token | | `ENABLE_SWAGGER` | Não | `false` | Expõe o Swagger fora de Development | | `TRUST_PROXY_HEADERS` | Não | `false` | Trata `X-Forwarded-*` atrás de proxy | Veja `.env.example` para um modelo completo. ## Como executar os testes ```bash dotnet test ``` **209 testes** cobrindo entidades de domínio, regras de estoque, hashing de senha, fuso horário (inclusive horário de verão histórico), a trilha de auditoria e a regra de dependência da arquitetura. ## Como realizar o deploy Dois destinos, propósitos diferentes: | | `terraform/` | `fly.toml` | |---|---|---| | Papel | Arquitetura-alvo de produção | Demonstração pública | | Componentes | ECS Fargate, RDS Multi-AZ, ALB, WAF | 1 máquina + Postgres gerenciado | | Estado | Validado, não aplicado | **Ativo** | O passo a passo completo do deploy no Fly.io está em [docs/DEPLOY-FLY.md](docs/DEPLOY-FLY.md). O pipeline de CI/CD (GitHub Actions) roda build, testes, análise estática (CodeQL) e scan de imagem (Trivy) a cada push. ## Boas práticas adotadas - **Secure by default** — `FallbackPolicy` exige autenticação em todo endpoint salvo `[AllowAnonymous]` explícito - **Segredos fora do código** — só variáveis de ambiente, validadas no startup - **Senhas nunca em texto puro** — PBKDF2-HMAC-SHA512, e o hash nunca aparece em respostas ou na auditoria - **Trilha de auditoria imutável** — via interceptor do EF Core, impossível de esquecer; o operador vem do token, não do corpo da requisição - **Fronteiras de arquitetura verificadas pelo compilador** e por testes - **Concorrência otimista, soft delete com filtro global, integridade referencial em `Restrict`** - **`TimeProvider` injetável** — nenhuma leitura direta de relógio, tornando regras temporais testáveis - **Observabilidade** — logs estruturados JSON com correlação, health checks separados de liveness/readiness - **Contêiner endurecido** — multi-stage, imagem Alpine, usuário não-root, health check ## Licença Distribuído sob a licença MIT. Veja [LICENSE](LICENSE). ## Possíveis melhorias futuras - **Isolamento multiempresa por linha** — a claim `company_id` já é emitida; falta o query filter global que a aplica - **Refresh token com rotação** e denylist de `jti` para revogação imediata - **Camada de aplicação (Use Cases)** — hoje a regra de negócio vive nos serviços de domínio; extrair casos de uso reduziria o acoplamento - **Testes de integração** cobrindo o pipeline HTTP → repositório → banco - **Bloqueio progressivo por conta** contra força bruta (os dados já estão em `authentication_events`) - **RS256** (assinatura assimétrica) no lugar de HS256 para cenários multi-serviço --- Autor: **Eduardo Costa Valente** · Projeto de portfólio demonstrando engenharia de software, segurança e DevOps em .NET.