Tenancy em produção
Como é hoje (correção do inventário antigo)
Seção intitulada “Como é hoje (correção do inventário antigo)”O inventário antigo (docs/architecture/legacy-systems-inventory.md na versão 2025) afirmava “1 VPS por cliente”. Errado. O modelo real, confirmado pelo dossiê de arqueologia, é layered multi-tenant:
flowchart TB
subgraph FRANQUIA1[VPS Franquia 1]
DB1[(PostgreSQL único)]
APP1[sowil-api + Grifos]
DB1 --> C1A[Cliente A]
DB1 --> C1B[Cliente B]
DB1 --> C1C[Cliente C]
end
subgraph FRANQUIA2[VPS Franquia 2]
DB2[(PostgreSQL único)]
APP2[sowil-api + Grifos]
DB2 --> C2A[Cliente D]
DB2 --> C2B[Cliente E]
end
subgraph FRANQUIA3[VPS Franquia 3]
DB3[(PostgreSQL único)]
APP3[sowil-api + Grifos]
DB3 --> C3A[Cliente F]
DB3 --> C3B[Cliente G]
end
classDef vps fill:#2b5383,stroke:#0e2a3a,color:#fff
classDef db fill:#00a9c9,stroke:#0e2a3a,color:#fff
classDef client fill:#76c0d5,stroke:#0e2a3a,color:#0e2a3a
class FRANQUIA1,FRANQUIA2,FRANQUIA3 vps
class DB1,DB2,DB3 db
class C1A,C1B,C1C,C2A,C2B,C3A,C3B client
Isolamento real
Seção intitulada “Isolamento real”| Camada | Mecanismo | Garantia |
|---|---|---|
| Entre franquias | VPS separadas | Forte (rede, disco, processo) |
| Entre clientes dentro da franquia | Filtro de aplicação WHERE ex_empresa IN (...) | Fraca — sem RLS de banco |
Como a v2 ataca
Seção intitulada “Como a v2 ataca”flowchart TB
subgraph V2POD[Pod multi-tenant default]
DBV2[(PostgreSQL TimescaleDB)]
ACTASTENANT[acts_as_tenant<br/>por Organization]
PUNDIT[Pundit policies<br/>por Organization]
DBV2 --> ACTASTENANT
ACTASTENANT --> PUNDIT
PUNDIT --> O1[Org 1<br/>franquia A]
PUNDIT --> O2[Org 2<br/>franquia B]
PUNDIT --> O3[Org 3<br/>franquia C]
end
subgraph ENTERPRISE[Pod isolado por tenant — opcional enterprise]
DBE[(PostgreSQL dedicado)]
ACTASTENANT2[acts_as_tenant]
DBE --> ACTASTENANT2
ACTASTENANT2 --> OE[Org Enterprise]
end
classDef pod fill:#00a9c9,stroke:#2b5383,color:#fff
classDef db fill:#2b5383,stroke:#0e2a3a,color:#fff
classDef org fill:#76c0d5,stroke:#0e2a3a,color:#0e2a3a
class V2POD,ENTERPRISE pod
class DBV2,DBE db
class O1,O2,O3,OE org
A v2 usa acts_as_tenant + Pundit para garantia em duas camadas:
- Banco: toda query passa pelo escopo de
current_organizationautomaticamente. - Aplicação: políticas Pundit explícitas por recurso.
Enterprise tier opcional: pod dedicado por tenant (DB + app), para central que exige isolamento físico.
Migração de dados
Seção intitulada “Migração de dados”flowchart LR
A[VPS franquia 1<br/>atual] --> B[ETL]
B --> C[Solutio v2 pod<br/>Organization 1]
D[VPS franquia 2<br/>atual] --> B
B --> E[Solutio v2 pod<br/>Organization 2]
classDef legacy fill:#cccccc,stroke:#666,color:#333,stroke-dasharray:5
classDef v2 fill:#00a9c9,stroke:#2b5383,color:#fff
classDef etl fill:#f4a261,stroke:#9a5d2c,color:#fff
class A,D legacy
class C,E v2
class B etl
ETL franquia-a-franquia, mapeando ex_empresa (campo do sistema atual) → organization_id v2. Plano detalhado vive em docs/migration/.