Solução em C# / .NET 10 para registro de lançamentos (débitos e créditos) e consolidado diário, com arquitetura de microsserviços orientada a eventos, alta disponibilidade no caminho de escrita e metas de performance documentadas para leitura de relatórios.
Um comerciante precisa:
- Registrar lançamentos diários (débito/crédito) de forma confiável.
- Consultar o saldo consolidado do dia, com gráficos e exportação.
flowchart LR
User[Usuario] --> Web[CashFlow.Web]
Web --> Auth[Auth API]
Web --> Tx[Transactions API]
Web --> Rpt[Reporting API]
Tx --> ES[(EventStoreDB)]
ES --> Relay[Transactions Relay]
Relay --> SNS[SNS]
SNS --> SQS[SQS]
SQS --> Worker[Reporting Worker]
Worker --> SQL[(reporting-db)]
Worker --> Redis[(Redis cache)]
Rpt --> SQL
Rpt --> Redis
Isolamento crítico (NFR): o serviço de lançamentos não depende do consolidado. A gravação confirma após append no EventStore; a projeção para relatórios é assíncrona (SNS → SQS → Worker).
Diagramas C4 detalhados: docs/c4/.
| Ferramenta | Versão mínima | Observação |
|---|---|---|
| .NET SDK | 10.0 | dotnet --version deve retornar 10.x |
| Docker Desktop | Engine em execução | WSL2 recomendado no Windows |
| PowerShell | 5.1+ (Windows) ou 7+ (Linux/macOS) | Scripts em scripts/*.ps1 |
| Recurso | Mínimo sugerido |
|---|---|
| RAM | 8 GB livres (stack sobe SQL Server, EventStore, LocalStack, 3 relays, 3 workers) |
| CPU | 4 cores |
| Disco | ~5 GB para imagens Docker |
| Ferramenta | Quando ajuda |
|---|---|
| AWS CLI v2 | Setup do Cognito Local mais rápido; sem CLI o script usa imagem amazon/aws-cli via Docker |
| Aspire Dashboard | Vem com o AppHost — confirme portas e saúde dos serviços |
Não é necessário criar .env, user-secrets ou editar appsettings para dev local. O fluxo padrão:
dotnet restore Aspire.CashFlow.slnx # primeira vez — baixa pacotes NuGet
.\scripts\run-full-local.ps1Arquivos gerados em runtime (não versionados): infra/**/generated/ — Cognito pool/client, filas SNS/SQS, etc.
Use esta tabela quando algo falhar ou precisar de ajuste fino.
| O quê | Onde configurar | Valor / padrão local |
|---|---|---|
| Orquestração Aspire | src/Aspire.CashFlow.AppHost/appsettings.Development.json |
Connection strings, Redis, réplicas, OTEL |
| Réplicas (API / relay / worker) | Env: CASHFLOW_API_REPLICAS, CASHFLOW_RELAY_REPLICAS, CASHFLOW_REPORTING_WORKER_REPLICAS ou CashFlow:*Replicas no AppHost |
1 / 3 / 3 |
| SQL Server (reporting-db) | ConnectionStrings:reporting-db no AppHost; senha em infra/transactions-stack/docker-compose.yml |
127.0.0.1:1433, sa / CashFlow@Dev123! |
| EventStoreDB | EventStore:ConnectionString no AppHost |
esdb://127.0.0.1:2113?tls=false |
| Redis (cache de relatórios) | Reporting:Redis no AppHost |
localhost:6379, Enabled: true |
| LocalStack (SNS/SQS/Secrets/KMS) | infra/localstack/docker-compose.yml + scripts setup-*.ps1 |
http://localhost:4566 |
| Cognito Local | Gerado em infra/cognito-local/generated/cognito.env pelo setup-cognito.ps1 |
http://localhost:9229 |
| Conta demo (login Web / load tests) | DemoAccount no AppHost; usuário criado pelo setup Cognito |
admin@cashflow.docker / Pass@word1 / MFA 123456 |
| JWT dev (sem Cognito) | Jwt:SigningKey em appsettings.Development.json de cada API |
Apenas dev — não usar em produção |
| Rate limiting | Security:RateLimitingEnabled — AppHost força false em reporting/transactions |
Load tests exigem desligado |
| Observabilidade (Prometheus/Grafana) | infra/observability/ + flag -ObservabilityHttps no run-full-local.ps1 |
Prometheus :9090, Grafana :3000 |
| URLs das APIs (load tests) | Env: CASHFLOW_AUTH_URL, CASHFLOW_TRANSACTIONS_URL, CASHFLOW_REPORTING_URL |
Ver tabela abaixo |
| Portas Docker (conflitos) | infra/*/docker-compose.yml — altere o mapeamento 127.0.0.1:PORTA e alinhe connection strings |
Ver tabela de portas |
| Porta | Serviço |
|---|---|
| 1433 | SQL Server |
| 2113 | EventStore HTTP |
| 6379 | Redis |
| 4566 | LocalStack |
| 9229 | Cognito Local |
| 4318 / 8889 | OTEL Collector |
| 9090 | Prometheus |
| 3000 | Grafana |
| 5154 / 7204 | Auth API (HTTP / HTTPS) |
| 5100 / 7093 | Transactions API (HTTP / HTTPS) |
| 5292 / 7090 | Reporting API (HTTP / HTTPS) |
| 7262 | Web (UI) |
Conflito de porta: o caso mais comum é 1433 já ocupada por outro SQL Server. Pare o serviço conflitante ou altere o mapeamento em
infra/transactions-stack/docker-compose.ymle a connection string no AppHost.
Portas Aspire: as APIs usam portas fixas via
launchSettings.json. Se o Dashboard mostrar outra porta, ajuste as variáveisCASHFLOW_*_URLnos scripts de carga.
Na raiz do repositório:
.\scripts\run-full-local.ps1Opções úteis:
# Prometheus/Grafana com scrape HTTPS (porta 7093)
.\scripts\run-full-local.ps1 -ObservabilityHttps
# Sem stack de observabilidade
.\scripts\run-full-local.ps1 -SkipObservabilityParar tudo:
.\scripts\stop-full-local.ps1| Serviço | URL típica |
|---|---|
| Web (UI) | https://localhost:7262 |
| Auth API | https://localhost:7204 |
| Transactions API | https://localhost:7093 |
| Reporting API | https://localhost:7090 |
| Aspire Dashboard | http://localhost:15888 (porta pode variar — ver terminal) |
| Prometheus | http://localhost:9090 |
| Grafana | http://localhost:3000 (admin / admin) |
As portas exatas aparecem no Aspire Dashboard após o AppHost subir.
| Campo | Valor |
|---|---|
admin@cashflow.docker |
|
| Senha | Pass@word1 |
| MFA (local) | 123456 |
- Acesse a Web e faça login.
- Registre um crédito e um débito na tela de fluxo de caixa.
- Abra Relatórios e selecione a data dos lançamentos.
- (Opcional) Exporte CSV/PDF e confira totais iguais ao dashboard.
dotnet test Aspire.CashFlow.slnxTestes de integração usam WebApplicationFactory e, quando disponível, Docker (LocalStack / SQL).
O teste ReportingAvailabilityIsolationTests prova que a Transactions API grava lançamentos sem serviços de reporting no pipeline HTTP.
Validação manual (stack rodando):
- Pare
reporting-apiereporting-workerno Aspire Dashboard. POST /api/transactionscom JWT — deve retornar 200.- Suba reporting novamente — backlog SQS deve ser projetado.
Com a stack local em execução (run-full-local.ps1 deve permanecer ativo — não pressione Ctrl+C antes):
# Em outro terminal (stack rodando no primeiro)
.\scripts\run-reporting-load-test.ps1Se acabou de subir a stack, aguarde endpoints:
.\scripts\run-reporting-load-test.ps1 -WaitTimeoutSeconds 120Ou diretamente (com stack já em execução — use --no-build para não recompilar e derrubar a reporting-api):
dotnet build tests/CashFlow.Reporting.Benchmarks -p:BuildProjectReferences=false
dotnet run --project tests/CashFlow.Reporting.Benchmarks --no-build -- load `
--url https://localhost:7090 `
--auth-url https://localhost:7204 `
--rate 50 `
--duration 30Metas em docs/reporting-slo.md e gates em ReportingLoadTestSloGates.cs: 50 RPS, ≤ 5% falhas, média < 200 ms (leituras com cache).
Com a stack ativa, em outro terminal:
.\scripts\run-transactions-load-test.ps1URLs padrão: Auth https://localhost:7204, Transactions https://localhost:7093. Sobrescreva se necessário:
$env:CASHFLOW_AUTH_URL = "https://localhost:7204"
$env:CASHFLOW_TRANSACTIONS_URL = "https://localhost:7093"
.\scripts\run-transactions-load-test.ps1Use
.\scripts\run-*-load-test.ps1oudotnet run --no-build— nuncadotnet runsem--no-buildcom a stack rodando (recompila a API e derruba o processo no Aspire).
.\scripts\lint.ps1 # CSharpier + analisadores (build)
.\scripts\lint.ps1 -Fix # analisadores + formatação CSharpier
.\scripts\security-audit.ps1 # vulnerabilidades em pacotes + SAST no códigoRelatórios de execução: tests/CashFlow.Reporting.Benchmarks/reports/.
CashFlow/
├── Aspire.CashFlow.slnx
├── src/
│ ├── Aspire.CashFlow.AppHost/ # Orquestração .NET Aspire
│ ├── Aspire.CashFlow.ServiceDefaults/ # Auth, observabilidade, segurança compartilhada
│ ├── CashFlow.Auth.Api/
│ ├── CashFlow.Transactions.Api/
│ ├── CashFlow.Transactions.Relay/
│ ├── CashFlow.Reporting.Api/
│ ├── CashFlow.Reporting.Worker/
│ └── CashFlow.Web/
├── tests/ # Unitários, integração, contrato, benchmarks
├── docs/ # ADRs, SLOs, C4, roadmap, constituição
├── specs/ # Especificações por feature (`spec.md`, contratos)
├── infra/ # Docker Compose (LocalStack, Cognito local, observabilidade)
└── scripts/ # run-full-local.ps1, lint.ps1, testes de carga
| Documento | Descrição |
|---|---|
| Índice de docs | Mapa da documentação (ADRs 000–003) |
| ADR 000 — Governança | Critérios e 3 categorias de ADR |
| ADR 001 — Arquitetura | Microsserviços, CQRS, NFR-01 |
| ADR 002 — Infraestrutura | EventStore, SNS/SQS, SQL, Redis |
| ADR 003 — Segurança | Cognito + JWT |
| SLO Transactions | Métricas do caminho de escrita |
| SLO Reporting | Métricas do consolidado |
| Observabilidade pipeline | EventStore → SQS |
| Roadmap | Evoluções futuras |
Repositório: github.com/adrdot/CashFlow
Pipeline GitHub Actions: .github/workflows/ci.yml — dotnet build + dotnet test em cada push/PR.
Resumo — detalhes em docs/roadmap.md:
- Deploy em Kubernetes com HPA para API/Worker e load balancer HTTP.
- Cognito Admin para gestão real de usuários.
- Federação AD/SAML/OIDC.
- Reavaliação de DynamoDB para idempotência de projeção em escala extrema (ADR 002, seção modelo de leitura).
- Secrets e JWT de produção via AWS Secrets Manager (sem chaves dev em
appsettings).
Não, para dev local padrão. Docker + .NET 10 + PowerShell bastam. O run-full-local.ps1 provisiona LocalStack, Cognito, filas, secrets e injeta variáveis de ambiente no AppHost. Arquivos em infra/**/generated/ são criados automaticamente.
- Confirme que o Docker Desktop está rodando.
- Verifique conflitos na tabela de portas (seção Onde configurar) — especialmente 1433 (SQL Server).
- Pare restos de execuções anteriores:
.\scripts\stop-full-local.ps1 - Se alterou portas no
docker-compose.yml, atualizeConnectionStrings:reporting-dbe demais endpoints no AppHost.
- Aguarde
Cognito Local readyno terminal dorun-full-local.ps1. - Pool e client IDs não vêm do
appsettingsestático — são gerados eminfra/cognito-local/generated/cognito.enve injetados pelo script. - Credenciais demo:
admin@cashflow.docker/Pass@word1/ MFA123456. - Se rodar o AppHost sem
run-full-local.ps1, definaCASHFLOW_COGNITO_ENABLED=truee carregue ocognito.env, ou useCASHFLOW_AUTO_LOAD_COGNITO_LOCAL=true.
- O
run-full-local.ps1deve continuar rodando no primeiro terminal (não pressione Ctrl+C). - Aguarde
Distributed application startedno Aspire Dashboard. - Use
-WaitTimeoutSeconds 120no script de reporting. - Confirme URLs no Dashboard; se diferentes, exporte
CASHFLOW_AUTH_URLeCASHFLOW_REPORTING_URL. - Compile benchmarks antes ou deixe o script fazer:
dotnet build tests/CashFlow.Reporting.Benchmarks -p:BuildProjectReferences=false.
Causa típica: dotnet run --project tests/CashFlow.Reporting.Benchmarks sem --no-build recompila CashFlow.Reporting.Api (referência do projeto) e encerra o processo em execução.
Solução: use .\scripts\run-reporting-load-test.ps1 ou dotnet run --no-build.
Rate limiting está ativo. Em dev o AppHost define Security__RateLimitingEnabled=false. Reinicie a stack via run-full-local.ps1 ou confira Security:RateLimitingEnabled em appsettings.Development.json da Reporting API.
- Prometheus: http://localhost:9090 → Status → Targets.
- Transactions HTTPS:
:7093/metrics; Reporting::7090/metrics. - Com
-ObservabilityHttps, use scrape HTTPS (cert dev ignorado no Prometheus). - Detalhes:
docs/transactions-slo.mdedocs/messaging-pipeline-observability.md.
O gate de reporting usa data fixa 2026-06-12 por padrão. Funciona com cache vazio (zero state). Para dados reais, registre lançamentos na Web e passe --report-date com a data usada.
Reduza réplicas antes de subir a stack:
$env:CASHFLOW_RELAY_REPLICAS = "1"
$env:CASHFLOW_REPORTING_WORKER_REPLICAS = "1"
.\scripts\run-full-local.ps1 -SkipObservabilityScripts são PowerShell — instale PowerShell 7+. Docker Desktop deve expor host.docker.internal (observabilidade). Caminhos usam \; execute a partir da raiz do repo com pwsh ./scripts/run-full-local.ps1.
Testes de integração SQL/Redis pulam automaticamente se Docker não estiver disponível. Para executá-los, suba a stack (run-full-local.ps1) ou apenas os containers necessários (SQL + Redis + LocalStack).
| Caminho | Conteúdo |
|---|---|
docs/reporting-slo.md |
50 RPS, 5% perda, latência cacheada |
docs/transactions-slo.md |
Caminho de escrita / persistência |
tests/CashFlow.Reporting.Benchmarks/ReportingLoadTestSloGates.cs |
Gates automatizados (reporting) |
tests/CashFlow.Transactions.Benchmarks/TransactionLoadTestSloGates.cs |
Gates automatizados (transactions) |
Projeto de demonstração arquitetural — ajuste conforme necessário antes de uso em produção.