Motivação do estudo
O LogisticSaaS nasceu pra estudar sistemas distribuídos de ponta a ponta usando uma stack poliglota: escolhendo, de propósito, a linguagem mais adequada pra cada serviço em vez de padronizar tudo numa só.
A meta é exercitar os blocos de construção de um SaaS escalável de varejo:
- Mensageria com filas
- Comunicação interna via gRPC
- Concorrência com Akka.NET
- Observabilidade (logs, métricas e dashboards)
- O padrão BFF entre os serviços e o frontend
O projeto foi conduzido com apoio do Antigravity + Gemini 3 Pro como exercício de orquestração assistida por IA.
O que foi feito
- Control Plane: frontend em Next.js 14 com dashboard de atores, inventário e um “Test Lab” pra simular regras de precificação.
- API Gateway / BFF: em NestJS, ponto único de entrada entre o frontend e os serviços — faz proxy REST pros serviços internos e expõe um endpoint SSE que repassa eventos do Kafka pro navegador em tempo real.
- Camada de ingestão: em Java 21 (Spring WebFlux), recebe telemetria via webhook e publica no
Kafka (
inventory-events). - Pricing Solver: em C++ 20 (Drogon), consome o Kafka e calcula preço dinâmico via REST
(
POST /calculate) a partir de uma fórmula de blend (peso × multiplicador + markup) configurável por regra. gRPC está desenhado como próximo passo pra substituir o REST entre Gateway e Solver. - Order Service: em .NET 8 com Akka.NET — um ator por reserva de estoque (TTL de 15min,
auto-expiração via
Scheduler), mais um worker que consome o Kafka, persiste pedidos no Supabase/PostgreSQL e republica no tópicoorder-events. - Catalog Service: em .NET 8 (Minimal APIs), CRUD de produtos e estoque direto no
Supabase/PostgreSQL, com trilha de auditoria (
stock_audit) a cada mudança de quantidade. - Mensageria e observabilidade: Apache Kafka (+ Zookeeper) entre os serviços; stack Loki + Promtail + Prometheus + cAdvisor + Grafana pra logs e métricas de todos os containers.
- Infra e orquestração:
docker-compose+Makefilepro ambiente local (11+ serviços), Helm chart inicial pro Gateway em Kubernetes.
Em andamento: próximas frentes de estudo incluem gRPC real entre Gateway e Pricing Solver, RBAC nos endpoints do Gateway (hoje desprotegidos para facilitar o dev local), CQRS/Event Sourcing, caching e o padrão SAGA para transações distribuídas.
Como funciona
Visão geral do sistema
flowchart TB
Browser["🌐 Navegador (usuário)"]
FE["frontend :3000 — Next.js 14\nDashboard · Atores · Test Lab"]
GW["gateway :3001 — NestJS BFF\nproxy REST · SSE /stream/kafka"]
subgraph Poliglota["Serviços de domínio"]
Ingestion["ingestion :3002\nJava 21 · Spring WebFlux"]
Solver["pricing-solver :3003\nC++ 20 · Drogon"]
Orders["order-service :3004\n.NET 8 · Akka.NET"]
Catalog["catalog-service :3005\n.NET 8 · Minimal API"]
end
Kafka[["Kafka\ninventory-events · order-events"]]
PG[("Supabase / PostgreSQL\npricing · orders · audit · catalog")]
subgraph Obs["Observabilidade"]
Promtail["promtail"]
Loki[("loki")]
Prometheus[("prometheus")]
Grafana["grafana :3006"]
cAdvisor["cadvisor"]
end
Browser --> FE
FE -->|REST| GW
FE -.->|SSE: eventos em tempo real| GW
GW -->|"proxy /telemetry"| Ingestion
GW -->|"REST: /pricing-rules, /calculate"| Solver
GW -->|"REST: /reservations"| Orders
GW -->|"REST: /api/products"| Catalog
GW -.->|consome p/ SSE| Kafka
Ingestion -->|publica| Kafka
Kafka -->|consome| Solver
Kafka -->|consome, filtra check_out/SALE| Orders
Orders -->|publica order-events| Kafka
Orders --> PG
Catalog --> PG
Solver -.->|regras de preço| PG
Ingestion --> Promtail
Solver --> Promtail
Orders --> Promtail
Catalog --> Promtail
GW --> Promtail
Promtail --> Loki
cAdvisor --> Prometheus
Loki --> Grafana
Prometheus --> Grafana
Fluxo orientado a eventos: da telemetria ao pedido
sequenceDiagram
participant Ext as Fonte externa (loja/PDV)
participant Ing as ingestion (Java)
participant K as Kafka
participant Solver as pricing-solver (C++)
participant Ord as order-service (.NET/Akka)
participant DB as Supabase
participant GW as gateway (SSE)
participant FE as frontend
Ext->>Ing: POST /telemetry (evento de estoque/venda)
Ing->>K: publica em inventory-events
K->>Solver: consome (recalcula sinais de preço)
K->>Ord: consome (worker Kafka)
alt evento é check_out ou SALE
Ord->>Ord: monta Order a partir do evento
Ord->>DB: INSERT orders
Ord->>K: publica ORDER_CREATED em order-events
end
K->>GW: consome inventory-events + order-events
GW-->>FE: SSE (Server-Sent Events) com o payload bruto
FE-->>FE: dashboard atualiza em tempo real
Reserva de estoque com atores (Akka.NET)
sequenceDiagram
actor U as Cliente (checkout)
participant FE as frontend
participant GW as gateway
participant Ord as OrderController
participant Sup as ReservationSupervisorActor
participant Res as ReservationActor (por pedido)
participant K as Kafka
U->>FE: inicia checkout do SKU
FE->>GW: POST /reservations
GW->>Ord: proxy REST
Ord->>Sup: Ask(CreateReservation)
Sup->>Sup: existe ator para este orderId?
alt não existe
Sup->>Res: cria filho (Props por orderId)
end
Sup->>Res: encaminha CreateReservation
Res->>Res: agenda auto-expiração (TTL 15min)
Res-->>Sup: ReservationCreated
Sup-->>Ord: ReservationCreated
Ord->>K: publica RESERVATION_CREATED em order-events
Ord-->>GW: 200 { status: RESERVED, expires_at }
GW-->>FE: resposta repassada
Note over Res: passados 15min sem confirmação
Res->>Res: ReservationExpired (self-message agendado)
Res->>Res: Context.Stop(Self)
Cálculo de preço dinâmico
sequenceDiagram
actor A as Analista de preços
participant FE as frontend (Test Lab)
participant GW as gateway
participant Solver as pricing-solver
A->>FE: define/edita uma pricing rule
FE->>GW: POST /pricing-rules
GW->>Solver: proxy REST (axios)
Solver-->>GW: regra criada (weight, multiplier, base_markup)
GW-->>FE: 201 Created
A->>FE: simula preço pra um SKU
FE->>GW: POST /pricing-rules/calculate
GW->>Solver: POST /calculate
Solver->>Solver: finalPrice = basePrice·(1-weight) + (basePrice+base_markup)·multiplier·weight
Solver-->>GW: preço final calculado
GW-->>FE: resultado da simulação
Modelo de dados (Supabase/PostgreSQL)
Cada domínio tem seu próprio schema no mesmo cluster Postgres, isolados por schema (pricing, orders,
audit) mais as tabelas do Catalog Service:
erDiagram
PRICING_RULE ||--o{ PRICE_CHANGE_LOG : "gera ao disparar"
PRICING_RULE {
uuid id PK
string name
string target_scope "GLOBAL|CATEGORY|SKU|REGION"
jsonb conditions
jsonb action_logic
double weight "0..1, peso do blend"
double multiplier
double base_markup
bool is_active
}
PRICE_CHANGE_LOG {
uuid id PK
string sku_id
decimal old_price
decimal new_price
uuid rule_id FK
string triggered_by "SOLVER|MANUAL|SCHEDULED"
}
ORDER_EVENT {
bigint id PK
uuid aggregate_id "order/reservation id"
string event_type "RESERVATION_CREATED|STOCK_LOCKED|ORDER_PAID"
jsonb payload
int version
}
INVENTORY_SNAPSHOT {
string sku_id PK
int total_reserved
int total_available
}
PRODUCT ||--o{ STOCK_AUDIT : "audita mudanças de"
PRODUCT {
uuid id PK
string sku
string name
decimal base_price
int stock_quantity
}
STOCK_AUDIT {
uuid id PK
uuid product_id FK
int change_amount
int new_quantity
string reason
}
Stack poliglota
| Serviço | Tecnologia |
|---|---|
| Frontend / Control Plane | Next.js 14 (Vercel) |
| API Gateway / BFF | Node.js · NestJS |
| Ingestão | Java 21 · Spring WebFlux |
| Pricing Solver | C++ 20 · Drogon (REST hoje, gRPC planejado) |
| Order / Catalog | .NET 8 · Akka.NET · Supabase |
| Mensageria | Apache Kafka |
| Observabilidade | Loki · Promtail · Prometheus · cAdvisor · Grafana |
Como rodar
# Subir o cluster de serviços localmente
docker-compose up --build
# Comandos comuns ficam no Makefile
make help # Código aberto
O LogisticSaaS é open source, um laboratório de padrões de sistemas distribuídos.