Arquitetura
API, workers, migrator, PostgreSQL, Valkey e ElasticMQ/SQS, e como conversam.
Executáveis
Cada executável é uma imagem própria, gerada do mesmo Dockerfile (alvos runtime, workers e migrator).
| Executável | O que faz | Porta |
|---|---|---|
api | HTTP (Hono, com OpenAPI). Grava efeitos e mensagens do outbox na mesma transação; nunca fala com a fila. | 3000 |
workers | Consome as filas listadas em QUEUES, roda tasks e assinantes de eventos; um deles também roda o relay do outbox. | 8081 (/healthz) |
migrator | Aplica as migrations pendentes e roda os seeders padrão (deploy), depois termina. | nenhuma |
A API não aplica migrations ao subir. A API e os workers carregam o nome da migration mais nova do próprio build e
ficam não prontos até o banco conter essa migration: /ready responde 503 com a migration pendente e os workers
não consomem. Assim uma versão nova nunca serve sobre um schema antigo, mesmo que o migrator rode depois dela.
Serviços
| Serviço | Papel |
|---|---|
| PostgreSQL 18 | Fonte da verdade: dados de domínio, outbox, idempotência e auditoria das execuções. |
| Valkey 8 | Fala o protocolo Redis (o serviço continua chamado redis, assim como REDIS_URL). Portão de idempotência, cache do status de runs e sinal para acordar o relay. Nada nele é fonte da verdade. |
| ElasticMQ | Fila compatível com SQS usada localmente e em staging: quatro filas (critical, default, bulk, maintenance) e suas dead-letter queues, em memória. |
Os workers usam a API do SQS, então o mesmo código fala com o SQS da AWS trocando endpoint e credenciais
(MONALISA_SQS_*).
HTTP
GET /health: o processo está vivo.GET /ready: PostgreSQL e Redis disponíveis e o schema na migration esperada; responde 503 com um motivo público quando não está pronto. As duas respostas trazemlatestMigration.GET /task-runs/{runId}: status de uma execução em segundo plano do tenant de quem chama (rota protegida)./docse/openapi.json: referência pública gerada das rotas.
Respostas de sucesso têm success, data e meta; erros têm success: false e error com code e message.
Accept-Language escolhe entre inglês (padrão) e português.
Rotas protegidas respondem 401 enquanto a autenticação é reconstruída (ver Autenticação). Cabeçalhos
como x-tenant-id ou x-user-id nunca estabelecem identidade nem autoridade.
Organização do código
| Diretório | Responsabilidade |
|---|---|
apps/api | Composição da API, rotas e validação de transporte |
apps/workers | Tasks (*.task.ts), assinantes (*.handler.ts) e o loop de consumo |
apps/migrator | Comandos de migrate/deploy, geração de migrations, seeders e o baseline |
packages/<domínio> | Entities, SQL de constraints, regras, seeders e fixtures de teste de cada domínio |
framework/* | DI, configuração, HTTP, ORM (MikroORM 7), mensageria, idempotência e jobs |
infra/dokploy | Layout de staging no Dokploy e os comandos dokploy:apply / dokploy:deploy |
A persistência usa MikroORM com registro explícito das entities. Cada operação abre seu próprio EntityManager e aplica o contexto do ator (tenant, usuário ou processo) dentro da mesma transação do PostgreSQL. O schema é controlado só por migrations; a API nunca sincroniza schema ao subir.