Três Olhares sobre Arquitetura de Software: Fowler, Evans e Uncle Bob: 1.3 - Domain Model: o coração da arquitetura em três vozes

Nos últimos vinte anos, três nomes se tornaram referência inevitável sempre que falamos sobre arquitetura de software: Martin Fowler, Eric Evans e Robert C. Martin. Cada um deles trouxe uma perspectiva única, mas complementar, para o ofício de projetar sistemas duradouros. Fowler, com seus padrões de aplicação empresarial, nos ofereceu um catálogo de soluções pragmáticas para problemas recorrentes. Evans, com o Domain-Driven Design, nos mostrou como aproximar o software da linguagem do negócio e como dar forma ao que realmente importa em um sistema: o domínio. E Uncle Bob, com a Clean Architecture, reforçou os princípios e as camadas que protegem esse domínio da corrosão do tempo, dos frameworks e da infraestrutura.
Esta série de artigos nasce do desejo de cruzar esses três olhares. A cada padrão descrito por Fowler, vamos buscar o eco que ele encontra em Evans e em Martin. Em alguns momentos, haverá convergência; em outros, choque. Mas em todos, a discussão nos ajuda a entender não apenas o “como” programar, mas o “porquê” de certas escolhas arquiteturais.
Vamos usar exemplos em C#, trazer referências aos livros originais e sempre terminar com uma reflexão sobre quando e por que adotar (ou evitar) cada padrão. A ideia não é canonizar um estilo, mas oferecer um mapa de raciocínio para que você, como engenheiro de software, tenha mais clareza ao tomar suas decisões.
Índice da Série
Parte 1 – A lógica do domínio
- 1.1 Transaction Script: do procedural ao domínio rico
- 1.2 Table Module: tabelas falam mais alto que objetos?
- 1.3 Domain Model: o coração da arquitetura em três vozes
- 1.4 Service Layer: orquestrando casos de uso
Parte 2 – Persistência e Infraestrutura
- 2.1 Active Record: simplicidade que pode custar caro
- 2.2 Data Mapper: separando domínio e banco de dados
- 2.3 Unit of Work: coordenando mudanças no domínio
- 2.4 Identity Map & Lazy Load: truques de performance e consistência
Parte 3 – Apresentação e Integração
- 3.1 MVC e Front Controller: a porta de entrada
- 3.2 Template View e Page Controller: quando a UI dita o jogo
- 3.3 Gateways e Mappers: defendendo o domínio
- 3.4 Mensageria e integração: eventos em três perspectivas
1.3 Domain Model: o coração da arquitetura em três vozes
Introdução
Quando Martin Fowler apresenta o Domain Model, ele o descreve como uma ruptura com os estilos anteriores. Diferente do Transaction Script, onde a lógica fica espalhada em funções procedurais, ou do Table Module, onde as tabelas ditam o ritmo do sistema, o Domain Model traz a lógica de negócio para dentro de objetos ricos em comportamento.
Eric Evans, em Domain-Driven Design (2003), transforma essa ideia em filosofia: o domínio deve ser o centro do software, sustentado por uma Linguagem Ubíqua que conecta especialistas de negócio e desenvolvedores.
Robert C. Martin, no clássico Clean Architecture (2017), dá o passo seguinte: proteger esse núcleo de regras de negócio contra a corrosão causada por frameworks, bancos de dados e interfaces. Para ele, a arquitetura deve “gritar” o domínio, e não detalhes técnicos.
Assim, o Domain Model se torna o ponto de convergência dos três autores: Fowler nos dá o padrão, Evans a metodologia de modelagem, e Uncle Bob a disciplina para preservar esse núcleo ao longo do tempo.
Modelando a variabilidade no coração do domínio
Um dos maiores desafios em modelos de domínio é lidar com variabilidade de regras de negócio. Em qualquer sistema real, certas lógicas mudam conforme o contexto: cálculo de frete, políticas de desconto, métodos de pagamento.
É justamente nesses cenários que Fowler recomenda o uso de padrões como o Strategy. Evans também reforça essa prática, mostrando como padrões clássicos se integram de forma natural ao modelo de domínio. Já Uncle Bob enxerga nisso a aplicação direta do Princípio Aberto/Fechado (OCP): nosso código deve permitir extensões sem exigir modificações constantes.
Exemplo prático: processamento de pagamentos
Vamos imaginar um e-commerce em que diferentes métodos de pagamento têm diferentes taxas. Essa lógica é naturalmente variável e, portanto, um terreno fértil para o uso do Strategy Pattern.
using System;
using System.Collections.Generic;
using System.Linq;
public interface IPaymentStrategy
{
decimal CalculateFee(decimal amount);
}
// Concrete strategies
public class CreditCardPaymentStrategy : IPaymentStrategy
{
public decimal CalculateFee(decimal amount) => amount * 0.025m;
}
public class PayPalPaymentStrategy : IPaymentStrategy
{
public decimal CalculateFee(decimal amount) => (amount * 0.03m) + 0.30m;
}
public class BankTransferPaymentStrategy : IPaymentStrategy
{
public decimal CalculateFee(decimal amount) => 0m;
}
// Value Object for money
public readonly record struct Money(decimal Amount, string Currency)
{
public static Money operator +(Money a, Money b)
{
if (a.Currency != b.Currency)
throw new InvalidOperationException("Currency mismatch.");
return new Money(a.Amount + b.Amount, a.Currency);
}
}
// Entity
public class Order
{
private readonly List<OrderItem> _items = new();
private readonly IPaymentStrategy _paymentStrategy;
public Guid Id { get; private set; } = Guid.NewGuid();
public IReadOnlyCollection<OrderItem> Items => _items.AsReadOnly();
public Money Total => new(_items.Sum(i => i.Total.Amount), Currency);
public string Currency { get; }
public Order(IPaymentStrategy paymentStrategy, string currency)
{
_paymentStrategy = paymentStrategy ?? throw new ArgumentNullException(nameof(paymentStrategy));
Currency = currency ?? throw new ArgumentNullException(nameof(currency));
}
public void AddItem(string productName, decimal unitPrice, int quantity)
{
_items.Add(new OrderItem(productName, new Money(unitPrice, Currency), quantity));
}
public Money CalculateTotalWithFees()
{
var fee = _paymentStrategy.CalculateFee(Total.Amount);
return new Money(Total.Amount + fee, Currency);
}
}
// Value Object for order item
public readonly record struct OrderItem(string ProductName, Money UnitPrice, int Quantity)
{
public Money Total => new(UnitPrice.Amount * Quantity, UnitPrice.Currency);
}
Três olhares sobre o mesmo modelo
No exemplo do processamento de pagamentos, podemos enxergar nitidamente como os três autores se encontram. Fowler ficaria satisfeito em ver que a lógica de negócio — o cálculo das taxas e o total do pedido — está encapsulada dentro do próprio modelo, em vez de espalhada em controladores ou scripts utilitários. A escolha do Strategy dá ao modelo a maleabilidade necessária para acomodar novos métodos de pagamento sem virar um código rígido e frágil.
Evans, por sua vez, reconheceria de imediato o vocabulário do domínio presente nas nossas estruturas. O uso de Value Objects imutáveis como Money e OrderItem ecoa a linguagem dos especialistas do negócio, enquanto a entidade Order protege sua identidade e seus invariantes. O código conversa com o domínio de forma quase natural: falamos em pedidos, valores e taxas, e não em tabelas ou IDs de framework.
Já Uncle Bob apontaria para a pureza desse núcleo. O domínio não sabe nada sobre bancos de dados, ORMs ou APIs externas. Ele é testável por natureza e protegido contra detalhes de infraestrutura que mudam com o tempo. Assim, preservamos o que realmente interessa: as regras que definem o negócio.
Esse design permite evoluir o sistema sem comprometer o que já existe. Se amanhã surgir um novo método de pagamento (como criptomoedas), basta criar uma nova estratégia — sem alterar o domínio existente.
Conclusão
O Domain Model marca a transição de sistemas centrados em dados para sistemas centrados no negócio. Ele é o coração da arquitetura de software: Fowler deu-lhe forma, Evans deu-lhe vocabulário, e Martin deu-lhe resiliência.
Nosso exemplo mostrou como um simples padrão de projeto, aplicado dentro do domínio, pode resolver um problema de variabilidade sem sacrificar clareza ou estabilidade.
No próximo artigo, veremos a Service Layer, que se posiciona acima do modelo para orquestrar casos de uso sem poluir o domínio. Se o Domain Model é o coração, a Service Layer será o maestro que conduz a sinfonia.