Bounded Contexts
Bounded Contexts
Seção intitulada “Bounded Contexts”O domínio canna-oss é organizado em 8 bounded contexts com fronteiras explícitas. Comunicação entre contextos ocorre exclusivamente por domain events e referências por ULID — nunca por FK direta entre contextos.
1. Membership
Seção intitulada “1. Membership”Aggregate root: Member
Value Objects
Seção intitulada “Value Objects”| Value Object | Descrição |
|---|---|
CPFHash | SHA-256 + site_salt (nunca CPF em claro) |
EncryptedPersonalData | AES-256-GCM com member_key individual |
MonthlyQuotaGrams | Quota mensal em gramas (imutável após definição médica) |
ConsentVersion | Versão do termo LGPD + timestamp de aceite |
Estados do Member
Seção intitulada “Estados do Member”PENDING_CONSENT → ACTIVE → SUSPENDED → ANONYMIZEDTransição para ANONYMIZED aciona crypto-deletion: descarta member_key, substitui EncryptedPersonalData por tombstone. Irreversível.
Invariantes
Seção intitulada “Invariantes”CPFHashúnico por tenant (site_salt diferente por instância)- Consentimento expresso (versão corrente) obrigatório antes de qualquer dispensação
- Quota mensal enforced:
Σ(dispensações_g no mês) + nova_g ≤ quota_g_month - Prescrição médica válida (não expirada) obrigatória para
ACTIVE ANONYMIZEDbloqueia toda operação subsequente — guard no aggregate
2. Cultivation
Seção intitulada “2. Cultivation”Aggregate root: CultivationBatch
Entidades
Seção intitulada “Entidades”Plant— identificada por ULID permanente (nunca reutilizado, mesmo após destruição)
Value Objects
Seção intitulada “Value Objects”EncryptedGeolocation— coordenadas cifradas (exigência RDC 1.013)FairValueBRL— valor justo CPC 29 calculado no harvest
State Machine da Plant
Seção intitulada “State Machine da Plant”GERMINATING → SEEDLING → VEGETATIVE → FLOWERING → HARVESTED ↓ ↓ ↓ ↓ DESTROYED DESTROYED DESTROYED DESTROYEDQualquer estágio pode transitar para DESTROYED. Destruição exige registro de testemunha (witness_user_id).
Invariantes
Seção intitulada “Invariantes”- Progressão de estágio é forward-only (exceto
DESTROYED) - Destruição requer
witness_user_idcom roleCULTIVADORouRESPONSAVEL_TECNICO fair_value_brl(CPC 29) obrigatório no eventoHarvestRecorded- Geolocalização sempre cifrada em repouso
- ULID da planta nunca reutilizado após destruição
3. Processing
Seção intitulada “3. Processing”Aggregate root: HarvestBatch (criado pelo evento HarvestRecorded vindo de Cultivation)
Entidades
Seção intitulada “Entidades”| Entidade | Campos-chave |
|---|---|
ProcessingRun | yield_pct (GENERATED), input_g, output_g |
LabSample | coa_file_hash (SHA-256), thc_pct, cbd_pct, contaminants_pass |
Invariantes
Seção intitulada “Invariantes”- Apenas usuário com role
RESPONSAVEL_TECNICOpode aprovarLabSample coa_file_hashimutável apósLabSampleApproved— nenhum UPDATE permitidoLabSampleRejectedbloqueia oInventoryLotupstream até nova amostrayield_pctcalculado automaticamente (output_g / input_g), não editável manualmente- Aprovação (aquisição ≠ aprovação ≠ dispensação — segregação RDC 1.014)
4. Inventory
Seção intitulada “4. Inventory”Aggregate root: InventoryLot
Estados do InventoryLot
Seção intitulada “Estados do InventoryLot”QUARANTINE → AVAILABLE → EXHAUSTED ↓ RECALLEDRECALLED é terminal — não pode ser revertido para AVAILABLE.
Invariantes
Seção intitulada “Invariantes”- Liberação (
QUARANTINE → AVAILABLE) requerLabSampleApprovedupstream - Aprovador do
LabSampledeve ter roleRESPONSAVEL_TECNICO RECALLEDé irreversível — toda dispensação pendente do lote é bloqueada- Quantidade disponível nunca negativa (constraint + check no aggregate)
5. Dispensation
Seção intitulada “5. Dispensation”Aggregate root: Dispensation (imutável após criação)
Referências Cross-Context
Seção intitulada “Referências Cross-Context”| Campo | Tipo | Origem |
|---|---|---|
member_ref | ULID | Membership context |
inventory_lot_ref | ULID | Inventory context |
Referências cross-context permanecem por ULID — sem FK direta. Atomicidade vs consistência eventual depende do tipo de fato:
- Estado regulatório crítico (quota + estoque) é atômico — emitido no mesmo append que
DispensationRecorded. Optimistic concurrency garante consistência sem 2PC. - Integrações externas (SNGPC, PDF, email) são eventualmente consistentes via BullMQ — falhas não invalidam a dispensação.
- Read models cross-context convergem a partir dos eventos — consistência eventual aceitável para queries de UI.
Cf. ADR-001 para o boundary sync vs async completo.
Invariantes
Seção intitulada “Invariantes”Dispensationé imutável após criação — sem UPDATE, sem cancelamento por UI. Estorno é dispensação compensatória nova.decide()rejeita comando se quota OU estoque insuficiente — emiteQuotaExceededAttemptouLotInsufficientQuantity, nãoDispensationRecorded.inventory_lot_refdeve apontar para lote em estadoAVAILABLEno momento dodecide().DispensationRecorded+MemberQuotaConsumed+LotQuantityDeductedemitidos em um único append no event store.- XML SNGPC gerado assincronamente via BullMQ após
DispensationRecorded— sua falha não invalida o fato regulatório. - Dispensador (
DISPENSADOR) ≠ aprovador de COA (RESPONSAVEL_TECNICO) — segregação RDC 1.014.
6. Compliance
Seção intitulada “6. Compliance”Modelo: Read model apenas (sem aggregate próprio)
Lê projeções de todos os outros contextos. Não emite commands — apenas gera relatórios.
Relatórios Produzidos
Seção intitulada “Relatórios Produzidos”| Relatório | Frequência | Formato |
|---|---|---|
| BSPO | Trimestral + anual | PDF + XML ANVISA |
| KPI Report | Sob demanda | 7 indicadores |
| DRE Mensal | Mensal | |
| SNGPC Batch | Diário/semanal | XML batch ANVISA |
| Relatório Judicial | Sob demanda |
Invariantes
Seção intitulada “Invariantes”- Relatórios são value objects imutáveis após geração — nenhum UPDATE
- BSPO assinado digitalmente pelo
RESPONSAVEL_TECNICO - SNGPC batch enviado somente após confirmação de conectividade ANVISA
7. Finance
Seção intitulada “7. Finance”Aggregate root: FinancialStatement
Entidades
Seção intitulada “Entidades”| Entidade | Descrição |
|---|---|
DREMonth | Demonstração de Resultado mensal |
BiologicalAssetValuation | Valoração CPC 29 / IAS 41 por harvest |
Invariantes
Seção intitulada “Invariantes”- Todos os valores monetários como
Decimal(15,2)— nuncafloat BiologicalAssetValuationcriado automaticamente por eventoHarvestRecorded- DRE mensal consolidado no fechamento do mês (sem edição retroativa)
- Mensalidades registradas por
MensalidadeRecorded— rastreável por membro
8. Identity & Access
Seção intitulada “8. Identity & Access”Aggregate root: User
RBAC — Roles Disponíveis
Seção intitulada “RBAC — Roles Disponíveis”| Role | Permissões principais |
|---|---|
ADMIN | Gestão de usuários, configuração do tenant |
RESPONSAVEL_TECNICO | Aprovação de COA, assinatura BSPO |
DISPENSADOR | Registrar dispensações |
CULTIVADOR | Registrar plantas, avançar estágios |
FINANCEIRO | Visualizar/gerar DRE e financeiro |
AUDITOR | Leitura completa, sem escrita |
MEMBRO | Acesso ao próprio prontuário |
Invariantes
Seção intitulada “Invariantes”- TOTP obrigatório para roles
ADMIN,RESPONSAVEL_TECNICO,DISPENSADOR - Segregação RDC 1.014: aquisição ≠ aprovação ≠ dispensação (roles distintos)
AUDITORtem acesso read-only — nenhum command permitido- Senha nunca armazenada em claro — bcrypt + pepper
- Sessão invalidada imediatamente após
UserRoleChanged