O mito que travou milhares de projetos

"DDD é caro. É complexo. Só vale a pena em enterprise."

Essa frase aparece em reuniões de planejamento, arquiva ideias ambiciosas e empurra times para a zona de conforto do CRUD. O resultado? Sistemas que funcionam, mas não conversam com o negócio. Código que muda toda semana porque ninguém entendeu as regras de verdade.

A tese deste artigo é direta: Domain-Driven Design não é sobre tamanho de sistema. É sobre complexidade do domínio. E sistemas pequenos — como um módulo de análise de dados criminais, um agendador de consultas ou um motor de regras de crédito — podem ter domínios tão ricos (ou mais) que um ERP de multinacional.

Eric Evans, no Domain-Driven Design: Tackling Complexity in the Heart of Software (2004), deixa claro desde o primeiro capítulo: o foco não é a arquitetura, a infraestrutura ou o número de usuários. É a modelagem do domínio — o conjunto de regras, invariantes e linguagem que torna o software útil para quem realmente importa: o especialista do negócio.

O que DDD realmente é (e o que não é)

DDD é uma disciplina de modelagem. Não é um framework. Não é uma arquitetura. Não exige microserviços, event sourcing ou CQRS.

Os três pilares centrais, segundo Evans, são:

  1. Linguagem ubíqua: um vocabulário compartilhado entre desenvolvedores e especialistas do domínio, usado consistentemente no código, nos diagramas e nas conversas.

  2. Bounded Contexts: limites explícitos onde um modelo faz sentido. Fora dele, o mesmo termo pode ter outro significado.

  3. Modelos ricos: objetos que carregam comportamento e regras, não apenas dados.

O que DDD não exige:

  • Microserviços (um monólito bem modelado vale mais que dez serviços mal divididos).

  • Event Storming (útil, mas opcional).

  • Equipes grandes (times de 3–5 pessoas podem aplicar DDD com disciplina).

  • Diagramas complexos (o modelo vive no código, não no PowerPoint).

Evans resume: "O modelo não é um diagrama. É o conjunto de abstrações que o time usa para resolver problemas do domínio." Em outras palavras: se seu código não reflete as regras do negócio, você não está fazendo DDD — está fazendo CRUD com nomes bonitos.

Por que o mito persiste

DDD virou buzzword. Consultorias venderam "implementação completa de DDD" como pacote de seis meses com seis figuras. Arquitetos apresentaram context maps com 40 bounded contexts antes de escrever uma linha de código.

O resultado foi previsível: times associaram DDD a burocracia, custo e overengineering.

A raiz do problema está na confusão entre estratégia e tática:

 
DDD Estratégico DDD Tático
Bounded Contexts, Context Maps, Subdomains Entities, Value Objects, Aggregates, Repositories
Define onde e como dividir o sistema Define como modelar dentro de cada limite
Essencial em sistemas grandes ou multi-time Essencial em qualquer sistema com regras não triviais

O erro comum: tentar aplicar tudo de uma vez. Começar pelo context map antes de entender o primeiro aggregate. Evans alerta: "A modelagem começa com o domínio, não com a arquitetura."

Começando pequeno: DDD incremental em um sistema real

Vamos a um caso concreto: um sistema de análise de dados criminais.

Contexto: uma secretaria de segurança pública precisa cruzar ocorrências, identificar padrões geográficos, calcular riscos de reincidência e gerar alertas para equipes de rua. Não é "enterprise" no sentido corporativo, mas o domínio é denso:

  • Uma ocorrência pode ter múltiplos envolvidos (vítima, suspeito, testemunha).

  • Um suspeito pode ser reincidente apenas se certos critérios forem atendidos (mesmo tipo de crime, intervalo de tempo, região).

  • Alertas devem ser priorizados com base em risco calculado — não apenas volume.

Passo 1: Identificar o núcleo do domínio

Nem tudo no sistema é igualmente complexo. O núcleo está na análise de padrões e cálculo de risco. É ali que o DDD traz mais valor.

O resto (cadastro de usuários, autenticação, logs) é domínio genérico — pode ser resolvido com CRUD ou bibliotecas prontas.

Passo 2: Criar linguagem ubíqua

Reuniões com analistas criminais revelaram termos críticos:

  • Ocorrência: registro de um evento (roubo, homicídio, etc.).

  • Envolvido: pessoa ligada a uma ocorrência (papel: vítima, suspeito, testemunha).

  • Perfil de Risco: cálculo baseado em histórico, localização e tipo de crime.

  • Alerta Prioritário: ocorrência que ultrapassa um threshold de risco.

Esses termos entraram no código exatamente como foram usados pelos especialistas. Nada de User, Incident, RiskScore genéricos. O código fala a língua do domínio.

Passo 3: Modelar aggregates mínimos viáveis

Um aggregate é um cluster de objetos tratados como uma unidade. No sistema criminal, o aggregate central é Ocorrencia:

php
 
// pseudocódigo PHP
class Ocorrencia 
{
    private OcorrenciaId $id;
    private array $envolvidos; // lista de Envolvido
    private PerfilRisco $risco;
    private Status $status;

    public function adicionarEnvolvido(Envolvido $envolvido): void 
    {
        // Regra: não pode haver mais de 1 vítima por ocorrência
        $vítimas = array_filter($this->envolvidos, fn($e) => $e->papel() === 'VITIMA');
        if ($envolvido->papel() === 'VITIMA' && count($vítimas) >= 1) {
            throw new DomínioException('Ocorrência já possui uma vítima registrada');
        }
        $this->envolvidos[] = $envolvido;
    }

    public function calcularRisco(): PerfilRisco 
    {
        // Regra complexa: tipo de crime + histórico do suspeito + localização
        $fatores = $this->coletarFatoresDeRisco();
        return new PerfilRisco($fatores);
    }
}

Note: o aggregate carrega regras, não apenas dados. A lógica de "apenas uma vítima por ocorrência" está protegida dentro do modelo — não espalhada em controllers ou services.

Padrões táticos essenciais (sem exagero)

Nem todo padrão do DDD precisa ser aplicado no dia 1. Comece pelo essencial:

Value Objects para validação e invariantes

php
 
class Endereco 
{
    public function __construct(
        private string $logradouro,
        private string $numero,
        private string $cep
    ) {
        if (!preg_match('/^\d{5}-\d{3}$/', $cep)) {
            throw new DomínioException('CEP inválido');
        }
    }
}

Um Endereco não tem identidade — é definido pelos seus atributos. E carrega validação intrínseca.

Entities quando há identidade e ciclo de vida

php
 
class Envolvido 
{
    public function __construct(
        private EnvolvidoId $id,
        private string $nome,
        private Papel $papel // VITIMA, SUSPEITO, TESTEMUNHA
    ) {}

    public function papel(): Papel 
    {
        return $this->papel;
    }
}
Envolvido tem identidade única (mesmo que dois tenham o mesmo nome) e pode mudar de papel ao longo do tempo.

Repositories como abstração de persistência

php
 
interface OcorrenciaRepository 
{
    public function salvar(Ocorrencia $ocorrencia): void;
    public function buscarPorId(OcorrenciaId $id): ?Ocorrencia;
    public function buscarPorRiscoMinimo(int $threshold): array;
}

O repository não é um "DAO com nome bonito". É a interface que o domínio usa para persistir e recuperar aggregates — sem saber se está falando com PostgreSQL, MongoDB ou uma API externa.

O que pular no início

  • Domain Events: úteis, mas adicione quando precisar de desacoplamento real (ex.: enviar alerta para outro sistema).

  • Factories complexas: comece com construtores diretos. Refatore para factories quando a criação ficar confusa.

  • CQRS: só faz sentido se leitura e escrita tiverem modelos radicalmente diferentes (raro no início).

Erros comuns (e como evitar)

1. Criar Value Objects para tudo "porque é DDD"

Nem tudo precisa ser um VO. Um string $nome é suficiente se não houver validação ou comportamento associado. Evans alerta: "O modelo deve ser tão simples quanto possível — mas não mais simples."

2. Confundir DDD com Clean Architecture ou Hexagonal

DDD é sobre o que modelar. Clean/Hexagonal são sobre como organizar o código. São complementares, mas não iguais. Você pode fazer DDD em uma estrutura tradicional de MVC — desde que o domínio carregue as regras.

3. Ignorar o domínio e focar na infraestrutura

Se seu código passa mais tempo configurando Docker, Kubernetes e pipelines do que modelando regras de negócio, você não está fazendo DDD. Está fazendo DevOps com distração.

Quando DDD realmente vale a pena

DDD não é bala de prata. Mas é a melhor ferramenta que temos para domínios complexos. Aplique quando:

  • Regras de negócio não triviais: cálculos, invariantes, fluxos condicionais.

  • Evolução de longo prazo: o sistema vai mudar por anos, com múltiplos times.

  • Linguagem crítica: erros de comunicação entre devs e negócio geram bugs caros.

No sistema de análise criminal, DDD evitou:

  • Ocorrências com múltiplas vítimas (bug silencioso que distorcia estatísticas).

  • Cálculos de risco inconsistentes (cada analista usava critérios diferentes).

  • Refatorações constantes (o modelo estabilizou após 2–3 iterações).

DDD como investimento em clareza

Eric Evans escreveu o Blue Book há mais de 20 anos. Mas a mensagem central permanece: software é feito para resolver problemas do mundo real. Se o modelo não reflete esses problemas, o software vai falhar — independente de quão bonita seja a arquitetura.

DDD não é sobre criar diagramas. É sobre entender o domínio o suficiente para modelá-lo no código. E isso vale a pena em sistemas pequenos, médios e grandes.

A pergunta que fica não é "meu sistema é grande o suficiente para DDD?". É: "meu domínio é complexo o suficiente para merecer um modelo bem feito?"

Se a resposta for sim — e na maioria dos casos reais, é —, comece pequeno. Modele um aggregate. Defina uma linguagem ubíqua. Proteja as invariantes. O resto vem com o tempo.

 

Leitura recomendada:
Evans, Eric. Domain-Driven Design: Tackling Complexity in the Heart of Software. Addison-Wesley, 2004. (Capítulos 1–4 para fundamentos; 5–6 para padrões táticos.)

  • Compartilhe esse post

Discussão e Comentários

Olá, deixe seu comentário para DDD não é (só) para sistemas gigantes: como aplicar Domain-Driven Design em projetos reais sem burocracia

Enviando Comentário Fechar :/