Executando verificação de segurança...
1

Três Olhares sobre Arquitetura de Software: Fowler, Evans e Uncle Bob: 3.4 - Mensageria e integração: eventos em três perspectivas

Capa

Depois de percorrer os padrões ligados ao coração do domínio e à persistência de dados, chegamos agora à camada mais visível de qualquer aplicação: a apresentação. É aqui que usuários, sistemas externos e integrações tocam o nosso software pela primeira vez. E, embora pareça apenas uma questão de “UI” ou “framework web”, a forma como estruturamos essa porta de entrada pode fortalecer, ou corroer, toda a arquitetura.

Martin Fowler dedicou uma parte importante do seu catálogo de Patterns of Enterprise Application Architecture a esse tema. Ele descreveu como padrões como MVC, Front Controller, Template View e Page Controller organizam a interação inicial com o sistema. São soluções para problemas recorrentes: evitar duplicação de lógica de roteamento, separar responsabilidades de renderização, manter consistência no fluxo de requisições.

Eric Evans, em Domain-Driven Design, lembra que o papel da camada de apresentação é traduzir intenções. Ela deve transformar cliques, mensagens ou chamadas HTTP em comandos compreensíveis para o domínio, preservando a Linguagem Ubíqua. O usuário nunca deveria sentir que está falando com tabelas ou APIs, mas sim com conceitos do negócio.

Já Robert C. Martin, em Clean Architecture, é incisivo: a web, o framework e a interface são apenas detalhes. Controllers, views ou middlewares não podem ditar a forma do modelo. A arquitetura deve gritar casos de uso, e não endpoints ou verbos HTTP.

Nesta terceira parte da série, exploraremos esses padrões de entrada e integração com o mesmo olhar triplo: Fowler com seu catálogo pragmático, Evans com a defesa do domínio como centro, e Uncle Bob com a disciplina que protege esse centro contra a pressão das bordas.


Índice da Série

Parte 1 – A lógica do domínio

Parte 2 – Persistência e Infraestrutura

Parte 3 – Apresentação e Integração


⚠️ Aviso: Devido ao limite de caracteres do TabNews, os exemplos completos deste artigo estão hospedados em GitHub Gists. Ao longo do texto, você encontrará links diretos para cada trecho citado.

Mensageria e integração: eventos em três perspectivas

Integrar é fazer duas linguagens conviverem em paz. Em Domain-Driven Design, Eric Evans deixa claro que não existe “o” modelo único: há Bounded Contexts e um Context Map descrevendo como se relacionam (Conformist, Customer/Supplier, Shared Kernel, Separate Ways). Para proteger o seu modelo na fronteira, usamos Anti-Corruption Layer (ACL); para expor o seu modelo, usamos Open Host Service (OHS) com Published Language (PL) — contratos claros e estáveis, alinhados à linguagem ubíqua do seu domínio .

Martin Fowler, em Patterns of Enterprise Application Architecture, materializa isso no cenário distribuído: Remote Facade como uma “pele” coarse-grained e DTOs para transporte de dados. A recomendação é desenhar a aplicação como se fosse local e só então “esticar” a distribuição adicionando a fachada remota e os DTOs — a fachada não contém regra de domínio, apenas mapeia e orquestra a chamada aos casos de uso.

Robert C. Martin (Clean Architecture) fecha o tripé com a Dependency Rule: o mecanismo de entrega (HTTP, gRPC, mensageria) é detalhe periférico. Entidades e casos de uso não sabem que existe rede; as dependências apontam para dentro. Se amanhã trocarmos HTTP por gRPC (ou por um listener de mensagens), a mudança acontece “nas bordas”; o núcleo permanece intocado .

Abaixo, mantemos o mesmo domínio — Pedidos (Orders) — e alternamos explicação e código. Primeiro, mostramos OHS + PL aproveitando Facade + DTO Mapper em dois canais (HTTP e gRPC). Depois, tratamos ACL + Translator com consistência eventual, modelando a confirmação assíncrona do pagamento.


OHS + PL em dois canais (valor real de Facade + Mapper reaproveitados em HTTP e gRPC)

A Published Language são os DTOs públicos que qualquer cliente HTTP, gRPC, mensageria entende. A Remote Facade é a superfície coarse-grained que recebe esses DTOs, delega ao caso de uso e devolve DTOs de resposta. O DTO Mapper concentra a tradução entre PLmodelos internos. Assim, vários canais reaproveitam o mesmo contrato e a mesma lógica de mapeamento, mantendo os adaptadores finos e específicos de transporte.

https://gist.github.com/rcarubbi/3a9d993cd1c65b2330633b9b06186f21

O valor aparece quando dois canais consomem a mesma fachada e mapper. O controller HTTP trata de roteamento, status code, autenticação; a service class gRPC trata de metadata e bindings. Ambos delegam para a OrdersFacade e não reimplementam o mapeamento.

https://gist.github.com/rcarubbi/b4ed3aef70a6a7101f0f0f9a3cf5cd59

Aqui, a Published Language (DTOs públicos) e a Remote Facade dão estabilidade de contrato e reuso entre canais. Se amanhã adicionarmos mensageria, o adaptador de fila só desembrulha o mesmo PlaceOrderRequestDto e delega para OrdersFacade. As dependências seguem apontando para dentro — exatamente a disciplina de Clean Architecture .


ACL + Translator (pagamento assíncrono)

Agora, integração saindo do nosso contexto: o processador de pagamentos tem outra linguagem e outro ritmo. Em vez de impor que ele responda “pago” na hora, aceitamos consistência eventual: a autorização pode voltar como PENDING e a liquidação (settlement) chega depois, em um callback (HTTP) ou mensagem.

A ACL (Anti-Corruption Layer) faz a tradução entre a Published Language do parceiro e a nossa. Em Clean Architecture, o caso de uso não conhece rede; ele depende de portas. Teremos duas portas: uma para solicitar autorização e outra para aplicar a liquidação quando o parceiro notificar.

https://gist.github.com/rcarubbi/8b946d34223785828a3a0a83f648e98e

A Published Language do parceiro é outra; a ACL Translator converte de/para o nosso idioma.

https://gist.github.com/rcarubbi/38dfd2a33501f74ce0e7c5882b5db167

A implementação concreta do gateway vive na borda e pode chamar HTTP/gRPC. O ponto é: se vier PENDING, nosso caso de uso marca o pedido como “Aguardando Liquidação” e continua; quando o parceiro notificar (callback/mensagem), aplicamos a liquidação via a outra porta (IPaymentSettlementHandler), fechando o ciclo eventualmente consistente. Nada disso coloca o domínio na obrigação de “saber” o protocolo do parceiro — a ACL traduz nas bordas, como Evans recomenda .

https://gist.github.com/rcarubbi/9220cca467814fff328daf05361070e3

No caso de uso “Registrar Pedido”, lidamos com os três desfechos: Approved, Declined, Pending. Em “Pending”, gravamos o pedido como “Aguardando Liquidação”, consistência eventual, e devolvemos a confirmação do registro ao cliente. Quando o callback do parceiro chegar (segundo canal), converteremos a notificação em um comando interno e marcaremos o pedido como Pago ou Rejeitado.

https://gist.github.com/rcarubbi/8a658eb812d6b599f088306fb6519ade

⚠️ Disclaimer — Idempotência na autorização externa

Esta implementação assume que o parceiro trata a chamada de autorização como idempotente em caso de falha de persistência local e retry do nosso lado. Em particular, ao executar var auth = _payments.Authorize(new PaymentRequest(order.Number, order.Currency, order.Total()));, se _orders.Save(order) falhar e for necessário reexecutar o fluxo, o parceiro deve deduplicar tentativas para não gerar autorizações duplicadas. Se o parceiro não for idempotente, você deve adaptar o desenho usando um domain event com outbox pattern.

Para fechar o ciclo, criamos um adaptador de entrada (HTTP ou gRPC) específico para o callback do parceiro. Ele recebe a Published Language do parceiro, usa o Translator da ACL e aplica a liquidação via um caso de uso/serviço de aplicação interno. Repare como isso pode ser outro canal, totalmente independente do canal pelo qual o pedido foi criado (HTTP/gRPC/mensagem).

https://gist.github.com/rcarubbi/92af90a68735048a7d67b3e2058c3387

Callback HTTP (parceiro → nós) usando a ACL:

https://gist.github.com/rcarubbi/c05b2d89721b90e66ce5106044f89d18

Se o parceiro preferir gRPC ou mensageria para o callback, basta criar outro adaptador que desembrulhe PartnerSettlementDto e invoque o mesmo _apply.Apply(notification). O núcleo não muda — o mecanismo de entrega é detalhe; a fronteira traduz e protege o modelo, como pedem Evans e Robert C. Martin.


Conclusão

No cenário OHS + PL, o mesmo contrato serviu dois canais (HTTP e gRPC) por meio de uma Facade com DTO Mapper reaproveitável. Resultado prático: estabilidade de contrato, redução de duplicação e adaptadores de transporte mais finos. Já em ACL + Translator, modelamos a autorização em dois estágios (autorização → liquidação), recebendo a confirmação por outro canal sem deixar que a linguagem do parceiro contamine o nosso domínio. Em ambos os casos, o núcleo permaneceu alheio ao mecanismo de entrega, e a tradução ficou nas bordas.

Epílogo — Três vozes, uma arquitetura que dura

Arquiteturas que perduram combinam o pragmatismo de Fowler (padrões aplicados onde doem, sem deixar a superfície remota redesenhar o miolo), a estratégia semântica de Evans (Bounded Contexts, OHS/Published Language e ACL para publicar e proteger a linguagem do domínio) e a disciplina estrutural de Robert C. Martin (dependências sempre apontando para dentro, mecanismos de entrega nas bordas). Em termos práticos: modele como se nada fosse distribuído, exponha contratos estáveis e traduza nas fronteiras. O efeito composto disso é simples e valioso: menor custo de mudança, múltiplos canais reutilizando a mesma superfície de aplicação, integrações assíncronas convivendo com consistência eventual, e um sistema que continua falando a própria língua apesar das trocas de frameworks, protocolos e brokers ao longo do tempo.

Carregando publicação patrocinada...