Domain-Driven Design (DDD): por que tantos sistemas se tornam difíceis de evoluir

Entenda o que é Domain-Driven Design (DDD) e por que essa abordagem ajuda a criar sistemas mais claros, organizados e fáceis de evoluir conforme o negócio cresce.

Domain-Driven Design (DDD): por que tantos sistemas se tornam difíceis de evoluir
Photo by Jakub Żerdzicki / Unsplash

Todo sistema começa organizado.

No início, existe entusiasmo, código limpo, poucas regras e decisões rápidas. As funcionalidades evoluem com velocidade e o time sente que está no controle.

Então o tempo passa.

Novas demandas chegam. Regras mudam. Exceções aparecem. Integrações aumentam. Diferentes áreas da empresa começam a depender do sistema.

E aquele projeto que parecia simples começa a apresentar sintomas conhecidos:

  • código difícil de entender
  • regras espalhadas em vários lugares
  • funcionalidades que quebram outras partes
  • nomes confusos
  • retrabalho constante
  • medo de alterar algo importante

Na maioria das vezes, o problema não está no framework, no banco de dados ou na linguagem utilizada.

O problema está no fato de que o software deixou de representar corretamente o negócio.

É exatamente para combater isso que existe o Domain-Driven Design (DDD).

O que é Domain-Driven Design?

DDD é uma abordagem de desenvolvimento de software focada em modelar sistemas de acordo com a realidade do negócio.

Em vez de começar perguntando:

  • Qual framework usar?
  • Vai ser microsserviço ou monólito?
  • Qual banco de dados escolher?
  • Como será o deploy?

DDD propõe começar por perguntas mais importantes:

  • Como esse negócio funciona?
  • Quais regras realmente importam?
  • Onde existe complexidade?
  • Quais termos a empresa usa no dia a dia?
  • O que gera valor para o cliente?

Em resumo:

DDD é construir software baseado no domínio, e não apenas na tecnologia.

O termo se popularizou com Eric Evans, autor do livro Domain-Driven Design: atacando as complexidades no coração do software, uma das obras mais influentes da engenharia de software moderna.

O que significa “Domínio”?

Domínio é o contexto de negócio onde o sistema atua.

É o problema real que a empresa precisa resolver.

Exemplos

  • Banco: contas, transferências, crédito, antifraude, investimentos, etc.
  • E-commerce: catálogo, estoque, carrinho, pagamento, entrega, etc.
  • Clínica médica: pacientes, consultas, convênios, prontuário, etc.

Perceba: o domínio não é banco de dados, API ou tela.

O domínio é a operação real do negócio.

Onde muitos sistemas erram

Grande parte dos projetos nasce orientada por estrutura técnica.

O fluxo costuma ser algo assim:

  1. Criar tabelas
  2. Criar CRUD
  3. Criar endpoints
  4. Criar telas
  5. Descobrir regras depois

Isso funciona no começo.

Mas conforme a empresa cresce, começam os problemas.

Exemplo comum

O negócio diz:

Cliente premium pode remarcar sem multa até duas vezes por mês.

O sistema responde com algo assim:

if (customerType == 3 && count < 2) {
   fee = 0;
}

Funciona? Sim.

Representa bem a regra? Não.

Agora veja uma modelagem melhor:

customer.canRescheduleWithoutPenalty();
appointment.reschedule();

Nesse segundo caso, o código comunica intenção.

Ele fala a linguagem do negócio.

Essa diferença parece pequena, mas muda completamente a capacidade de evolução do sistema.

O verdadeiro objetivo do DDD

DDD não existe para “deixar código bonito”.

DDD existe para reduzir a distância entre:

  • quem entende o negócio
  • quem escreve o software

Quando essa distância é grande, surgem sistemas tecnicamente corretos e operacionalmente ruins.

Quando essa distância diminui, o software se torna mais claro, previsível e sustentável.

Linguagem Ubíqua: quando todos falam a mesma língua

Um dos conceitos mais importantes do Domain-Driven Design é a Linguagem Ubíqua (Ubiquitous Language).

A ideia é simples: os termos usados pelo negócio devem ser os mesmos usados pelo time técnico e pelo próprio sistema.

Parece algo pequeno, mas muitos problemas em software começam justamente quando cada área chama a mesma coisa por nomes diferentes.

O comercial fala cliente ativo.
O suporte chama de cliente habilitado.
Na base de dados existe status = 1.
No código alguém criou enabled = true.

Todos tentam falar do mesmo conceito, mas cada um usa uma linguagem diferente. O resultado costuma ser confusão, interpretações erradas e regras implementadas de forma equivocada.

A proposta do DDD é eliminar essa tradução constante.

Se a empresa fala Cliente Inadimplente, esse conceito deve existir claramente no sistema. Se o negócio usa o termo Agendamento Confirmado, o código também deveria refletir isso.

Em vez de algo assim:

status = 7
flag = true
type = 2

O ideal seria algo como:

AppointmentConfirmed
DelinquentCustomer
PremiumPlan

No primeiro exemplo, alguém precisa explicar o sistema. No segundo, o sistema praticamente se explica sozinho.

Esse alinhamento melhora reuniões, reduz erros e torna o código muito mais legível. Em vez de discutir campos e tabelas, o time passa a discutir regras reais do negócio.

Linguagem Ubíqua não é sobre “dar nomes bonitos”. É sobre garantir que o software represente a realidade da empresa com clareza.

E essa é uma das maiores forças do DDD:

quando todos falam a mesma língua, construir software se torna muito mais simples.

DDD não é sobre dados. É sobre comportamento.

Muitos sistemas tratam objetos como recipientes de atributos.

appointment.status = "C";
appointment.date = newDate;
appointment.cancelled = true;

Qualquer camada altera qualquer coisa.

Resultado:

  • inconsistência
  • regras duplicadas
  • bugs difíceis de rastrear

No DDD, objetos importantes carregam comportamento.

appointment.confirm();
appointment.reschedule(newDate);
appointment.cancel(reason);

Agora o próprio domínio protege suas regras.

Por exemplo:

appointment.reschedule(newDate);

Esse método pode validar:

  • antecedência mínima
  • disponibilidade do profissional
  • limite de remarcações
  • bloqueios operacionais

Tudo centralizado no lugar certo.

Conceitos importantes do DDD

Depois de entender a proposta do Domain-Driven Design, é natural surgir a pergunta: como isso aparece na prática dentro do código?

DDD possui alguns blocos fundamentais que ajudam a transformar regras de negócio em software bem modelado. Mais do que padrões técnicos, esses conceitos existem para representar a realidade do domínio com clareza.

Entidades

Entidades são objetos que possuem identidade própria e continuam sendo os mesmos ao longo do tempo, mesmo que seus dados mudem.

Um cliente pode alterar nome, telefone ou e-mail. Ainda assim, continua sendo o mesmo cliente dentro do sistema.

Exemplos comuns:

  • Cliente
  • Pedido
  • Conta bancária
  • Agendamento
Customer customer = new Customer(id);

O que define uma entidade não é apenas seus atributos, mas sua identidade no negócio.

Value Objects

Diferente das entidades, Value Objects não são definidos por identidade, e sim pelo valor que representam.

Dois objetos de e-mail com o mesmo conteúdo são equivalentes. O mesmo vale para dinheiro, endereço ou período de datas.

Exemplos:

  • Email
  • CPF
  • Dinheiro
  • Endereço
Email email = new Email("[email protected]");
Money total = new Money(89.90);

Eles ajudam a deixar o código mais expressivo, seguro e alinhado ao domínio.

Aggregates

Um Aggregate é um conjunto de objetos que precisa manter consistência como uma única unidade.

O exemplo clássico é um pedido. Ele pode conter itens, total, desconto e status, mas tudo deve respeitar regras centralizadas.

order.addItem(product, 2);
order.applyCoupon("BLACK10");
order.confirm();

Nesse caso, o pedido controla seu próprio estado e garante que nenhuma regra seja quebrada.

Repositórios

Repositórios são responsáveis por salvar e recuperar entidades.

Eles isolam o domínio dos detalhes de persistência, permitindo que o negócio não dependa diretamente de banco de dados ou framework.

orderRepository.save(order);
customerRepository.findById(id);

Isso mantém o modelo mais limpo e facilita testes e manutenção.

Serviços de Domínio

Algumas regras importantes não pertencem claramente a uma única entidade. Quando isso acontece, usamos Serviços de Domínio.

Um processo de autorização de pagamento, por exemplo, pode envolver pedido, limite, antifraude e operadora.

paymentAuthorizationService.authorize(order);

O papel desse serviço é representar comportamento de negócio relevante, e não apenas agrupar código utilitário.

Esses conceitos não existem para complicar o sistema. Eles existem para organizar a complexidade natural do negócio.

Quando bem aplicados, o software deixa de ser apenas código que manipula dados e passa a representar com clareza como a empresa realmente funciona.

Bounded Context: definindo limites para evitar confusão

Conforme um sistema cresce, surge um problema comum: a mesma palavra começa a significar coisas diferentes dependendo da área da empresa.

Um bom exemplo é o termo cliente.

Para o time comercial, cliente pode representar lead, oportunidade ou conversão.
Para o financeiro, significa cobrança, limite ou inadimplência.
Para o suporte, envolve chamados, SLA e satisfação.

Todos usam a palavra “cliente”, mas cada área enxerga esse conceito de uma forma diferente.

Quando o sistema tenta reunir tudo isso em um único modelo genérico, normalmente nasce confusão. A entidade cresce demais, regras se misturam e o software perde clareza.

É justamente para resolver esse problema que existe o Bounded Context.

A ideia é criar limites claros dentro do sistema, onde cada contexto possui sua própria linguagem, regras e modelo de domínio.

Dentro do contexto comercial, “cliente” pode ter um significado. No financeiro, outro. No suporte, outro completamente diferente. E tudo bem.

O objetivo não é forçar uma definição universal, e sim respeitar a realidade de cada área do negócio.

Na prática, isso reduz acoplamento, facilita manutenção e permite que partes diferentes do sistema evoluam com mais autonomia.

Esse conceito é especialmente valioso em sistemas grandes, monólitos modulares e arquiteturas de microsserviços.

Mais do que dividir código, Bounded Context define fronteiras de responsabilidade e significado.

E muitas vezes, entender esses limites vale mais do que qualquer decisão tecnológica.

Em empresas maiores, a mesma palavra pode significar coisas diferentes.

DDD não é arquitetura

Um erro comum é tratar Domain-Driven Design como se fosse um estilo de arquitetura de software. Não é.

DDD não define se o sistema será monólito ou microsserviços. Não escolhe framework, banco de dados, mensageria ou estratégia de deploy.

Ele também não é sinônimo de:

  • Clean Architecture
  • Hexagonal Architecture
  • CQRS
  • Event Sourcing
  • Microsserviços

Essas abordagens podem coexistir com DDD, mas são coisas diferentes.

Enquanto arquitetura costuma responder perguntas como “como organizar tecnicamente o sistema?”, o DDD responde uma questão anterior e mais estratégica:

Como modelar o software para refletir corretamente o negócio?

Isso significa que você pode aplicar DDD em um monólito bem estruturado, em uma aplicação feita com Quarkus, Spring Boot, Node.js ou em um ecossistema distribuído de microsserviços.

Na prática, DDD fala menos sobre tecnologia e mais sobre entendimento.

Inclusive, muitos projetos falham justamente por investir cedo demais em arquitetura sofisticada sem antes compreender o domínio. O resultado costuma ser um sistema moderno por fora, mas confuso por dentro.

Por isso, antes de discutir camadas, containers ou serviços, vale perguntar:

O software está representando corretamente o negócio que deveria resolver?

Quando DDD vale a pena?

Nem todo sistema precisa de Domain-Driven Design em alto nível de profundidade. Em projetos simples, aplicar todos os conceitos de forma rígida pode gerar complexidade desnecessária.

DDD costuma entregar mais valor quando o problema de negócio é realmente complexo.

Isso acontece em cenários onde existem muitas regras, exceções, decisões importantes e processos que mudam com frequência. Quanto mais difícil for entender a operação da empresa, maior tende a ser o benefício de uma modelagem orientada ao domínio.

Alguns sinais claros:

  • regras de negócio extensas e específicas
  • múltiplas áreas envolvidas no mesmo sistema
  • crescimento constante de funcionalidades
  • alto impacto quando algo dá errado
  • linguagem própria do negócio
  • dificuldade recorrente para evoluir o sistema

Imagine uma plataforma bancária, um ERP, um marketplace ou um SaaS de gestão operacional. Nesses casos, normalmente o desafio principal não é criar telas ou endpoints — é representar corretamente o funcionamento do negócio.

Por outro lado, se o projeto é apenas um CRUD simples, uma landing page administrativa ou uma ferramenta interna pequena, talvez uma abordagem mais leve seja suficiente.

DDD não deve ser usado por moda ou status técnico.

Ele faz sentido quando ajuda a lidar com complexidade real.

Em resumo:

quanto mais complexo for o negócio, mais valioso tende a ser o DDD.

Erros comuns ao tentar aplicar DDD

Domain-Driven Design é poderoso, mas também costuma ser mal interpretado. Muitos times adotam o termo, estudam alguns patterns e acreditam que já estão aplicando DDD — quando, na prática, apenas adicionaram complexidade ao projeto.

Um dos erros mais comuns é começar pelos padrões e não pelo negócio.

O time discute Aggregate, Factory, CQRS, Event Sourcing e microsserviços antes mesmo de entender como a empresa opera, quais regras importam ou onde está a real complexidade. Quando isso acontece, o DDD vira decoração arquitetural.

Outro erro frequente é criar um modelo anêmico.

As entidades existem apenas com atributos e getters/setters, enquanto toda a regra de negócio fica espalhada em services, controllers ou classes utilitárias. O nome parece sofisticado, mas o domínio continua pobre.

Também é comum ignorar quem realmente entende a operação.

DDD depende de proximidade com especialistas do negócio. Sem conversar com produto, atendimento, financeiro ou áreas operacionais, o sistema tende a refletir suposições técnicas — e não a realidade da empresa.

Há ainda o erro de tentar sofisticar tudo.

Nem todo sistema precisa de todos os conceitos do livro. Nem todo projeto precisa de múltiplos contextos, eventos complexos ou uma modelagem profunda. Em alguns casos, simplicidade é a melhor decisão.

DDD não serve para complicar software. Serve para organizar complexidade que já existe.

Por isso, uma boa aplicação de DDD exige equilíbrio: profundidade quando necessária e pragmatismo quando suficiente.

Em resumo, o maior risco não é “não usar DDD”.

É usar o nome DDD para justificar decisões que afastam o sistema do próprio negócio.

Como começar na prática

Começar com DDD não significa redesenhar todo o sistema de uma vez ou aplicar todos os patterns possíveis logo no início.

O melhor caminho é começar pelo entendimento do negócio.

Antes de pensar em classes, tabelas ou endpoints, converse com quem vive a operação no dia a dia. Entenda quais regras realmente importam, quais exceções acontecem com frequência e quais decisões causam impacto no negócio.

Perguntas simples já ajudam bastante:

O que pode dar errado nesse processo?
Quais regras não podem ser quebradas?
Existem exceções?
Quais termos vocês usam para descrever isso?
O que muda de uma área para outra?

A partir dessas conversas, comece a montar um vocabulário comum. Os termos usados pelo negócio devem aparecer no código, na documentação e nas APIs. Esse glossário inicial ajuda a reduzir ambiguidades e aproxima o time técnico da realidade da empresa.

Depois, tente identificar os principais comportamentos do domínio.

Em vez de pensar apenas em “cadastro de cliente” ou “tabela de pedido”, procure ações reais do negócio: confirmar um pedido, cancelar um agendamento, bloquear um cliente, aprovar um pagamento, remarcar um horário.

Esses comportamentos revelam onde estão as regras mais importantes.

Também vale observar onde os conceitos mudam de significado. Um “cliente” para o financeiro pode ser diferente de um “cliente” para o suporte. Esse tipo de diferença pode indicar a existência de contextos separados.

Por fim, evolua aos poucos.

DDD não nasce perfeito na primeira modelagem. O entendimento melhora conforme o time conversa mais, entrega funcionalidades e aprende com o próprio sistema.

O objetivo não é criar uma arquitetura impressionante desde o primeiro dia.

É construir, pouco a pouco, um modelo que represente cada vez melhor o negócio.

Conclusão

Grande parte dos problemas em software não nasce da tecnologia utilizada.

Na maioria das vezes, frameworks modernos, cloud, microsserviços ou bancos de dados robustos não resolvem aquilo que realmente trava a evolução de um sistema: entendimento ruim do negócio, comunicação confusa e modelos que não representam a realidade da empresa.

É por isso que Domain-Driven Design continua tão atual.

DDD nos lembra que software corporativo não existe apenas para processar dados ou expor APIs. Ele existe para resolver problemas reais, apoiar operações e sustentar decisões importantes do negócio.

Quando o domínio é bem compreendido, o código ganha clareza. As regras ficam explícitas. As conversas entre áreas melhoram. O sistema evolui com menos atrito.

Mais do que um conjunto de padrões, DDD é uma mudança de foco.

Em vez de começar pela tecnologia, começamos pelo problema que realmente importa resolver.

Una búsqueda eficaz parte de concretar la necesidad de una prenda suelta o una equipación completa. La información disponible resulta más completa si permite comprobar si el producto incluye solo la camiseta o más piezas. Antes de decidir, «» puede orientar la tarea de comparar las descripciones y las imágenes disponibles cuando existan. En el control final, conviene verificar el contenido exacto de la opción elegida.

撮影やイベントに使う衣装は、再現度に加えて動きやすさと着用時間も考えて選びましょう。サイズやセット内容を確認する場合は、ONE PIECE コスプレ衣装から候補を探せます。使用後は素材に合う方法で手入れし、装飾を傷めない形で保管することが大切です。

Una búsqueda eficaz parte de concretar la necesidad de una prenda suelta o una equipación completa. La información disponible resulta más completa si permite comprobar si el producto incluye solo la camiseta o más piezas. Antes de decidir, «equipaciones de fútbol de clubes» puede orientar la tarea de comparar las descripciones y las imágenes disponibles cuando existan. En el control final, conviene verificar el contenido exacto de la opción elegida.

E quando isso acontece, o software deixa de ser apenas implementação técnica e passa a se tornar um ativo estratégico para o negócio.