O modelo é o software. Linguagem ubíqua no código. DDD não é pastas Domain/ com getters — é invariantes protegidas por agregados e contextos com limites claros.
Escolha de estilo: software-architecture. Setas de dependência: clean-architecture. Um deploy, vários contextos: modular-monolith. Extração: microservices-architecture.
Quando (não) usar
Use quando regras, conflitos de linguagem e ciclos de vida de dado forem o risco.
Não use (tático pesado) em CRUD, BFF fino, jobs de glue. Ali, transações simples e DTOs bastam. Strategic (nomes de contexto) ainda ajuda.
Estratégico
Linguagem ubíqua
- Os termos do domínio aparecem em tipos, métodos e eventos — não
ManagerUtil,DataDTO2. - Se o negócio diz “reserva” e o código diz “OrderItemFlag`, o modelo está errado.
- Glossário curto por contexto; o mesmo termo não significa a mesma coisa em outro contexto (
Pagamento≠Fatura).
Bounded context
- Um contexto = um modelo + uma equipe mental. API pública; interior privado.
- Mapa: parente (upstream/downstream), ACL, shared kernel (mínimo), customer/supplier.
- Integração entre contextos: eventos ou ACL — não join de tabelas nem import de entidades internas.
Vendas --OrderPlaced--> Fulfillment
Vendas --ACL--> Legado ERP
- Um contexto ≠ um microsserviço obrigatório. Pode ser módulo no monolito.
Tático
| Bloco | Papel |
|---|---|
| Entity | Identidade + ciclo de vida |
| Value object | Igualdade por valor, imutável |
| Aggregate | Fronteira de consistência; um root |
| Domain event | Algo que aconteceu no domínio |
| Repository | Reconstitui agregados (não “DAO de tabela”) |
| Domain service | Regra que não cabe numa entidade |
| Application service / use case | Orquestra 1 transação / 1 comando |
Agregado
- Invariantes dentro do root. Nada de fora altera filhos sem passar pelo root.
- Referências entre agregados por ID, não por objeto navegável enorme.
- Pequeno: transação = um agregado. “God aggregate” é monolito dentro do contexto.
- Persistência: carrega o agregado, aplica comando, salva. Sem entity semi-gráfica espalhada na UI.
Order (root)
├── OrderLine (entity interna)
└── Money (VO)
outro agregado: Customer → só customerId dentro de Order
Eventos de domínio
- Nome no passado (
OrderPlaced). Payload mínimo + IDs. - Publicado após o commit (outbox se cruzar processo) —
event-driven-architecture. - Não use evento para “chamar o outro serviço sincronicamente”.
Repositório
Get(id),Save(aggregate). Queries de tela/listagem não precisam passar pelo agregado — modelo de leitura (cqrsse divergir).- Implementação na infra; interface no domínio (Clean) se o projeto já inverte dependências.
Application vs Domain
- Use case: autentica contexto, carrega agregado, chama domínio, persiste, dispara evento.
- Domínio: sem HTTP, sem SQL, sem framework de web. Relógio/IDs injetados se a regra precisar.
Anti-padrões
- Anemic domain: entidades só dados + “service” procedural de 2k linhas
- Agregado = banco inteiro
- DDD pasta copiada (
Entities,DTOs,Interfaces) sem invariante - Shared kernel com 40 tipos “comuns”
- Repositório
GetAll+ filtro na aplicação para regra que era do agregado - Bounded context por tabela
Integração com outras skills
- Decisão:
software-architecture - Dependências:
clean-architecture - Módulos:
modular-monolith - Reads vs writes:
cqrs - Integração assíncrona:
event-driven-architecture - Serviços:
microservices-architecture - Authz no agregado/contexto dono:
authentication-authorization
Critérios de conclusão
- Contextos e linguagem visíveis no código
- Agregados com invariante e transação clara
- Referência cross-aggregate por ID
- Eventos no passado, após persistência
- Sem DDD tático onde o domínio é CRUD