Design Orientado a Domínio (DDD) em Microsserviços

Design Orientado a Domínio (DDD) em Microsserviços

Quando as organizações fazem a transição de uma arquitetura monolítica para microsserviços, elas enfrentam uma questão crítica e de alto risco: Como traçamos os limites de nossos serviços?

Em teoria, os microsserviços deveriam ser unidades soltas e desacopladas que podem ser desenvolvidas, implantadas e dimensionadas de forma independente. Na prática, porém, muitas equipes acabam construindo um monólito distribuído — um sistema em que os serviços estão tão fortemente acoplados que uma única mudança nos negócios exige a modificação e a implantação de vários serviços simultaneamente, agravando a latência da rede e os impasses de implantação.

Para evitar essa armadilha, os arquitetos de software recorrem ao Domain-Driven Design (DDD). Introduzido pela primeira vez por Eric Evans em 2003, DDD é uma metodologia de desenvolvimento de software que alinha estruturas de código com os domínios de negócios complexos que representam.

Neste artigo, exploraremos como o DDD fornece os planos estratégicos e táticos para projetar microsserviços limpos, desacoplados e de alta manutenção.


1. O Plano Estratégico: Traçando os Limites do Serviço

O DDD é dividido em duas fases principais: Design Estratégico e Design Tático. O design estratégico trata da modelagem de contextos de negócios e da definição de limites de serviço. Ele fornece a visão “macro” da arquitetura.

Mapeamento de contextos limitados DDD para diagrama de arquitetura de microsserviços

A. Linguagem onipresente

Dentro de uma empresa, diferentes departamentos usam as mesmas palavras para significar coisas completamente diferentes. Por exemplo:

  • Para a equipe de vendas, um “Usuário” é um lead ou cliente potencial.
  • Para a equipe de segurança, um “Usuário” é um conjunto de credenciais de login.
  • Para a equipe de envio, “Usuário” é um endereço físico do destinatário.

Tentar construir um modelo de banco de dados único e unificado para um “usuário” que satisfaça a todos resulta em uma base de código enorme e emaranhada. O DDD resolve isso estabelecendo uma linguagem onipresente — um vocabulário compartilhado definido e usado por especialistas e desenvolvedores do domínio de negócios dentro de um limite específico.

B. Contextos limitados

Um Contexto Limitado é o limite explícito dentro do qual um modelo de domínio se aplica. Dentro da fronteira, todos os termos da Linguagem Onipresente têm um significado único e inequívoco.

Em vez de um único modelo de “Usuário”, definimos modelos separados dentro de seus respectivos Contextos Limitados:

  • No Contexto do pedido, temos um modelo Order contendo informações de contato do cliente.
  • No Contexto de Identidade, temos um modelo Credentials.
  • No Contexto de Envio, temos um modelo DeliveryAddress.

A regra de ouro dos microsserviços: Um contexto limitado é mapeado diretamente para um microsserviço.

Ao particionar o sistema em contextos limitados, garantimos que os microsserviços sejam fracamente acoplados e representem capacidades de negócios distintas.

C. Mapeamento de contexto: como os serviços se comunicam

Contextos limitados não existem isoladamente; eles devem interagir. Um Mapa de Contexto define os relacionamentos e mecanismos de tradução entre contextos. Os principais padrões incluem:

  • Kernel Compartilhado: Dois contextos compartilham um pequeno subconjunto do modelo de domínio e banco de dados (geralmente desencorajado em microsserviços devido ao acoplamento).
  • Cliente-Fornecedor: Um contexto (fornecedor) deve fornecer dados a outro (cliente). O fornecedor deve coordenar os cronogramas de liberação com o cliente.
  • Camada Anticorrupção (ACL): Uma camada de tradução que traduz dados recebidos de um sistema externo no modelo de domínio interno do cliente, evitando que modelos externos poluam a arquitetura interna.

2. O projeto tático: estruturando a base de código de microsserviços

Depois que os limites do serviço são traçados usando o design estratégico, o Design Tático fornece um conjunto de padrões de design para estruturar o código em um único microsserviço.

A. Entidades e objetos de valor

Dentro de um microsserviço, os modelos são divididos em duas categorias:

  1. Entidades: objetos que possuem uma identidade exclusiva que persiste ao longo do tempo, mesmo que seus atributos mudem. Os exemplos incluem Order ou Product.
  2. Objetos de valor: Objetos que não possuem identidade única e são definidos inteiramente por seus atributos. Eles são imutáveis. Os exemplos incluem ShippingAddress ou ProductDimensions. Se dois objetos de valor tiverem os mesmos atributos, eles serão considerados iguais.

B. Agregados e raízes agregadas

Um Agregado é um cluster de entidades e objetos de valor associados que são tratados como uma única unidade para alterações de dados. Cada agregado tem uma Raiz Agregada, que é o único ponto de entrada através do qual objetos externos podem interagir com o agregado. A raiz garante que todas as invariantes de negócios (regras) sejam aplicadas.

Por exemplo, no diagrama acima:

  • No Serviço de Pedido, o Order é a Raiz Agregada. Contém OrderItem (Entidade) e ShippingAddress (Objeto de Valor). Os serviços externos não podem modificar OrderItem diretamente; eles devem chamar um método na raiz Order (por exemplo, order.AddItem()), que valida que o pedido ainda não foi enviado.

C. Eventos de domínio

Um Evento de Domínio é algo que aconteceu no domínio que interessa aos especialistas de negócios. Ele é usado para comunicar alterações em contextos limitados de forma assíncrona. Quando um agregado executa um comando, ele publica um evento de domínio (por exemplo, OrderCreated). Outros serviços escutam esse evento e atualizam seu estado de acordo, garantindo consistência eventual sem dependências de API síncronas.


3. Um exemplo concreto: serviço de pedido vs. serviço de estoque

Vejamos uma implementação simplificada de DDD tático em Go para o Serviço de Pedidos, mostrando como a Raiz Agregada coordena as regras de negócios.

package domain

import (
	"errors"
	"time"
)

// Value Object (Immutable, identity-less)
type ShippingAddress struct {
	Street  string
	City    string
	ZipCode string
}

// Entity (Has identity, mutable)
type OrderItem struct {
	ProductID string
	Quantity  int
	Price     float64
}

// Aggregate Root (Enforces transactional boundaries)
type Order struct {
	ID        string
	Items     []OrderItem
	Address   ShippingAddress
	Status    string
	CreatedAt time.Time
}

// NewOrder creates a new Order Aggregate Root
func NewOrder(id string, address ShippingAddress) *Order {
	return &Order{
		ID:        id,
		Items:     []OrderItem{},
		Address:   address,
		Status:    "PENDING",
		CreatedAt: time.Now(),
	}
}

// AddItem enforces business rules before mutating state
func (o *Order) AddItem(productID string, qty int, price float64) error {
	if o.Status != "PENDING" {
		return errors.New("cannot add items to a finalized or cancelled order")
	}
	if qty <= 0 {
		return errors.New("quantity must be greater than zero")
	}
	
	o.Items = append(o.Items, OrderItem{
		ProductID: productID,
		Quantity:  qty,
		Price:     price,
	})
	return nil
}

Quando o pedido é salvo com sucesso, o serviço publica um evento em um corretor de mensagens:

{
  "event_id": "evt_98231",
  "event_type": "OrderCreated",
  "timestamp": "2026-06-26T00:15:00Z",
  "payload": {
    "order_id": "ord_5521",
    "items": [
      { "product_id": "prod_88", "quantity": 2 }
    ]
  }
}

O Serviço de Inventário escuta esse evento, atualiza sua entidade StockLevel local e conclui o fluxo de forma assíncrona.


4. DDD estratégico vs. tático em microsserviços

Fase/Aspecto DDD Estratégico DDD tático
Escopo Arquitetura global do sistema (macro) Dentro de uma única base de código de serviço (micro)
Meta principal Traçar limites de serviço claros e desacoplados Modele lógica de negócios rica e aplique invariantes
Conceitos-chave Contextos limitados, linguagem onipresente, mapa de contexto Entidades, Objetos de Valor, Agregados, Eventos de Domínio
Público Arquitetos, Gerentes de Produto, Desenvolvedores Desenvolvedores de software, revisores de código
Impacto nos microsserviços Determina o número e o âmbito dos serviços Determina transações de banco de dados e estrutura de diretórios

Conclusão: comece primeiro com DDD estratégico

Aplicar Domain-Driven Design a microsserviços é uma maneira poderosa de garantir que sua arquitetura corresponda à estrutura de seu negócio. No entanto, as equipes muitas vezes cometem o erro de focar demais em padrões táticos (como escrever interfaces de repositório e objetos de valor) enquanto ignoram o design estratégico.

Se você não definir os limites corretamente, nenhuma quantidade de código tático limpo evitará que seu sistema se transforme em um monólito distribuído. Sempre comece com o mapeamento estratégico – defina sua linguagem ubíqua, agrupe a lógica em contextos limitados, mapeie os fluxos de comunicação e deixe os limites de seus microsserviços emergirem naturalmente dessas definições.


Explore mais arquitetura de software, padrões de design e insights de engenharia no Blog Ghaznix →