Acesse o painel da sua conta

Não tem uma conta? Registrar

Entrar em contato

Visite também nosso site craftxp.com.br

  • img
  • img
  • img
  • img
  • img
  • img

Entre em contato

Domain-Driven Design (DDD): Guia Completo para Modelagem de Software Complexo

Domain-Driven Design (DDD): Guia Completo para Modelagem de Software Complexo

O que é Domain-Driven Design e Por Que Sua Empresa Precisa Dele?

Domain-Driven Design (DDD) é uma abordagem de desenvolvimento de software criada por Eric Evans em seu livro homônimo de 2003. O DDD propõe que a complexidade dos sistemas de software deve ser enfrentada colocando o domínio do negócio — a área de conhecimento e atividade da empresa — como centro de todas as decisões de design.

Diferente de abordagens puramente técnicas que priorizam bancos de dados, frameworks ou padrões de código, o DDD parte do princípio de que o software de maior valor é aquele que modela fielmente os processos, regras e linguagem do negócio real. Grandes empresas como Amazon, Uber e Spotify utilizam princípios de DDD para gerenciar a complexidade de seus sistemas distribuídos.

O DDD não é um framework ou biblioteca — é uma mentalidade de design que combina modelagem estratégica e tática para produzir software que evolui junto com o negócio.

Os Dois Pilares do DDD: Estratégico e Tático

O DDD se divide em duas grandes áreas complementares:

  • DDD Estratégico: Foca na organização de grandes sistemas, definindo limites entre contextos, relacionamentos entre equipes e a linguagem compartilhada (Ubiquitous Language). É ideal para arquiteturas de microsserviços.
  • DDD Tático: Fornece blocos de construção — entidades, value objects, agregados, repositórios, serviços de domínio e factories — para implementar o modelo de domínio no código.

Ambos os pilares trabalham juntos. O estratégico define onde aplicar cada modelo, e o tático define como implementá-lo.

Linguagem Ubíqua (Ubiquitous Language): A Base de Tudo

A linguagem ubíqua é o conceito mais importante do DDD. Ela consiste em um vocabulário compartilhado entre desenvolvedores, especialistas de negócio, product owners, designers e QA. Todos usam os mesmos termos para descrever o domínio — em conversas, documentação, código, testes e APIs.

Por exemplo, em um sistema bancário, termos como "Conta Corrente", "Transferência", "Saldo Disponível" e "Extrato" devem ser usados consistentemente por todos os envolvidos e refletidos diretamente nas classes, métodos e tabelas do banco de dados.

Dica: Quando desenvolvedores e especialistas de negócio usam palavras diferentes para a mesma coisa, é um sinal de que o modelo de domínio está desconectado da realidade.

DDD Estratégico: Bounded Contexts e Context Mapping

Em sistemas grandes, um único modelo de domínio monolítico se torna inviável. O DDD estratégico resolve isso com Bounded Contexts (Contextos Delimitados).

Um Bounded Context é um limite explícito dentro do qual um modelo de domínio específico se aplica. Cada contexto tem sua própria linguagem ubíqua e suas próprias regras. A comunicação entre contextos é feita através de Context Maps, que definem relacionamentos como:

  • Partner (Parceiro): Duas equipes colaboram ativamente para alinhar seus modelos
  • Shared Kernel (Núcleo Compartilhado): Uma porção do modelo é compartilhada entre contextos
  • Customer-Supplier (Cliente-Fornecedor): Um contexto upstream fornece dados para um downstream
  • Conformist (Conformista): O contexto downstream aceita passivamente o modelo do upstream
  • Anti-Corruption Layer (Camada Anticorrupção): Uma camada de tradução isola um contexto de mudanças em outro
  • Open-Host Service: Um contexto publica uma interface explícita (como uma API REST ou gRPC)
  • Published Language: Um formato padronizado (ex: JSON Schema, Protobuf) é usado para comunicação
  • Separate Ways: Contextos independentes sem integração direta

Esses padrões são fundamentais para arquiteturas de microsserviços bem-sucedidas, onde cada microsserviço geralmente corresponde a um Bounded Context.

DDD Tático: Blocos de Construção do Modelo

O DDD tático fornece blocos de construção que traduzem o modelo conceitual em código implementável:

Entidades (Entities)

Objetos que têm uma identidade contínua ao longo do tempo. Dois objetos com os mesmos atributos não são iguais se tiverem identidades diferentes.

class ContaCorrente {
    private final String id;  // Identidade única
    private double saldo;
    private String titular;

    public void depositar(double valor) {
        this.saldo += valor;
    }

    public void sacar(double valor) {
        if (valor > this.saldo)
            throw new SaldoInsuficienteException();
        this.saldo -= valor;
    }
}

Value Objects

Objetos imutáveis que são definidos por seus atributos, não por uma identidade. Dois value objects são iguais se todos os seus atributos forem iguais.

class Dinheiro {
    private final double valor;
    private final String moeda;

    public Dinheiro(double valor, String moeda) {
        this.valor = valor;
        this.moeda = moeda;
    }

    public Dinheiro somar(Dinheiro outro) {
        if (!this.moeda.equals(outro.moeda))
            throw new IllegalArgumentException("Moedas diferentes");
        return new Dinheiro(this.valor + outro.valor, this.moeda);
    }

    @Override
    public boolean equals(Object o) {
        if (!(o instanceof Dinheiro)) return false;
        Dinheiro d = (Dinheiro) o;
        return this.valor == d.valor && this.moeda.equals(d.moeda);
    }
}

Agregados (Aggregates)

Um agregado é um cluster de objetos de domínio tratados como uma única unidade. Cada agregado tem uma raiz (Aggregate Root) que é a única entidade acessível externamente. Toda operação no agregado passa pela raiz, garantindo consistência e invariantes.

class Pedido {  // Aggregate Root
    private String id;
    private Cliente cliente;
    private List<ItemPedido> itens;
    private StatusPedido status;

    public void adicionarItem(Produto produto, int quantidade) {
        if (status != StatusPedido.ABERTO)
            throw new IllegalArgumentException("Pedido não está aberto");
        itens.add(new ItemPedido(produto, quantidade));
    }

    public double calcularTotal() {
        return itens.stream()
            .mapToDouble(ItemPedido::getSubtotal)
            .sum();
    }

    public void finalizar() {
        if (itens.isEmpty())
            throw new IllegalStateException("Pedido sem itens");
        this.status = StatusPedido.FINALIZADO;
    }
}

Repositórios (Repositories)

Abstrações para recuperar e persistir agregados, dando a impressão de que os objetos estão disponíveis em memória:

interface PedidoRepository {
    Pedido buscarPorId(String id);
    void salvar(Pedido pedido);
    void remover(Pedido pedido);
    List<Pedido> buscarPorCliente(String clienteId);
}

Serviços de Domínio (Domain Services)

Quando uma operação não pertence naturalmente a uma entidade ou value object, ela é modelada como um serviço de domínio. Serviços de domínio operam sobre o modelo de domínio e contêm lógica de negócio significativa:

class TransferenciaService {
    private final ContaRepository contas;

    public void transferir(String origemId, String destinoId, Dinheiro valor) {
        Conta origem = contas.buscarPorId(origemId);
        Conta destino = contas.buscarPorId(destinoId);

        origem.sacar(valor);
        destino.depositar(valor);

        contas.salvar(origem);
        contas.salvar(destino);
    }
}

Eventos de Domínio (Domain Events)

Eventos que representam algo relevante que aconteceu no domínio. São imutáveis e nomeados no passado:

class TransferenciaRealizada {
    public final String origemId;
    public final String destinoId;
    public final Dinheiro valor;
    public final Instant ocorridoEm;

    public TransferenciaRealizada(...) { ... }
}

// Publicando e consumindo eventos
origem.registrarEvento(new TransferenciaRealizada(
    origem.id, destino.id, valor, Instant.now()
));

DDD na Prática: Exemplo Completo com E-Commerce

Vamos aplicar DDD em um sistema de e-commerce:

Bounded Contexts identificados:

  1. Catálogo: Gerencia produtos, categorias e preços
  2. Carrinho: Gerencia sessões de compra temporárias
  3. Pedidos: Processa pedidos, pagamentos e entregas
  4. Estoque: Controla inventário e logística
  5. Pagamentos: Integra com gateways de pagamento e antifraude

Context Mapping sugerido:

  • Catálogo → Carrinho: Open-Host Service (API REST com DTOs)
  • Carrinho → Pedidos: Evento (CarrinhoFinalizado → PedidoService)
  • Pedidos → Estoque: Anti-Corruption Layer (adapta modelos diferentes)
  • Pedidos → Pagamentos: Partner (forte alinhamento entre equipes)

Dentro do contexto Pedidos, modelamos:

  • Pedido (Aggregate Root): controla itens, descontos, status
  • ItemPedido (Entity dentro do Agregado): produto + quantidade + preço
  • Dinheiro (Value Object): valor + moeda, imutável
  • PedidoRepository: persiste e recupera pedidos
  • PedidoService: coordena fluxos como finalização e reembolso
  • PedidoFinalizado (Domain Event): notifica outros contextos

DDD e Arquiteturas Modernas: Microsserviços e Clean Architecture

O DDD se alinha perfeitamente com arquiteturas modernas:

DDD + Microsserviços: Cada microsserviço idealmente corresponde a um Bounded Context. Isso garante que cada serviço tenha seu próprio modelo de domínio, sua própria linguagem ubíqua e sua própria persistência, evitando o acoplamento entre equipes.

DDD + Clean Architecture: A Clean Architecture (Arquitetura Limpa) de Robert C. Martin organiza o código em camadas concêntricas, com o domínio no centro. O DDD fornece o conteúdo para esse centro — as entidades, value objects e regras de negócio que são independentes de frameworks, bancos de dados e interfaces externas.

DDD + Event-Driven: Eventos de domínio são a base para arquiteturas orientadas a eventos. Cada Bounded Context publica eventos que outros contextos consomem, permitindo comunicação assíncrona e desacoplada.

Quando Usar e Quando Evitar o DDD

Use DDD quando Evite DDD quando
Domínio complexo com regras de negócio ricas Sistema CRUD simples sem lógica complexa
Equipe com acesso a especialistas de domínio Projetos com prazo muito curto e time pequeno
Sistema de longa vida útil que evoluirá com o negócio Protótipos e MVPs que serão descartados
Arquitetura de microsserviços com múltiplas equipes Domínio predominantemente técnico (middleware, infraestrutura)

Ferramentas e Frameworks que Facilitam DDD

Embora o DDD seja independente de tecnologia, algumas ferramentas ajudam na implementação:

  • Axon Framework (Java): Framework completo para CQRS/ES com DDD, incluindo suporte a agregados, sagas e eventos
  • NestJS CQRS (Node.js): Módulo de CQRS e eventos para aplicações TypeScript com DDD
  • Rails Event Store (Ruby): Gem para implementar event sourcing com DDD em aplicações Rails
  • Sequent (Ruby): Framework DDD para Ruby com suporte a comandos, eventos e projeções
  • EventFlow (.NET): Framework CQRS+ES para aplicações .NET
  • Python + Eventsourcing: Biblioteca Python para event sourcing com suporte a agregados e projeções
  • Context Mapper: Ferramenta open-source que gera código a partir de modelos DDD
  • Structurizr: Ferramenta de modelagem arquitetural baseada no modelo C4, compatível com DDD

Desafios Comuns na Adoção do DDD

  • Curva de aprendizado íngreme: Os conceitos de DDD exigem prática e estudo contínuo, especialmente a distinção entre entidades e value objects e o design de agregados.
  • Dificuldade com especialistas de domínio: Encontrar especialistas de negócio que tenham tempo e disposição para colaborar ativamente no modelo pode ser desafiador.
  • Tamanho correto do agregado: Agregados muito grandes criam contenção de concorrência; muito pequenos perdem o significado de unidade consistente. Não existe fórmula mágica — é iterativo.
  • Overengineering: Aplicar DDD tático em contextos simples que seriam melhor atendidos por um CRUD direto é um antipadrão comum.
  • Integração com sistemas legados: A Anti-Corruption Layer é essencial, mas requer manutenção contínua e pode se tornar complexa.

Conclusão

Domain-Driven Design é muito mais que um conjunto de padrões de código — é uma filosofia de desenvolvimento que coloca o entendimento profundo do negócio como a principal ferramenta para lidar com a complexidade do software. Os conceitos estratégicos como Bounded Contexts e Context Mapping são indispensáveis para arquiteturas modernas de microsserviços, enquanto os blocos táticos fornecem um vocabulário rico para implementar regras de negócio complexas de forma expressiva e testável.

Para começar com DDD, recomenda-se: (1) estude os conceitos fundamentais no livro azul do Eric Evans ou no livro vermelho de Vaughn Vernon; (2) pratique com um domínio real que você conheça bem; (3) aplique os padrões estratégicos primeiro (Bounded Contexts, linguagem ubíqua) antes de mergulhar nos padrões táticos; e (4) evolua iterativamente — o modelo de domínio nunca está pronto, ele vive e respira junto com o negócio.

Craft XP
Craft XP