CRUD simples
Entrada valida, grava, devolve. Sem invariante entre o input e o banco.
/// 00 · Matthew Boyle
Use Go para criar sistemas simples que resolvem problemas de negócio complexos. DDD não é microserviço. É modelar o domínio até o código falar a mesma língua da mesa.
/// 01 · Quando usar
Boyle abre o livro sem Go. Recupera Evans (2003), os padrões da Gang of Four e o scorecard do Big Red Book, de Vaughn Vernon. A regra é nua: CRUD com validação e persistência não precisa de DDD. Aplica se a nota passa de 7.
Entrada valida, grava, devolve. Sem invariante entre o input e o banco.
Histórias do tipo “como usuário, quero X”. Poucas, e o futuro é ajuste fino.
O aviso precoce. A complexidade quase nunca é reconhecida a tempo.
Hoje é MVP. O caminho até o produto real já se vê no pitch.
Trocar feriado no calendário é uma classe. Suportar um protocolo novo de câmbio é outra.
O domínio é novo. Modelar e nomear é o pão com manteiga do DDD.
Acima de 7: candidato forte. Abaixo: ainda aproveita linguagem ubíqua e desacoplar infraestrutura, sem montar o aparato inteiro. O compromisso não nasce só da engenharia — precisa dos especialistas de domínio na mesa.
/// 02 · Três pilares
Evans reuniu práticas antigas de orientação a objetos sob nomes claros. Boyle reduz a três pilares. Os três funcionam sozinhos. Juntos, o sistema deixa de ser um banco com endpoints.
Lead, customer, churned, lost lead. Se o time fala uma coisa e o repositório outra, o requisito se perde na tradução.
O mapa do domínio vem antes da arquitetura. Shipping no centro; o resto existe para servir o núcleo.
Aqui o Go aparece. Identidade, igualdade por valor, fronteira transacional. O banco não dita o modelo.
2003Big Blue Book. Evans. O nome que começou tudo.
>7Nota mínima no scorecard de Vernon para valer o investimento.
2Sistemas construídos no livro: CoffeeCo (monólito) e recommendations (serviço).
/// 03 · Linguagem ubíqua
O time de pagamentos e assinaturas ouve o especialista: o lead escolhe um plano, vira customer, churned volta a lead, depois de seis meses vira lost lead. O primeiro rascunho em Go ainda fala em User e AddUser. O domínio não tem “adicionar usuário”.
Leia domain-driven design como business-problem-driven design.
Matthew Boyle · cap. 2
A correção é literal. LeadCreator.CreateLead. Lead.Convert devolve Customer. O código passa a ser a ata da reunião. Glossário no wiki do time, revisado no planning. Engenheiro desafia o especialista nos cantos: e se o lead nunca pagar? E se o e-mail mudar?
A língua não escala para a empresa inteira. Evans é explícito: rigor só cabe dentro de um bounded context. Customer em assinaturas não é customer em marketing. Forçar a mesma palavra em todos os times esvazia o termo.
/// 04 · Integração
Contextos conversam. Sem padrão, o modelo do vizinho vaza e corrompe o seu. Boyle usa os três de Evans.
Uma fatia do seu contexto exposta. REST, gRPC, até XML se o legado mandar. Exemplo do livro: GET /user/{id}/subscription/active para o marketing saber se a assinatura está viva — sem copiar o modelo interno.
A língua interna fica no time. A língua publicada é o contrato: OpenAPI ou protobuf. Swagger gera cliente. No Go, Boyle aponta oapi-codegen.
Quando você consome o vizinho, traduz na fronteira. O time de recommendations chama partnerships, que falha “de vez em quando” e vai ser reescrito. O adaptador isola o JSON alheio (priceInUSDPerNight, ISO-3166 alpha-3) do modelo de recomendação. HashiCorp retryablehttp, RetryMax 10: falha é o caso normal.
/// 05 · Tático
Parte 1 fecha com os tijolos. Nenhum é exclusivo de DDD. Errar o agregado, porém, deixa o sistema lento e inconsistente.
Definida pela identidade, não pelos atributos. O leilão muda preço, data, vendedor; o ID permanece. E-mail parece único e não serve: o usuário troca. Prefira UUID — int estoura. Google uuid no Go. Lógica de negócio mora no tipo: timezone só UTC, duração do leilão calculada num método, não no caller.
Getter, setter, zero invariante. Qualquer cliente inventa a regra. Cheira a tabela do banco ditando o domínio. Metadado de ads e tracing ID não entram na entidade — ficam fora do modelo.
Igualdade por valor. Point{1,1} com ponteiro falha o teste; sem ponteiro, passa. Imutável, substituível, funções sem efeito colateral. Dinheiro via biblioteca, nunca float. Perguntas: dá para ser imutável? Mede, quantifica ou descreve? Compara só pelos valores? Três sim → valor. Dúvida → valor, depois upgrade.
Não é slice. É fronteira transacional: pedido e itens, time e pessoas, carteira e cartões. Carrega, grava, apaga tudo ou nada. Um repositório por agregado, nunca uma struct por tabela.
Cria o objeto e aplica a invariante na porta. CreateBooking recusa horário depois das 17h. Encapsula default. No Go, uma função BuildCar / NewX basta — a linguagem não é OO e o padrão continua útil.
Acesso a dado, ponto. Interface ao lado do domínio; Postgres, arquivo ou S3 atrás. Sem regra de negócio no SQL. Boyle não gosta de ORM: GORM esconde query e empurra anemia. Se usar, adaptador no meio.
Domínio — operação sem estado que cruza entidades (colocar produto no carrinho). Aplicação — fina: auth, transação, orquestra repositório + domínio. Infra — Stripe, Mailchimp, Mixpanel. Não são o núcleo; entram por interface.
// língua no código — cap. 2, reescrito na ideia
type LeadCreator interface {
CreateLead(ctx context.Context, req LeadRequest) (Lead, error)
}
func (l Lead) Convert(ctx context.Context, plan SubscriptionType) (Customer, error)
/// 06 · CoffeeCo
CoffeeCo cresceu 50 lojas. Vende café, acessório e drink da casa. Campanha nacional mexe no preço; loja também. Loyalty: CoffeeBux — 1 drink grátis a cada 10, em qualquer unidade. Assinatura e loja online no horizonte.
Sessão de modelagem com barista, matriz e fornecedor. A língua que sobra:
Domínios no mapa: Store, Products, Loyalty, Subscription. Produto começa como value object — imutável, descreve, compara por valor. Se um dia a identidade doer, sobe para entidade.
Pacote internal/ no Go: o compilador impede o domínio de vazar para outro módulo. É o bounded context no filesystem. Pagamento (CoffeeBux, cartão, dinheiro) vira serviço de infraestrutura. Desconto da loja mora no repositório da loja, não espalhado no handler.
Monólito continua a escolha certa enquanto o deploy é um, o time é um e o domínio ainda cabe na cabeça. Dói depois: boot de minutos, escala numa dimensão só, commit de uma linha sobe o binário inteiro, stack travada. Aí o mapa de contextos já existe para cortar.
Monólito legado, sem DDD desde o dia um: comece pela língua com o especialista. Repositório e agregado podem esperar. Vale o tempo desacoplar Stripe e e-mail — a regra de negócio fica visível, o provedor vira plug.
/// 07 · Microserviço
O segundo sistema do livro é o time de recommendations de um comparador de viagem. Precisa do time de partnerships, cuja API é mal-humorada, sem paginação, ISO-3166 e ISO-8601, e “vamos reescrever, não acopla”.
Ports and adapters: handler HTTP de um lado, adaptador do parceiro do outro, domínio no meio. O serviço não cai se o vizinho cair — degrada. Deploy, escala e linguagem podem divergir. O preço é operação: Kubernetes, latência, teste de jornada, falha que parece aleatória. Boyle pergunta se a liderança vai pagar a conta. Sem sim, fica no monólito.
/// 08 · Distribuído
Sistema distribuído: escala, tolera falha, parece um só para o usuário, é heterogêneo e replica estado. CAP obriga a escolher dois. Mongo é CP. Cassandra é AP.
Comando muda estado e devolve erro ou nil. Query responde e não muta. No Go, Boyle recusa a interface genérica Command(args ...interface{}) — perde o tipo. Brilha em sistema de eventos: o comando é o emit do domínio. No monólito, só se estiver redondo.
Mudança de estado que importa: user.logged_in, purchase.failed. Header com meta, body com o fato. JSON, Protobuf, Avro. Um contexto ignora o que não é seu; o que é, traduz. Pipeline de eventos deixa encaixar requisito novo no meio — se addressesMatched < 500, dispara o processo leve.
Prepara (trava o recurso) e confirma. Rollback protege a invariante. Bloqueia concorrência. No pior caso, o sistema para até o lock expirar.
Cada ação traz a compensação. Consistência sem travar o cluster. É o padrão que combina com linguagem ubíqua: o negócio já sabe o que fazer quando o pagamento autoriza e o estoque falha.
Message bus no livro: Kafka, RabbitMQ, NATS. Escolha de protocolo, não de religião. O domínio emite; o bus carrega.
/// 09 · TDD ∩ BDD ∩ DDD
Capítulo bônus. TDD: escreve o teste do requisito, vê falhar, escreve o mínimo, refatora. Pacote chapter8_test — black-box, como consumidor. Arquivo vazio no Go passa: Boyle coloca t.FailNow() de propósito.
O ticket da cookie: em estoque e cartão ok → cobra e manda recibo; sem estoque → erro para o caixa pedir desculpa; cartão recusado → erro para banir da loja; e-mail falha depois da cobrança → avisa o caixa, a venda vale. Os t.Run copiam o Given-When-Then. BDD é a língua ubíqua no harness. Go trata teste como cidadão: _test.go fora do binário.
Ainda não escrevemos uma linha de produção e já estamos no requisito.
Boyle · cap. 8
/// 10 · Como aplicar
O livro constrói dois sistemas. O uso da síntese é outro: olhar o produto que já existe e nomear as salas.
Pede a ata. Destaca verbo e substantivo. Monta o glossário. Discute os cantos. Cardápio, pedido, mesa, loja e Pix não são o mesmo pedido.
Onde o contexto vizinho entra, entra adaptador. Stripe, iugu, WAHA, NFe: infraestrutura. A regra de desconto da loja não mora no client HTTP.
Domínio não vira API pública por acidente. Value object primeiro. UUID na entidade. Repositório por agregado quando a transação já dói.
Microserviço depois do mapa. Sem orçamento de observabilidade, o monólito com contextos claros ainda é o sistema simples que o subtítulo promete.
Given-When-Then no t.Run. Falha de e-mail depois do pagamento é caso de domínio, não de log.
Se a nota não passa de 7, colhe a língua e o desacoplo de provedor. Guarda agregado e saga para o dia em que o fluxo passar de 40.
/// 11 · Índice
Antes de Evans, OOD e GoF. Três pilares. Scorecard. Sem Go neste capítulo.
Lead vira customer. OHS, published language, ACL.
Identidade vs valor. Anemia. Fronteira transacional.
Invariante na criação. Um repo por agregado. Três camadas de serviço.
internal/, coffee lover, CoffeeBux, desconto de loja, Stripe à parte.
ACL no parceiro instável. Retry. Ports and adapters.
CAP, CQRS, EDA, 2PC, saga, Kafka / Rabbit / NATS.
O teste copia o ticket. Black-box. Cookie, cartão, recibo.
Uma boa parte do DDD sai direto dos padrões velhos de orientação a objetos.
Eric Evans · Software Engineering Radio
O livro de Evans é leitura essencial para engenheiros sérios.
Martin Fowler · martinfowler.com