Arquitectura Macro v2 — Micro-serviços

GORO PMP Enterprise · Kubernetes (I52) · divisão CORE v2 (I49/I50 + emenda I53: Offboarding em M4) · fonte: docs/ARCHITECTURE_MACRO_V2.md

⚠️ Decisões desta revisão: I52 (Kubernetes desde já — revoga I40, +3-5 dias na S0, buffer consumido) · I53 (Offboarding migra para M4; a escrita da cessação permanece em M0 por evento) · P6 RATIFICADA (input #41): PROD híbrido — K3s no edge + Azure Arc para governança, sem AKS obrigatório. Zero pendentes nesta página.

1 · Bounded Contexts e Serviços

MóduloServiçoPortaResponsabilidade únicaPublica
M0 Colaboradoressvc-hr:3045Fonte única de verdade do colaborador — cadastro 360º, orgânica, contratos, documentos, self-service, escrita da cessaçãohr.employee.*, hr.contract.*
M1 Tempo & Assiduidadesvc-time:3051Biometria HMAC idempotente, consolidação raw→ponto, faltas, leave (maker-checker), turnos/escalastime.timesheet.closed, time.absence.approved
M2 Payroll & Despesassvc-payroll:3052Motor por rubricas (HE, INSS, IRT Lei 28/20), fechos, recibos, despesas, alteração salarial maker-checker — consome snapshots, nunca lê BD alheiapayroll.run.approved
M3 Desempenho & Formaçãosvc-perf + svc-train:3053Ciclos de avaliação, objectivos, calibração · catálogo/edições/certificados + LMS (schemas separados, extraível)perf.review.closed
M4 Recrutamento & Ciclo de Talentosvc-recruit:3054Vagas→ofertas→contratação + Offboarding (I53): checklist de saída, devoluções, quitação (fluxo já existente no GORO migrado). Purga PII 12mrecruit.hired, recruit.offboarding.completed
M5 Disciplinaressvc-discipline:3055Máquina de estados LGT (nota de culpa, prescrição), validação jurista, docs WORMdiscipline.sanction.applied
M6 Plataformagateway · svc-identity · svc-workflow · svc-audit · svc-notify · svc-report P11:443 · :3041 · :3050 · :3060 · :3061 · :3062Entrada única TLS/JWT/rate-limit · Keycloak/MFA/JWT v2 · motor de aprovações SoD · trilha imutável · notificações · Relatórios & BI read-only: read models por eventos, mapas legais AGT/INSS do snapshot, agregados p/ dashboards de todos os módulos (docs/REPORTING_BI_SPEC.md)workflow.instance.* · report.export.ready

Regra: 1 contexto = 1 schema PG próprio (hr,org,ged,time,pay,perf,train,recruit,discipline,wf,audit,report) com RLS por tenant. Tecto para 2 devs (emenda P11): 9 serviços de domínio + svc-report read-only + gateway — a excepção é defensável porque report não tem escrita de negócio nem workflow; dividir mais é nano-serviço sem retorno.

2 · Padrões de Comunicação

PadrãoQuandoTecnologiaRegras duras
SíncronoCliente precisa da resposta agora, operação do próprio contextoREST via API Gateway (SPA/PWA/Manager/biometria); JWT validado localmente (JWKS)timeout 2s + circuit breaker (opossum); proibido encadear 2+ serviços síncronos num pedido; proibido ler BD alheia
Síncrono interno (excepção)Payroll puxa snapshot fechado de assiduidade no arranque da corridagRPC svc-payroll → svc-time (leitura imutável, idempotente)única chamada inter-serviço síncrona autorizada
AssíncronoFactos de negócio consumados, integração entre contextosRabbitMQ (topic goro.events) — escolhido sobre Kafka: sem replay/débito que o justifique para 2 devsOutbox na mesma transacção + Inbox idempotente + DLQ com alerta
EventoProdutorConsumidoresEfeito
hr.employee.created/updated/terminatedsvc-hrtime, payroll, perf, disciplineréplicas locais read-only do colaborador
recruit.hiredsvc-recruitsvc-hrM0 cria o funcionário (recruit nunca escreve em funcionarios)
recruit.offboarding.completedsvc-recruitsvc-hrM0 cessa → dispara hr.employee.terminated
time.timesheet.closedsvc-timepayrollsnapshot imutável para a corrida
payroll.run.approvedsvc-payrollnotify, contabilidade (futuro)recibos, integração
discipline.sanction.appliedsvc-disciplinepayroll, recruit, notifydesconto · despedimento → M4 inicia offboarding
perf.review.closedsvc-perftrain, payroll (prémios por tenant)sugestão de formação, prémios
workflow.instance.approved/rejectedsvc-workflowserviço donoconclusão maker-checker
todostodossvc-audit, svc-notifytrilha imutável + notificações

3 · Diagrama (Mermaid)

flowchart TB subgraph CLIENTS["Clientes"] SPA["SPA React · tema Sober"] PWA["PWA Mobile"] MGR["PMP Manager"] BIO["Biometria"] end subgraph K8S["Kubernetes — K3s DEV · P6: K3s vs AKS PROD (dia 45)"] GW["API Gateway / Ingress
TLS · rate-limit · JWT · correlation-id"] subgraph PLATFORM["M6 · Plataforma"] IDN["svc-identity
Keycloak"] WF["svc-workflow
SoD/maker-checker"] AUD["svc-audit"] NOT["svc-notify"] end subgraph CORE["Módulos CORE"] HR["M0 · svc-hr
fonte única do colaborador"] TIME["M1 · svc-time
ponto · leave · shift"] PAY["M2 · svc-payroll"] PERF["M3 · svc-perf + train"] REC["M4 · svc-recruit
+ OFFBOARDING (I53)"] DISC["M5 · svc-discipline
LGT · WORM"] end MQ[("RabbitMQ
goro.events · outbox/inbox · DLQ")] subgraph DATA["PostgreSQL 16 · 1 schema por contexto · RLS"] DBHR[("hr")] DBTIME[("time")] DBPAY[("pay")] DBPERF[("perf·train")] DBREC[("recruit")] DBDISC[("discipline")] DBWF[("wf·audit")] end OBJ[("MinIO/R2 · WORM")] end SPA --> GW PWA --> GW MGR --> GW BIO -->|HMAC| GW GW --> IDN GW --> HR GW --> TIME GW --> PAY GW --> PERF GW --> REC GW --> DISC GW --> WF HR --- DBHR TIME --- DBTIME PAY --- DBPAY PERF --- DBPERF REC --- DBREC DISC --- DBDISC WF --- DBWF HR -->|outbox| MQ TIME -->|outbox| MQ PAY -->|outbox| MQ PERF -->|outbox| MQ REC -->|outbox| MQ DISC -->|outbox| MQ WF -->|outbox| MQ MQ -->|hr.employee.*| TIME MQ -->|hr.employee.*| PAY MQ -->|hr.employee.*| PERF MQ -->|hr.employee.*| DISC MQ -->|recruit.hired · offboarding.completed| HR MQ -->|time.timesheet.closed| PAY MQ -->|discipline.sanction.applied| PAY MQ -->|discipline.sanction.applied| REC MQ -->|perf.review.closed| PAY MQ -->|todos| AUD MQ -->|todos| NOT PAY -.->|gRPC snapshot · única sync interna| TIME HR --- OBJ DISC --- OBJ

4 · Consequências no plano

ItemImpacto
I52 — Kubernetes na S0 (revoga I40)+3-5 dias K3s na VM DEV, Helm charts base, GHCR, cert-manager, ingress. Absorvível SE a S0 não sofrer mais acréscimos — buffer consumido.
P6 — cluster PROD✅ RATIFICADA (input #41, 30 dias antes do prazo) — HÍBRIDO: K3s no edge + Azure Arc. K3s mantém-se o runtime (edge/on-prem); ligação operacional ao Azure via Azure Arc (Azure Policy, Monitor, Defender a partir do plano central) — sem forçar workloads para AKS. Custo AKS evitado; governança Azure mantida. Esta página fica com ZERO pendentes.
RabbitMQ como StatefulSet0 dias — outbox/inbox já estavam no desenho
I53 — Offboarding em M40 dias agora — migração do fluxo existente na fase do M4; escrita da cessação permanece em M0 (linha vermelha)
GA20/03/2027 (I55, input #18): 120 dias dev (fim 19/01) + 30 revisões (18/02) + 15 testes (05/03) + 15 polimentos → GA. A janela de dev mantém-se 120 dias — acréscimo de escopo dentro dela continua a empurrar as fases seguintes

5 · Registo — Comparação de Arquitecturas v1 vs v2 (FECHADA)

Input #29: o Hélio submeteu uma imagem com uma proposta de arquitectura alternativa (v1: Python core, tendência monólito modular, Redis para eventos, shadcn/ui). Foi feita comparação critério a critério contra a v2 (NestJS+Nx microserviços). A página dedicada /arch-compare foi arquivada no input #38 (comparação fechada) — este registo preserva o resultado na fase a que respeita.

ItemResultado
Placar por critérioArquitectura actual (v2) vence em 8 critérios · Proposta (v1) em 5 · Empates 2-3
VeredictoMANTER v2 — NestJS+Nx microserviços M0-M6, RabbitMQ goro.events, PG16 RLS, K3s
Ideias absorvidas da v1 (→ I60)① TimescaleDB no schema time · ② spec do Go Edge Agent antes da F2 · ③ Redis Streams SÓ para telemetria edge→M1. I60 RATIFICADA no input #37 («avança com mudanças i60-i65»)
RecusadosPython no core · monólito modular · Redis como bus de eventos · shadcn/ui
Fila de esperaExpo (mobile) — reavaliar na F3
Documento fontedocs/ARCH_COMPARISON_V1_IMAGE.md (mantido no repositório) · página arquivada em tracker/archive/arch-compare.html