O DDD Domain Driven Design é a abordagem que alinha código e regras de negócio no mesmo vocabulário. Em vez de modelar sistemas a partir de tabelas ou frameworks, você começa pelo domínio. Dessa forma, especialistas e desenvolvedores falam a mesma língua. Por isso, o DDD Domain Driven Design ficou conhecido como o método de Eric Evans para domar projetos complexos sem perder clareza.
Neste guia prático, você vai entender o que é DDD, por que ele continua relevante em 2026 e quais conceitos sustentam o modelo. Além disso, vamos mostrar um exemplo real em TypeScript com Entity, Value Object e Aggregate. Em seguida, você vai descobrir quando aplicar DDD e quando ele só atrapalha. Portanto, ao final do artigo, terá uma base sólida para começar.
O que é DDD Domain Driven Design?
O DDD Domain Driven Design é uma filosofia de modelagem proposta por Eric Evans em 2003. Ela coloca o domínio do negócio no centro de toda decisão técnica. Em outras palavras, a estrutura do código reflete diretamente como o negócio funciona. Por isso, equipes que adotam DDD escrevem classes, funções e módulos com nomes idênticos aos que aparecem nas reuniões com especialistas.
A ideia central é simples. Primeiro, você conversa com quem entende do negócio. Em seguida, identifica entidades, ações e regras que importam de verdade. Depois, traduz tudo isso em um modelo executável. Dessa forma, o software vira um espelho fiel da operação real. Inclusive, esse alinhamento entre linguagem e código é o que o DDD chama de ubiquitous language.
Vale destacar que DDD não é framework nem tecnologia. Pelo contrário, é um conjunto de práticas estratégicas e táticas. O site oficial de Eric Evans reúne os padrões originais do livro azul. Por outro lado, autores como Vaughn Vernon expandiram esses padrões em obras mais recentes. Portanto, DDD funciona em qualquer linguagem, do Java ao TypeScript.
Por que usar DDD Domain Driven Design em projetos modernos?
Adotar DDD Domain Driven Design faz sentido quando o negócio é complexo. Em sistemas de e-commerce, fintechs ou logística, as regras mudam direto. Por exemplo, calcular frete envolve dezenas de condições. Sem um modelo claro, esse tipo de regra se espalha pelo código e vira pesadelo. Dessa forma, manutenções simples acabam quebrando funcionalidades distantes.
O DDD organiza essa complexidade em fronteiras explícitas. Primeiro, você separa o sistema em contextos delimitados. Em seguida, cada contexto recebe seu próprio modelo de domínio. Por isso, mudanças locais não afetam o resto. Além disso, novos desenvolvedores entendem rápido onde mexer. Martin Fowler explica em seu blog que essa divisão é o coração do DDD estratégico.
Outra vantagem é a comunicação. Quando código e negócio falam a mesma língua, reuniões ficam mais curtas. Portanto, o time entrega mais rápido e com menos retrabalho. Em projetos com arquitetura na AWS, cada microsserviço pode representar um bounded context próprio. Em breve teremos um artigo sobre System Design que complementa esses conceitos.
Conceitos fundamentais do DDD Domain Driven Design

O DDD Domain Driven Design divide seus padrões em dois grupos. Primeiro, vem o design estratégico, focado em contextos e linguagem. Em seguida, surge o design tático, que trata de blocos como Entity, Value Object e Aggregate. Veja a seguir os conceitos que todo desenvolvedor precisa dominar antes de aplicar DDD.
Ubiquitous Language é o vocabulário compartilhado entre time técnico e especialistas. Cada termo tem significado único dentro do contexto. Por exemplo, “Pedido” no contexto de vendas é diferente de “Pedido” no contexto de logística. Dessa forma, ambiguidades desaparecem do código e da conversa.
Bounded Context é a fronteira onde um modelo faz sentido. Cada contexto tem seu próprio modelo, sua própria linguagem e suas próprias regras. Em projetos grandes, contextos diferentes podem rodar como microsserviços separados. Por isso, integrações entre contextos exigem mapeamentos explícitos.
Entity é um objeto com identidade única que persiste no tempo. Um cliente, por exemplo, continua sendo o mesmo cliente mesmo que mude de endereço. Já o Value Object não tem identidade. Ele é definido apenas pelos seus atributos. Endereço, dinheiro e CPF são exemplos clássicos de value objects.
Aggregate é um cluster de entities e value objects tratado como unidade. Toda mudança passa pela aggregate root, que garante as invariantes do grupo. Por outro lado, o Repository abstrai o acesso a aggregates como se fossem coleções em memória. Finalmente, o Domain Service encapsula regras que não pertencem a nenhuma entity específica.
Quando aplicar (e quando não aplicar) DDD Domain Driven Design
O DDD Domain Driven Design brilha em domínios complexos e duradouros. Por exemplo, sistemas bancários, ERPs corporativos e plataformas de seguros se beneficiam muito. Nesses casos, as regras de negócio são o ativo mais valioso. Portanto, investir tempo modelando o domínio paga a conta no longo prazo.
Por outro lado, DDD pode ser exagero em projetos simples. CRUDs básicos, MVPs descartáveis e scripts internos não precisam de aggregate roots. Inclusive, aplicar DDD nesses casos gera overhead desnecessário e o time perde velocidade sem ganhar clareza.
Outro sinal de que vale aplicar DDD é a presença de especialistas disponíveis. Modelagem rica depende de conversas frequentes com quem domina o negócio. Sem esse acesso, o time acaba inventando regras e o modelo perde valor. Por isso, antes de adotar DDD, garanta acesso direto a stakeholders. Caso contrário, prefira abordagens mais simples como arquitetura em camadas tradicional.
Exemplo prático de DDD Domain Driven Design
Para tornar o DDD Domain Driven Design concreto, veja a seguir um exemplo em TypeScript. Vamos modelar um pedido simples com seus itens. Primeiro, definimos um Value Object chamado Money. Em seguida, criamos a Entity OrderItem. Por fim, construímos o Aggregate Root Order que coordena tudo.
// Value Object: imutável, sem identidade
class Money {
constructor(
public readonly amount: number,
public readonly currency: string
) {
if (amount < 0) throw new Error('Valor não pode ser negativo')
}
add(other: Money): Money {
if (this.currency !== other.currency) {
throw new Error('Moedas diferentes')
}
return new Money(this.amount + other.amount, this.currency)
}
equals(other: Money): boolean {
return this.amount === other.amount && this.currency === other.currency
}
}
// Entity: tem identidade própria (id)
class OrderItem {
constructor(
public readonly id: string,
public readonly productId: string,
public quantity: number,
public readonly price: Money
) {}
subtotal(): Money {
return new Money(this.price.amount * this.quantity, this.price.currency)
}
}O Money é imutável: qualquer operação retorna nova instância. Dessa forma, evitamos bugs de compartilhamento de estado. Já o OrderItem tem identidade própria via id. Por isso, dois itens com mesmos atributos ainda são considerados distintos. A seguir, veja o Aggregate Root que costura essas peças.
// Aggregate Root: ponto único de entrada
class Order {
private items: OrderItem[] = []
private status: 'draft' | 'placed' | 'paid' = 'draft'
constructor(
public readonly id: string,
public readonly customerId: string
) {}
addItem(item: OrderItem): void {
if (this.status !== 'draft') {
throw new Error('Só pedidos em rascunho aceitam novos itens')
}
this.items.push(item)
}
total(): Money {
return this.items.reduce(
(acc, item) => acc.add(item.subtotal()),
new Money(0, 'BRL')
)
}
place(): void {
if (this.items.length === 0) {
throw new Error('Pedido vazio não pode ser fechado')
}
this.status = 'placed'
}
}O Order é a aggregate root. Toda mudança nos itens passa por ele e, dessa forma, as invariantes ficam protegidas. Por exemplo, você nunca consegue adicionar item em pedido já fechado. Inclusive, Vaughn Vernon recomenda em seu artigo “Effective Aggregate Design” manter aggregates pequenas. Aggregates enormes prejudicam performance e geram conflitos transacionais.
Ferramentas e padrões complementares ao DDD
Na prática, o DDD raramente vive sozinho. Em geral, ele se combina com padrões arquiteturais que reforçam seus princípios. Primeiro, a Clean Architecture separa domínio das camadas de infraestrutura. Em seguida, o CQRS divide leituras e escritas em modelos distintos. Dessa forma, consultas complexas não poluem o modelo de domínio.
Outro casamento natural é com Event Sourcing. Em vez de salvar o estado atual, você grava cada mudança como evento. Por isso, todo histórico fica auditável. Inclusive, a Microsoft documenta esse padrão em suas referências de microsserviços. Além disso, a ThoughtWorks reforça em seu blog que DDD segue como base teórica.
No ecossistema TypeScript, frameworks como NestJS oferecem suporte natural para DDD. Já em Java, Spring Boot continua sendo a escolha clássica. Por fim, ferramentas de modelagem como Event Storming ajudam workshops com especialistas. Dessa forma, descobrir bounded contexts vira um exercício colaborativo e visual.
Em suma, o DDD continua sendo a referência para sistemas complexos em 2026. Sua força não está em padrões específicos, mas no alinhamento entre código e negócio. Portanto, comece pelo vocabulário e só depois pense em classes.
Próximos passos
Lembre-se de que adotar DDD é uma jornada. Por isso, comece pequeno e evolua. Primeiro, leia o livro azul de Eric Evans para entender os fundamentos. Em seguida, pratique modelagem em um projeto real, ainda que pequeno. Depois, organize um Event Storming com seu time. Por fim, refatore um módulo legado aplicando aggregates e value objects. Dessa forma, você ganha experiência sem comprometer entregas em produção.



























