Módulo Tenant / Organização — visão geral
O módulo de organização é o dono da hierarquia multi-tenant do Portal 2.0: tenants
(organizações/clientes) e as unidades que cada tenant tem, mais quatro capacidades que vivem em
cima dessa hierarquia — branding (identidade visual e domínio customizado), configuração
tipada com override por nível, módulos (feature flags por tenant/unidade) e PACS (o
storage de imagens médicas usado por cada unidade). Ele vive no pacote organization do backend
(NestJS), organizado nos submódulos tenant, unit, branding, configuration, module e
pacs.
Não existe camada "empresa" (company) neste código. Um rascunho anterior desta doc descrevia uma hierarquia Tenant → Company → Unit; o código atual tem só dois níveis: Tenant → Unit. Se você tiver visto uma versão antiga com "companies", ela não reflete o backend atual.
Prefixo de rotas e versionamento
Como o restante da API principal, este módulo usa o versionamento nativo do NestJS por URI
(VersioningType.URI, versão padrão 1), então toda rota fica sob /v1/.... Não há segmento de
tenant no prefixo — os ids são sempre parâmetros explícitos de path (:tenantId, :unitId), o que
permite que o mesmo access token atue sobre qualquer tenant/unidade que o ator tenha permissão de
acessar.
Em desenvolvimento local, o serviço sobe na porta 3000 (MAIN_API_PORT, default 3000) e expõe
o Swagger em http://localhost:3000/docs.
Hierarquia de domínio
Arquitetura dos submódulos
Autorização: dois padrões coexistindo
Este módulo usa dois mecanismos de autorização diferentes, e vale reconhecê-los para não confundir um pelo outro ao ler o código ou o Swagger:
- Guard declarativo genérico (
@Authorize({ permissions, scope })+AuthorizationGuard, compartilhado com o resto do backend) — resolve o escopo (tenant,unit,unit-list,platform-admin, etc.) a partir do path/query da requisição e decide no nível HTTP. - Checagem fina dentro do serviço (
OrganizationTenantAdminAuthorizationServicepara tenant/unit/branding;OrganizationModuleAuthorizationServicepara configuration/module;OrganizationPacsAuthorizationServicepara pacs) — quase sempre redundante com o guard, mas é a fonte de verdade real, porque alguns serviços são chamados também fora do HTTP (ex.: API pública interna entre módulos do backend).
Divergências confirmadas entre o que o guard HTTP parece permitir e o que o serviço realmente aceita — documentadas em detalhe nas páginas de cada rota, mas vale destacar aqui porque afeta várias páginas:
- Em
pacs, três rotas (liberar/removerPACS do tenant,registrarunidade num PACS) declaram um guard que aceitaria um gestor de tenant compermission:manage, mas o serviço por trás exige administrador de plataforma de fato. reativar/mover unidadeexigem administrador de plataforma de forma estrita (assertCanManageTenants), sem o atalho depermission:managepor tenant que outras rotas de escrita aceitam.- Em
brandinge em partes detenant/unit, o serviço aceita também quem tem a permissão equivalente no tenant dono do recurso, mesmo sem uma concessão granular no recurso específico (ex.: gerenciar o tenant inteiro dá acesso à branding de qualquer unidade dele).
Configuração e módulos: dois mecanismos irmãos de override
configuration e module resolvem o mesmo problema de formas propositalmente distintas:
Configuração (configuration) | Módulos (module) | |
|---|---|---|
| O que guarda | Um valor tipado (STRING/NUMBER/BOOLEAN/JSON) por chave | Um estado ligado/desligado por chave (14 chaves fixas de feature) |
| Cascata | DEFAULT (catálogo) → TENANT → UNIT, primeiro nível com valor explícito vence | TENANT → UNIT, override de unidade sempre vence quando existe |
| Histórico | Não — só o valor atual por nível | Sim — cada mudança gera uma linha de controle preservada (soft-delete, nunca apagada) |
| Escrita restrita por | Allowlist de chaves liberadas para escrita (a maior parte do catálogo ainda não migrou) | Nada análogo — qualquer uma das 14 chaves pode ser ligada/desligada, mas nem toda chave tem efeito real no resto do sistema (ver "consumer matrix" na página de módulos) |
| Auditoria | Telemetria de acesso (sucesso/falha), sem gravar o valor | Evento de outbox com antes/depois a cada troca |
PACS: três camadas
pacs é o submódulo mais complexo. Três conceitos, hierárquicos:
- Platform PACS — catálogo global (não pertence a nenhum tenant) dos storages de imagem médica; espelha um storage que existe de fato num serviço externo (PACS Storage Management).
- PACS do tenant ("release") — a liberação de um Platform PACS para um tenant específico, com
uma classificação de negócio: papel (
MAIN/BACKUP/CUSTOM), se é o default do papel, e (para backup) retenção. - PACS da unidade — o vínculo de uma unidade a um PACS do tenant, que por padrão herda o
default do papel e pode ser sobrescrito manualmente para apontar a um PACS de papel
CUSTOM.
Ver visão detalhada em PACS do tenant para as regras de classificação e herança, e PACS por unidade para o vínculo em si.
Convenção de erros
Toda exceção de negócio estende BaseError (ValidationError → sempre 400; BusinessError →
statusCode explícito no construtor; AlreadyExistsError → sempre 409; ForbiddenAction → sempre
403) e já carrega seu statusCode e errorCode fixos. O filtro global de exceções só repassa esses
valores — não há uma tabela central de mapeamento status↔erro. Cada página de rota abaixo lista as
classes de erro reais confirmadas no código, com o status e a condição exata.
Páginas deste módulo
| Página | Cobre |
|---|---|
| Tenants (perfil e ciclo de vida) | GET/PATCH /tenants/:id/profile, GET /tenants, GET /tenants/:id, POST .../inactivate, POST .../reactivate |
| Unidades (perfil, ciclo de vida e logo) | POST/PATCH/GET /units[/:id], DELETE/POST .../inactivate, POST .../reactivate, GET/PUT/DELETE .../logo |
| Mover unidade entre tenants | POST .../units/:id/move e .../move/revert |
| Branding do tenant | GET/PUT/DELETE /tenants/:id/branding/*, domínio customizado |
| Branding da unidade | GET/PUT/DELETE /units/:id/branding/* |
| Resolução de domínio customizado (interno) | POST /internal/v1/organization/custom-domains/resolve |
| Configuração (tenant e unidade) | GET/PUT/DELETE .../configurations/* |
| Módulos (feature flags por tenant/unidade) | GET/PUT/DELETE .../modules/*, histórico de controle |
| PACS — Catálogo global (plataforma) | POST/GET/PUT/DELETE /pacs[/:id] |
| PACS — Liberação e classificação do tenant | POST/PUT/GET/DELETE /tenants/:id/pacs/*, apply-to-inherited |
| PACS — Vínculo da unidade | GET/POST /tenants/:id/units/:id/pacs/*, move, override, retry |
:::tip OpenAPI
A documentação interativa (schemas + "Try it out") está disponível em /docs no ambiente onde a
API está rodando (local: http://localhost:3000/docs).
:::