03 - Bounded Context (Contexto Delimitado)
video: https://youtu.be/... no frontmatter da nota.Bounded Context (Contexto Delimitado)
Um bounded context é a fronteira explícita dentro da qual um modelo de domínio é válido e sua
linguagem ubíqua é consistente. Fora dessa fronteira, os mesmos termos podem significar coisas
diferentes e as mesmas palavras seguem regras distintas, então o modelo não deve ser compartilhado.
O conceito existe porque nenhum modelo único consegue descrever um negócio inteiro sem se tornar
vago, e a solução não é um modelo gigante, e sim vários modelos pequenos e precisos, cada um com
escopo declarado. Em produção, o bounded context é a unidade prática de propriedade de código, de
time e de deploy em arquiteturas de serviços. Times que ignoram fronteiras acabam com um banco de
dados compartilhado que acopla deploys e com traduções implícitas espalhadas pelo código.
Fronteira de validade do modelo
Um modelo é uma simplificação útil, e toda simplificação vale apenas para um propósito. O modelo de
cliente do time de vendas otimiza a conversão e guarda histórico de interações; o modelo do time
fiscal otimiza conformidade e guarda inscrições e regime tributário; nenhum dos dois está errado,
eles apenas respondem perguntas diferentes. A fronteira declara onde cada simplificação vale, de
modo que dentro dela todo termo tem um único significado e toda regra tem um único dono. Essa
declaração costuma ser documentada em um diagrama de context map, que mostra os contextos e as
relações entre eles. Sem esse registro, a fronteira vive apenas na memória dos integrantes antigos e
desaparece na primeira troca de time.
O problema que resolve: o mesmo termo, significados distintos
O exemplo clássico é a palavra cliente. Em vendas, cliente é quem está no funil: tem score de
crédito, preferências de produto e histórico de carrinhos. Em faturamento, cliente é o sacado da
nota: tem razão social, CNPJ, regime de impostos e endereço de cobrança. Tentar unificar os dois em
uma única entidade gera uma classe com dezenas de campos opcionais, validações que só valem para
metade dos casos e um time com medo de tocar qualquer atributo. O mesmo acontece com produto entre
catálogo e estoque, e com pedido entre vendas e logística. O bounded context resolve permitindo que
cada área mantenha seu próprio modelo do conceito, mais enxuto e mais fiel à sua função.
Sinais de que a fronteira existe e funciona
- Dentro do contexto, cada termo do glossário tem exatamente um significado, e conversas com o
especialista não exigem cláusulas explicativas. - O código de um conceito concentra-se em um módulo ou serviço, sem réplicas espalhadas com nomes
parecidos e regras divergentes. - Um time é responsável pelo contexto e responde por suas mudanças, seus incidentes e seu roadmap.
- Mudanças de modelo acontecem sem negociação com outros times, porque o contrato externo é estável
mesmo quando o modelo interno muda.
Cliente em vendas e cliente em faturamento
O código a seguir, em Java, mostra o mesmo conceito modelado de forma independente em dois
contextos. Note que os identificadores são compartilhados por valor, mas atributos e regras são
próprios de cada contexto.
// Contexto: Vendas. Cliente é quem compra, importa a relação comercial.
public final class ClienteDeVendas {
private final ClienteId id; // identidade compartilhada com os demais contextos
private final Segmento segmento; // usado para campanhas e preços
private ScoreDeCredito score; // regra exclusiva de vendas
private HistoricoDeInteracoes historico;
public boolean podeReceberCupomDeAltoValor() {
return score.acimaDe(700) && historico.comprasNoUltimoAno() >= 3;
}
}
// Contexto: Faturamento. Cliente é o tomador do serviço, importam dados fiscais.
public final class ClienteDeFaturamento {
private final ClienteId id;
private final RazaoSocial razaoSocial;
private final Cnpj cnpj;
private final RegimeTributario regime; // regra exclusiva de faturamento
public Aliquota aliquotaAplicavel() {
return regime.aliquotaPara(razaoSocial.municipio());
}
}
Nenhum dos dois modelos está incompleto, porque nenhum deles tenta ser o modelo de cliente universal.
Quando vendas precisa emitir a nota, ela não navega pelos objetos internos de faturamento: recebe
um contrato estreito com apenas os dados fiscais necessários. A tradução acontece na fronteira,
uma vez, e não em todos os pontos que tocam o conceito.
Context map e relações entre contextos
O context map registra como os contextos se relacionam, e cada relação tem um padrão adequado.
Parceria descreve times que coordenam mudanças em contratos compartilhados. Fornecedor e cliente
descreve a relação em que o fornecedor atende às necessidades do cliente em prioridade acordada.
Conformista descreve o cliente que adota o modelo do fornecedor sem tradução, aceitando o custo.
Camada anticorrupção descreve a tradução protetora na fronteira com sistemas legados ou de
fornecedores, impedindo que conceitos externos vazem para dentro do modelo. Núcleo compartilhado
descreve um pedaço pequeno de modelo mantido em conjunto, útil mas arriscado quando cresce. Serviço
aberto e linguagem publicada descrevem contratos bem documentados que vários consumidores integram.
Escolher o padrão conscientemente evita que a integração mais cômoda do momento vire acoplamento
permanente.
Bounded context versus subdomínio
Subdomínio é um conceito do espaço do problema: uma fatia do negócio com função própria. Bounded
context é um conceito do espaço da solução: uma fronteira onde um modelo concreto vive. O
desejável é uma correspondência próxima, um contexto por subdomínio, mas a realidade raramente é
perfeita: um subdomínio legado pode exigir dois contextos durante uma migração, e um sistema simples
pode cobrir dois subdomínios com um único contexto sem perda relevante. A checagem prática é a
linguagem: se dentro da fronteira suposta o mesmo termo carrega dois significados, existem dois
contextos ali, independentemente do que diz o diagrama. Contextos também não precisam coincidir com
serviços: um serviço pode hospedar dois contextos separados por módulos, e isso é preferível a
dividir em dois serviços que compartilham o mesmo banco.
Fronteiras no código e na entrega
No código, a fronteira aparece como módulo ou pacote com regras de dependência explícitas, e no
runtime aparece como processo, fila ou contrato de API. O importante é que o modelo de um contexto
não seja importado por outro: tipos internos, entidades e exceções ficam contidos, e o que cruza a
fronteira são contratos estáveis com seus próprios dados. Em monólitos modulares, ferramentas de
análise estática podem barrar imports entre contextos, e em serviços o contrato de API cumpre esse
papel naturalmente. Banco de dados é o ponto de maior descuido em produção: tabelas compartilhadas
entre contextos criam acoplamento de schema, e qualquer alteração de coluna vira negociação
multilateral. O padrão seguro é um schema por contexto, com acesso cruzado apenas por contratos.
Sinais de fronteiras erradas
| Sintoma em produção | Causa provável | Mitigação |
|---|---|---|
| Mudança simples exige edição em cinco repositórios | Fronteira cortada no lugar errado ou inexistente | Redesenhar a fronteira guiado pela linguagem do negócio |
| Entidade com dezenas de campos opcionais e validações condicionais | Modelos de contextos distintos fundidos em um só | Separar os modelos e traduzir apenas na fronteira |
| Deploy de uma feature trava na fila de outro time | Acoplamento de dados ou de código entre contextos | Schema próprio por contexto e contratos versionados |
| Tradutores de objeto espalhados por todo o código | Conceitos externos entrando sem camada anticorrupção | Concentrar a tradução em uma borda única e testada |
| Discussões infinitas sobre o significado de uma palavra | Contextos sobrepostos usando o mesmo vocabulário | Declarar a fronteira e manter um glossário por contexto |
Erros comuns e boas práticas
- Criar um contexto por camada técnica, como contexto de persistência ou contexto de API. Fronteira
válida é delimitada pelo negócio, não pela tecnologia. - Compartilhar um modelo de entidades entre contextos para economizar código. A economia de hoje
vira acoplamento que congela a evolução de todos os envolvidos. - Deixar o context map apenas no slide da fase de descoberta. O mapa precisa viver no repositório e
ser atualizado quando uma relação muda. - Unificar contextos durante uma migração "para simplificar". Migração é o pior momento para
fundir modelos, porque cada passo precisa ser reversível. - Ignorar a língua falada nas reuniões como sinal de fronteira. Quando o time troca de vocabulário
no meio da conversa, ali existe uma fronteira real.