Home / Software / DDD: o que é Domain-Driven Design e como implementar?

DDD: o que é Domain-Driven Design e como implementar?

Entenda o que é Domain-Driven Design

Um sistema de faturamento que o time de negócio chama de “nota fiscal” mas o código trata como Document. Um processo que os especialistas descrevem como “aprovar pedido” mas que no banco de dados vira updateOrderStatus(1). Uma lógica de desconto que vive em três lugares diferentes do sistema porque ninguém decidiu onde ela “pertence”.

Esses descompassos entre a linguagem do negócio e a linguagem do código são sintomas de um problema que Eric Evans identificou no início dos anos 2000: a maioria das falhas em projetos de software complexos não vem de limitações técnicas, mas de um desalinhamento profundo entre a forma como o domínio de negócio funciona e a forma como o software o representa.

Domain-Driven Design (DDD) nasceu como resposta direta a esse problema. Evans publicou o livro fundador da abordagem em 2003 — Domain-Driven Design: Tackling Complexity in the Heart of Software — e estabeleceu um conjunto de princípios, padrões e práticas para alinhar o modelo de software com o modelo de negócio, eliminando a tradução constante entre o mundo técnico e o mundo do domínio.

Neste guia, você vai entender o que é DDD e que problemas ele resolve, conhecer os três princípios fundamentais (modelagem do domínio, linguagem ubíqua e Bounded Contexts), dominar os cinco componentes táticos (Entidades, Value Objects, Agregados, Repositórios e Serviços de Domínio), aprender a diferença entre design estratégico e design tático, e descobrir quando DDD faz sentido e quando pode ser excessivo.

O que é Domain-Driven Design?

Domain-Driven Design é uma abordagem de desenvolvimento de software que coloca o domínio de negócio no centro de todas as decisões de design. Domínio, aqui, significa o problema que o software resolve — as regras, processos, entidades e conceitos do negócio que o sistema precisa representar e operar.

A premissa central do DDD é simples: software que não reflete fielmente o domínio que resolve falha, não importa quão tecnicamente sofisticado seja. Um sistema de gestão hospitalar que modela “pacientes” de forma que faça sentido para desenvolvedores mas não para médicos e enfermeiros vai gerar bugs de lógica de negócio que nenhum teste unitário detecta — porque os testes também foram escritos na linguagem errada.

DDD resolve três categorias de problemas sistêmicos:

Comunicação ineficiente entre times: a desconexão entre equipes técnicas e especialistas de negócio gera requisitos mal interpretados, funcionalidades entregues erradas e ciclos infinitos de retrabalho. O DDD cria uma linguagem comum — a linguagem ubíqua — que elimina a tradução constante e as ambiguidades que crescem nessa lacuna.

Sistemas altamente acoplados e difíceis de manter: sem uma estrutura que reflita os limites naturais do domínio, sistemas complexos se tornam monolitos onde tudo depende de tudo. O DDD introduz Bounded Contexts que modulam o sistema de acordo com os limites reais do negócio, não com limites técnicos arbitrários.

Dificuldade de adaptação a mudanças: negócios mudam — e software que não reflete a estrutura do negócio resiste a essas mudanças de forma proporcional ao desalinhamento acumulado. Um modelo de domínio bem construído evolui junto com o negócio, porque representa a mesma realidade.

💡 Dica: DDD não é uma metodologia de desenvolvimento ágil nem um padrão de arquitetura — é uma filosofia de design que pode coexistir com Scrum, Kanban, arquitetura hexagonal, microserviços ou monólitos bem estruturados. O DDD define o que modelar e como pensar sobre o modelo; a arquitetura define onde esse modelo vive.

Os três princípios fundamentais do DDD

1. Modelagem do Domínio

A modelagem do domínio é o processo contínuo de entender, capturar e representar os conceitos, processos e regras essenciais do negócio em um modelo que guia o desenvolvimento do software. Não acontece uma vez no início do projeto — acontece ao longo de todo o ciclo de vida do sistema, à medida que o entendimento do domínio se aprofunda e o negócio evolui.

Esse processo exige engajamento genuíno com especialistas do domínio. Não basta ler documentação e escrever tickets — os desenvolvedores precisam entender como o negócio funciona de verdade, quais são suas regras, suas exceções, seus casos de borda e sua terminologia própria. Técnicas como Event Storming (mapeamento colaborativo de eventos de negócio) e sessões regulares de refinamento com especialistas criam esse canal de conhecimento.

A colaboração entre desenvolvedores e especialistas do domínio é o motor do DDD. Especialistas trazem o conhecimento profundo de como o negócio funciona; desenvolvedores trazem a expertise de como modelar esse conhecimento em estruturas de software. Quando essa colaboração é contínua e iterativa, o modelo melhora progressivamente — e o software melhora junto.

Ferramentas visuais como diagramas, protótipos e modelos compartilhados tornam conceitos abstratos tangíveis para todos na sala. O objetivo é que tanto o gerente de produto quanto o desenvolvedor backend olhem para o mesmo diagrama e vejam a mesma realidade — sem tradução necessária.

2. Ubiquitous Language (Linguagem Ubíqua)

A linguagem ubíqua é uma das contribuições mais práticas e mais transformadoras do DDD. Trata-se de uma linguagem comum, compartilhada por todos os membros do projeto — desenvolvedores, testadores, Product Owners, especialistas do domínio e stakeholders — e usada de forma consistente em absolutamente tudo: conversas, documentos de requisitos, código-fonte, testes e mensagens de commit.

A chave é a consistência. Quando o especialista de negócio fala em “fatura”, o desenvolvedor não pode chamar isso de invoice no código e de document no banco de dados. Quando o processo de negócio descreve “cancelar pedido”, o método no código deve se chamar cancelOrder, não updateOrderStatusToCancelled. Cada divergência entre a linguagem do domínio e a linguagem do código é uma porta de entrada para bugs de lógica de negócio.

Criar a linguagem ubíqua começa com um glossário colaborativo que reúne desenvolvedores e especialistas para definir e acordar os termos do domínio. Esse glossário não fica em um documento esquecido — vira o vocabulário do código, dos testes e de todas as conversas sobre o sistema.

Manter a linguagem ubíqua requer vigilância contínua. Quando um novo termo aparece nas conversas de negócio, precisa entrar no código. Quando o código usa um termo diferente do que o negócio usa, a inconsistência precisa ser corrigida — não justificada com “mas todo mundo sabe que é a mesma coisa”.

⚠️ Atenção: linguagem ubíqua não é tradução literal de termos de negócio para inglês no código. É garantir que os conceitos do domínio tenham representação direta no modelo de software, com os mesmos nomes que os especialistas usam no dia a dia — independentemente do idioma escolhido para o código.

3. Bounded Contexts (Contextos Delimitados)

Bounded Contexts são talvez o conceito mais importante e menos intuitivo do DDD. A ideia central é que, em sistemas complexos, o mesmo termo pode ter significados diferentes em contextos diferentes — e tentar manter um único modelo global que cubra todos os contextos gera contradições que progressivamente destroem a coerência do sistema.

Em um sistema de e-commerce, “produto” significa uma coisa no contexto de catálogo (nome, descrição, fotos, categorias), outra no contexto de estoque (SKU, quantidade disponível, localização no armazém) e outra no contexto de faturamento (preço, alíquota de imposto, código fiscal). Tentar criar um único objeto Produto que sirva para todos esses contextos produz uma classe com dezenas de responsabilidades, campos opcionais e lógica condicional que variam por contexto.

Bounded Contexts resolvem isso criando limites explícitos dentro dos quais um modelo específico se aplica. Cada contexto tem sua própria representação do que “produto” significa naquele contexto, sua própria linguagem ubíqua e sua própria lógica. Os contextos se comunicam através de interfaces bem definidas, sem compartilhar o modelo interno.

Exemplos de Bounded Contexts:

E-commerce: “Catálogo de Produtos” (informações e busca), “Carrinho de Compras” (seleção e armazenamento temporário), “Gestão de Pedidos” (processamento e acompanhamento), “Faturamento” (emissão de notas e gestão financeira) e “Estoque” (controle de inventário) — cada um com seu próprio modelo, mesmo que todos falem sobre “produtos” e “pedidos”.

Banco: “Gestão de Contas” (abertura, encerramento, movimentações), “Empréstimos” (solicitação, análise de crédito, amortização) e “Investimentos” (portfólio, alocações, rentabilidade) — contextos com lógicas radicalmente diferentes, mesmo que todos operem sobre a conta do mesmo cliente.

A definição de Bounded Contexts é uma das decisões de design mais impactantes em um projeto DDD — e uma das mais difíceis de fazer bem. Contextos mal delimitados criam fronteiras que o negócio não reconhece, gerando integrações forçadas e acoplamento desnecessário.

Os cinco componentes táticos do DDD

Entidades

Entidades são objetos que possuem identidade única e persistente — sua identidade não muda mesmo que seus atributos mudem ao longo do tempo. Um cliente continua sendo o mesmo cliente se mudar de endereço, de telefone ou de nome. O que o identifica como aquele cliente específico é sua identidade (um ID único), não suas propriedades.

Exemplos de entidades:

  • Cliente: identificado por um número único de cliente, persiste através de mudanças de dados pessoais
  • Pedido: identificado por um número de pedido, rastreado desde a criação até a entrega ou cancelamento

Como identificar uma entidade: o conceito precisa ser rastreado ao longo do tempo? Precisa de identidade que persista mesmo quando seus atributos mudam? Se sim, é uma entidade.

Como modelar: defina a identidade (geralmente imutável e gerada na criação), liste os atributos que descrevem o estado atual e identifique as operações que a entidade suporta. Garanta que operações que afetam o estado da entidade respeitem suas invariantes — as regras de negócio que sempre devem ser verdadeiras.

Value Objects (Objetos de Valor)

Value Objects são objetos definidos apenas por seus atributos — não têm identidade própria e são imutáveis. Dois Value Objects com os mesmos atributos são considerados iguais e intercambiáveis. Quando o estado precisa mudar, o Value Object antigo é descartado e um novo é criado.

A diferença fundamental entre Entidade e Value Object:

EntidadeValue Object
IdentidadeÚnica e persistenteNão tem
MutabilidadePode mudar de estadoImutável
IgualdadePor identidade (ID)Por atributos

Exemplos de Value Objects:

  • Endereço: rua, cidade, estado e CEP definem completamente o endereço. Dois endereços com os mesmos atributos são o mesmo endereço.
  • Dinheiro: moeda e valor definem completamente a quantidade monetária. R$100 é R$100, independentemente de “qual” R$100.
  • Período: data de início e data de fim definem completamente o período.

Value Objects capturam conceitos do domínio que têm significado próprio mas não precisam de identidade rastreável. Usá-los em vez de tipos primitivos (strings, numbers) torna o modelo mais expressivo e mais seguro — um CPF que valida seu próprio formato é melhor do que uma string que pode conter qualquer coisa.

Agregados

Agregados são grupos de Entidades e Value Objects tratados como uma unidade coesa para fins de consistência de dados. Cada Agregado tem uma Entidade raiz — a única que o mundo externo pode referenciar diretamente. Todas as modificações ao Agregado passam pela raiz, que garante que as invariantes do grupo sejam sempre respeitadas.

Princípios para design de Agregados:

  • A raiz do Agregado é a única porta de entrada para modificar o estado interno
  • Objetos externos só mantêm referências à raiz, nunca a membros internos
  • O Agregado define os limites de consistência transacional — uma transação não deve afetar múltiplos Agregados simultaneamente (se precisar, é sinal de que os limites estão errados)
  • Agregados devem ser pequenos — grandes o suficiente para garantir consistência, pequenos o suficiente para não criar gargalos de performance

Exemplo: em um sistema de pedidos, o Pedido é a raiz do Agregado. Ele gerencia ItensDoPedido (Entidades ou Value Objects internos) e EnderecoDeEntrega (Value Object). Ninguém manipula um ItemDoPedido diretamente — sempre passa pelo Pedido, que valida regras como “o pedido não pode ter mais de 50 itens” ou “não se pode adicionar item a um pedido cancelado”.

Repositórios

Repositórios abstraem a persistência dos Agregados. Fornecem uma interface que parece uma coleção em memória — save, findById, findByEmail — mas por baixo pode ser qualquer mecanismo de persistência: banco relacional, document store, cache, API externa.

O domínio não sabe nem se importa com como os dados são persistidos. Essa separação mantém a lógica de negócio limpa, livre de SQL, queries específicas de banco ou detalhes de infraestrutura.

Boas práticas:

  • Um Repositório por tipo de Agregado — PedidoRepository cuida de Pedido, ClienteRepository cuida de Cliente
  • A interface define operações do domínio (findPedidosPendentesDoCliente), não operações técnicas (selectWhereStatusAndClienteId)
  • A implementação concreta fica na camada de infraestrutura, não no domínio
  • Operações de repositório respeitam limites transacionais do Agregado

Serviços de domínio

Serviços de Domínio encapsulam lógica de negócio que não pertence naturalmente a nenhuma Entidade ou Value Object específico. Quando uma operação envolve múltiplas Entidades, múltiplos Agregados ou precisa de informações externas ao domínio, um Serviço de Domínio é o lugar certo para ela.

Use um Serviço de Domínio quando:

  • A operação envolve múltiplas Entidades ou Agregados
  • A lógica representa uma operação importante do domínio mas não “pertence” a nenhum objeto específico
  • A operação precisa coordenar entre diferentes partes do modelo

Exemplos:

  • Processamento de pagamento: coordena entre o Pedido (para atualizar status), a Conta do Cliente (para verificar saldo ou aplicar crédito) e o gateway de pagamento externo — nenhuma dessas Entidades deve conter essa lógica isoladamente
  • Cálculo de frete: depende de endereço (Value Object do Pedido), peso e dimensões dos itens (do Catálogo) e regras de frete (tabelas externas) — uma operação que cruza múltiplos contextos

💡 Dica: a distinção entre Serviço de Domínio e Serviço de Aplicação é importante. Serviços de Domínio contêm lógica de negócio — regras do domínio. Serviços de Aplicação orquestram casos de uso — chamam Repositórios, invocam Serviços de Domínio, publicam eventos. Lógica de negócio no Serviço de Aplicação é o sinal de que ela deveria estar no domínio.

Design estratégico vs. Design tático

O DDD opera em dois níveis que se complementam:

Design estratégico define a arquitetura de alto nível: identifica os Bounded Contexts, mapeia as relações entre eles e decide como diferentes partes do sistema se comunicam. O Context Map é a ferramenta central — uma representação visual dos contextos e de como se relacionam.

As relações entre contextos seguem padrões específicos:

  • Conformista: um contexto adota o modelo do outro sem negociação — útil quando um contexto tem pouca influência sobre como o outro foi projetado
  • Cliente-Fornecedor: um contexto fornece serviços ao outro com acordos formais sobre a interface e os compromissos de serviço
  • Parceria: dois contextos colaboram estreitamente e coordenam mudanças mutuamente
  • Caminhos Separados: contextos que operam de forma independente, sem integração necessária

Design tático define como o modelo é construído dentro de cada Bounded Context: quais são as Entidades, os Value Objects, os Agregados, os Repositórios e os Serviços de Domínio. É aqui que vivem os componentes descritos na seção anterior.

Técnicas de design tático incluem:

Event Storming: workshop colaborativo onde desenvolvedores e especialistas mapeiam eventos de negócio em um quadro, identificando gatilhos, atores, comandos e reações. Produce um entendimento compartilhado do fluxo de processos do domínio e serve como ponto de partida para identificar Agregados e Bounded Contexts.

Story Mapping: cria uma visão compartilhada das funcionalidades priorizadas pelo valor para o usuário, conectando o backlog técnico aos objetivos de negócio.

Diagramas C4: representam a arquitetura em quatro níveis — contexto, containers, componentes e código — facilitando a comunicação entre stakeholders com diferentes níveis de detalhe técnico.

Benefícios e desafios do DDD

Quando DDD entrega mais valor?

Alinhamento genuíno entre negócio e tecnologia: quando a linguagem ubíqua está estabelecida e o modelo de domínio reflete o negócio real, desenvolvedores e especialistas falam a mesma língua. Isso reduz retrabalho causado por mal-entendidos, acelera o refinamento de requisitos e produz software que faz o que o negócio realmente precisa.

Gerenciamento eficaz de complexidade: Bounded Contexts modulam sistemas complexos de acordo com limites naturais do negócio. Cada contexto pode evoluir de forma independente, sem que mudanças em uma área do domínio propaguem efeitos colaterais imprevisíveis para outras áreas.

Código mais expressivo e manutenível: Entidades, Value Objects e Agregados nomeados com a terminologia do domínio produzem código que se documenta a si mesmo. Um desenvolvedor que entra no projeto encontra um modelo que descreve o negócio, não uma sopa de classes técnicas sem conexão com o mundo real.

Desafios da adoção

Custo de implementação inicial: definir Bounded Contexts, criar a linguagem ubíqua e modelar todos os componentes táticos requer investimento significativo de tempo e colaboração. Para sistemas simples ou projetos de curto prazo, esse custo pode não ser justificado.

Curva de aprendizado íngreme: DDD tem muitos conceitos interconectados que precisam fazer sentido juntos para que a abordagem funcione. Times sem experiência prévia com DDD frequentemente cometem erros comuns — Agregados muito grandes, Bounded Contexts mal delimitados, linguagem ubíqua que vira apenas jargão técnico.

Dependência de colaboração contínua: DDD só funciona quando especialistas do domínio participam ativamente e de forma contínua. Times onde essa colaboração é difícil — por distância física, agendas incompatíveis ou cultura de silos — enfrentam dificuldades para manter o modelo alinhado com a realidade do negócio.

Refatoração de sistemas legados: migrar um sistema existente para DDD é complexo e arriscado. Identificar Bounded Contexts em código que não os tinha, introduzir linguagem ubíqua em um codebase com nomenclatura inconsistente, e extrair Agregados de um modelo de dados plano exige cuidado extremo para não introduzir regressões.

⚠️ Atenção: DDD não é para todo projeto. Sistemas CRUD simples, aplicações de pequeno porte com domínio bem compreendido, ou projetos de vida curta raramente justificam o investimento em DDD completo. A complexidade que o DDD gerencia só se manifesta em domínios genuinamente complexos com muitas regras de negócio, múltiplas áreas funcionais e equipes que precisam trabalhar em paralelo.

Ferramentas para DDD

Modelagem e colaboração

Miro: plataforma de quadro branco digital que suporta Event Storming, Context Mapping e modelagem colaborativa em tempo real. Funciona bem para times distribuídos e permite que desenvolvedores e especialistas do domínio colaborem visualmente na mesma sessão.

MURAL: alternativa ao Miro com funcionalidades similares de quadro branco colaborativo. Suporta templates específicos para Event Storming e facilita sessões de descoberta de domínio.

Modelagem UML

Enterprise Architect: ferramenta robusta para criação de diagramas UML que representam entidades, agregados e relações entre componentes do modelo de domínio. Suporta exportação e integração com outros sistemas.

StarUML: alternativa mais leve ao Enterprise Architect, com suporte a diagramas de classe e outros diagramas UML úteis para DDD. Acessível para times que precisam de modelagem visual sem a complexidade de ferramentas enterprise.

Mapas de contexto e Bounded Contexts

Context Mapper: ferramenta específica para DDD que permite criar e gerenciar mapas de contexto usando uma DSL declarativa. Gera diagramas visuais dos Bounded Contexts e das relações entre eles, e integra com ferramentas de modelagem UML.

Structurizr: implementação do modelo C4 que representa arquitetura em diferentes níveis de abstração. Permite definir a estrutura do sistema como código e gerar diagramas automaticamente — útil para manter a documentação arquitetural sincronizada com o sistema real.

Perguntas frequentes sobre DDD

DDD é adequado para microserviços?


Sim — e os dois conceitos se complementam muito bem. Bounded Contexts fornecem uma base natural para definir os limites de microserviços: cada contexto delimitado pode se tornar um ou mais microserviços. A vantagem é que os limites entre serviços refletem limites reais do domínio de negócio, não apenas limites técnicos. O risco é que Bounded Contexts mal definidos produzam microserviços com fronteiras erradas — o que cria integrações caras e acoplamento oculto entre serviços. DDD bem feito melhora a arquitetura de microserviços; DDD mal feito pode criar uma distribuição do problema em vez de uma solução.

Qual a diferença entre DDD e Clean Architecture?


DDD e Clean Architecture operam em níveis diferentes e se complementam. DDD define como modelar o domínio de negócio — como identificar Entidades, Agregados, Bounded Contexts e como criar a linguagem ubíqua. Clean Architecture define como organizar o código em camadas de forma que o domínio seja independente de infraestrutura, frameworks e UI. Um projeto pode usar DDD para modelar o domínio e Clean Architecture para organizar como esse modelo se relaciona com o restante do sistema. Os dois juntos produzem código bem estruturado tanto do ponto de vista de domínio quanto de arquitetura.

É necessário implementar todos os componentes do DDD (Entidades, Value Objects, Agregados, Repositórios e Serviços) para fazer DDD?


Não — e tentar implementar tudo de uma vez é um dos erros mais comuns na adoção de DDD. Eric Evans e Vaughn Vernon recomendam uma abordagem gradual: começar pelos conceitos estratégicos (linguagem ubíqua, Bounded Contexts) e introduzir os padrões táticos (Entidades, Agregados) onde fazem mais sentido para o domínio específico. DDD é um conjunto de ferramentas, não uma checklist. Usar as ferramentas corretas nos problemas certos é mais valioso do que implementar todas elas de forma uniforme.

Como começar a implementar DDD em um sistema legado?


A abordagem mais segura é incremental. Comece criando um Bounded Context novo — uma área do sistema que vai ser construída do zero ou significativamente refatorada — e aplique DDD nele. Use o Strangler Fig Pattern: o novo contexto “envolve” progressivamente o código legado, substituindo partes antigas por partes bem modeladas. Ao mesmo tempo, documente a linguagem ubíqua do domínio com os especialistas, mesmo que o código legado ainda não a reflita. Isso cria a base para refatorações futuras guiadas por uma linguagem compartilhada.

DDD é compatível com metodologias ágeis como Scrum?


Totalmente — e a combinação é poderosa. Sprints criam o ritmo de iteração que o DDD precisa para refinar o modelo progressivamente. Event Storming se encaixa naturalmente nas sessões de refinamento de backlog. A linguagem ubíqua melhora a qualidade das histórias de usuário — quando todos usam os mesmos termos, a definição de done fica mais clara e as estimativas ficam mais precisas. O risco é quando a pressão por velocidade do Scrum leva a pular as conversas de domínio necessárias para manter o modelo alinhado com o negócio. DDD exige investimento de tempo em colaboração que Scrum mal gerido pode subestimar.

Conclusão

Domain-Driven Design resolve um problema que software técnico excelente não resolve sozinho: o desalinhamento entre o modelo mental do negócio e o modelo de dados do sistema. Quando esse desalinhamento cresce — e ele sempre cresce sem intervenção consciente — o custo de cada mudança no software aumenta, os bugs de lógica de negócio proliferam e a comunicação entre times piora progressivamente.

Três pontos centrais para levar desta leitura: a linguagem ubíqua é o investimento com maior retorno do DDD — começa simples e transforma a qualidade de toda comunicação e de todo código; Bounded Contexts são mais importantes do que os padrões táticos — acertar os limites do domínio vale mais do que implementar Agregados perfeitos em contextos mal definidos; e DDD tem custo real de adoção que só se justifica em domínios genuinamente complexos com múltiplas áreas funcionais e equipes que precisam evoluir o sistema ao longo do tempo.

Para aprofundar: leia Domain-Driven Design de Eric Evans para os fundamentos, Implementing Domain-Driven Design de Vaughn Vernon para aplicação prática detalhada, e Domain-Driven Design Distilled do mesmo Vernon para uma introdução mais acessível se você está começando agora.

Deixe um Comentário

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *