Camadas¶
O Spryx Backend segue a Clean Architecture com fronteiras estritas entre camadas. Cada camada tem uma única responsabilidade e depende apenas das camadas abaixo dela.
Diagrama de Camadas¶
flowchart TD
A["HTTP Controllers\nsrc/entrypoints/http/"]
B["Use Cases · Query Handlers\nsrc/modules/{module}/application/"]
C["Services\nsrc/modules/{module}/application/services/"]
D["Repositories\nsrc/modules/{module}/infrastructure/"]
E[("PostgreSQL\nvia Supabase")]
A -->|"chama UC ou Query Handler"| B
B -->|"delega lógica reutilizável"| C
B -->|"lê / escreve"| D
C -->|"lê / escreve"| D
D -->|"SQL via AsyncSession"| E
Responsabilidades por Camada¶
HTTP Controllers (src/entrypoints/http/)¶
- Recebem requisições HTTP, validam schemas de entrada
- Chamam um Use Case ou Query Handler
- Retornam respostas HTTP
- Nunca contêm lógica de negócio ou acesso ao banco
Use Cases (application/ucs/)¶
- Possuem a fronteira da transação via Unit of Work
- Orquestram services e repositories
- Aplicam regras de negócio e idempotência
- Cada UC é uma pasta com 3 arquivos:
command.py,result.py,uc.py - Nunca chamam outros Use Cases — extraia lógica compartilhada para um Service
Query Handlers (application/queries/)¶
- Operações somente leitura otimizadas para endpoints de listagem
- Usam SQL puro para performance (joins, CTEs, paginação)
- Retornam Views/DTOs, nunca entidades de domínio
- Chamados diretamente por controllers para endpoints GET /list
Services (application/services/)¶
- Lógica de negócio reutilizável compartilhada entre múltiplos Use Cases
- Recebem
tx: Transactiondo UC - Podem coordenar múltiplos repositories
Repositories (infrastructure/)¶
- Apenas persistência — sem lógica de negócio
- Recebem
tx: Transactioncomo primeiro parâmetro - Seguros por padrão para multi-tenant: leituras requerem
tenant_id, escritas usamentity.tenant_id - Classes concretas apenas (sem ABC/interface)
Padrão de Transação (Unit of Work)¶
Todo write no banco passa por uma transação de propriedade do Use Case.
sequenceDiagram
participant C as Controller
participant UC as Use Case
participant UoW as Unit of Work
participant Repo as Repository
participant DB as PostgreSQL
C->>UC: execute(command)
UC->>UoW: abre transação
UoW->>Repo: save(tx, entity)
Repo->>DB: INSERT / UPDATE
UoW->>UC: commit
UC->>C: result
Note over UC: side effects (email, webhook)<br/>acontecem APÓS o commit
Regra das três zonas:
- Antes da transação — chamadas externas preparatórias (vault, APIs externas)
- Dentro da transação — todos os writes SQL e leituras necessárias
- Após o commit — side effects (notificações, webhooks, emails)
Padrões Principais¶
IDs Tipados¶
Todas as entidades usam IDs baseados em PrefixedULID para type safety:
AgentID # "ag_01HQ..."
TenantID # "tenant_01HQ..."
WorkflowID # "wf_01HQ..."
ContactID # "contact_01HQ..."
Previne mistura de IDs entre tipos de entidade em tempo de compilação.
Entidades Imutáveis¶
Entidades de domínio são modelos Pydantic atualizados via model_copy:
Sem mutação no lugar — toda mudança retorna uma nova instância.
Comunicação Entre Módulos¶
Módulos nunca importam uns dos outros. Dependências cross-module passam por Ports (interfaces em src/modules/shared/ports/) conectados via DI: