Microservices: Information Hiding
O designer deve fornecer ao usuário pretendido todas as informações necessárias para usar o módulo corretamente, e nada mais.
A ocultação de informações é um princípio da arquitetura de software, introduzido por David Parnas no artigo clássico “On the Criteria to Be Used in Decomposing Systems into Modules”. Esse conceito, que aborda modularidade, ganhou relevância nos últimos anos no contexto dos microservices, onde a independência de implantação e a redução do acoplamento são essenciais. Ao esconder detalhes internos de um serviço e expor apenas o necessário por meio de interfaces bem definidas, é possível construir sistemas mais resilientes, flexíveis e fáceis de evoluir.
Neste artigo, vamos explorar como a ocultação de informações pode ser aplicada em microservices, com base em três fontes:
• O artigo de David Parnas, que introduziu o conceito no contexto da modularidade.
• A visão de Sam Newman, em seu livro “Building Microservices” e na entrevista à InfoQ, onde ele conecta a ocultação de informações à implantabilidade independente.1
• Exemplos práticos que destacam benefícios, erros comuns e ferramentas para nos ajudar.
Vamos iniciar então esse assunto tão importante!
Ocultação de Informações: O que Isso Realmente Significa?
Eu gosto de analogias, por isso aqui vai uma para deixar claro o conceito. Imagine que cada microservice é como uma casa e que a interface pública dele é uma caixa de correio. A casa (o microservice) pode passar por reformas internas — mudar paredes, redesenhar os quartos, concertar o telhado, trocar os móveis ou até refazer toda a estrutura elétrica. No entanto, a caixa de correio permanece no mesmo lugar, acessível e funcional para o carteiro e qualquer outra correspondência que chegar.
O carteiro (que representa os consumidores do seu serviço) não precisa saber o que está acontecendo na casa. Ele só precisa de acesso à caixa de correio — clara, bem posicionada e estável. Enquanto a caixa de correio continuar funcionando como sempre, a casa pode sofrer modificações livremente sem afetar o trabalho do carteiro.
Agora, imagine o caos se, a cada reforma, você mudasse a posição da caixa de correio ou trocasse o formato dela sem avisar o carteiro. Ele não conseguiria entregar a correspondência e todo o sistema falharia.
A ocultação de informações segue exatamente esse princípio: mantenha estáveis e acessíveis apenas os elementos essenciais (como a caixa de correio, ou as interfaces públicas) e permita que o restante mude livremente sem causar impacto em quem consome o serviço.
A ideia principal é simples, mas poderosa: o que acontece dentro do módulo (ou microservice) deve ficar dentro dele. O que é compartilhado externamente deve ser apenas o mínimo necessário para que outros módulos consigam interagir de forma segura. Parece óbvio, mas, na prática, essa abordagem pode transformar completamente a maneira como os sistemas são construídos e mantidos.
Por que Isso é Tão Importante?
Parnas explorou esse conceito há décadas e identificou três grandes benefícios que, ainda hoje, fazem total sentido:
1. Prazo Melhor para o Desenvolvimento
Você já se perguntou por que projetos com múltiplas equipes podem se arrastar por meses a mais do que o previsto? É porque, muitas vezes, os times acabam presos em dependências entre módulos. Quando um módulo é modificado, outros quebram, gerando retrabalho.
A ocultação de informações permite que módulos sejam desenvolvidos de forma independente. Com cada equipe focando no seu próprio domínio e confiando apenas nas interfaces compartilhadas (como a caixa de correio do exemplo), o trabalho se torna mais paralelo, reduzindo gargalos. Ou seja, mais mãos podem trabalhar no sistema ao mesmo tempo, com menos conflitos.
2. Capacidade de Compreensão
Imagine abrir o código de um módulo e perceber que ele depende diretamente de detalhes internos de outros cinco módulos. A confusão que isso gera dificulta a leitura, o entendimento e, consequentemente, a evolução do sistema.
Quando aplicamos a ocultação de informações, cada módulo pode ser entendido isoladamente. A complexidade do sistema como um todo é reduzida porque você precisa lidar apenas com o que está dentro dos limites daquele módulo. Assim como na analogia, você não precisa saber como a casa foi reformada, apenas que a caixa de correio está lá e funciona. Isso facilita não só o trabalho dos desenvolvedores, mas também a entrada de novos membros no time.
3. Flexibilidade
E aqui está o ponto mais importante: um módulo que esconde bem suas informações pode ser alterado sem que outros módulos precisem saber ou se adaptar a essas mudanças. Quer reformular o banco de dados ou refatorar uma lógica interna? Vá em frente! Se ninguém está “olhando” para dentro do seu módulo, você tem total liberdade para evoluir como quiser.
Além disso, essa flexibilidade permite combinações inovadoras. Você pode reutilizar ou recombinar módulos de maneiras criativas, criando novas funcionalidades com pouco esforço — sem bagunçar o trabalho de outros times.
Esses três benefícios — prazo, compreensão e flexibilidade — são mais do que vantagens técnicas. Eles são a base para que times de desenvolvimento entreguem sistemas sustentáveis e escaláveis, mesmo diante de prazos apertados e requisitos que mudam constantemente.
Mas Onde Isso Entra nos Microservices?
Agora, você deve estar se perguntando: “Ok, entendi a importância disso. Mas o que isso tem a ver com microservices e modularidade?”
A promessa dos microservices de oferecer implantabilidade independente é um dos maiores atrativos dessa arquitetura. Afinal, quem não gostaria de poder atualizar ou reimplantar um serviço específico sem causar impacto no restante do sistema? Mas, como Sam Newman aponta em sua entrevista ao podcast da InfoQ, alcançar essa independência requer mais do que boa vontade — é preciso adotar a ocultação de informações como princípio fundamental.
A Definição de Ocultação de Informações em Microservices
Na entrevista, Newman oferece uma definição clara: “Se você pensar em um limite de microsserviço, sua posição padrão é quase que você não expõe nada.” Esse princípio, de expor apenas o mínimo necessário por meio de interfaces externas, está no coração da ocultação de informações.
Quando você expõe métodos, estruturas de dados ou funcionalidades de um serviço, cria um contrato implícito ou explícito com os consumidores externos. Assim que esse contrato é estabelecido, qualquer mudança no microservice precisa considerar o impacto nos consumidores que dependem dessas informações. O resultado? O microservice perde sua liberdade de evolução e, consequentemente, sua capacidade de implantação independente.2
A Conexão Entre Ocultação e Implantabilidade Independente
A ocultação de informações está diretamente ligada à capacidade de um microservice ser alterado ou reimplantado sem “quebrar” consumidores externos. Newman enfatiza que “quanto mais você esconde, mais liberdade você tem como um desenvolvedor trabalhando em um microsserviço para mudar as coisas com segurança.” Isso significa que o segredo para a independência está em esconder detalhes desnecessários e reduzir o que é compartilhado ao essencial.
Se os limites de um microservice forem bem definidos e sua implementação interna for completamente isolada, as mudanças feitas dentro desse limite não causarão um efeito cascata no sistema. Por outro lado, quando detalhes internos vazam, mesmo que de forma acidental, cada mudança pequena pode se transformar em um problema complexo para os consumidores.
Ocultação como uma Forma de Clareza e Controle
Para Newman, a ocultação de informações não é apenas uma questão técnica; é uma questão de clareza. Ele diz: “Ocultar informações é realmente sobre ser claro para um desenvolvedor sobre quais coisas podem ser mudadas com segurança e quais não podem.” Esse entendimento claro cria um ambiente onde os desenvolvedores podem fazer alterações sem o medo constante de quebrar integrações ou causar problemas em produção.
Além disso, a ocultação de informações força um foco nos contratos explícitos das interfaces. Newman destaca que contratos bem definidos são uma ferramenta poderosa para garantir que mudanças possam ser feitas de forma segura. Por exemplo, ao usar contratos REST, gRPC ou eventos, os desenvolvedores sabem exatamente o que pode ser alterado e o que exige compatibilidade retroativa.
A Relação Entre Contratos e Ocultação de Informações
Os contratos são o ponto de interação entre um microservice e seus consumidores. Eles representam o “acordo” explícito sobre o que o serviço fornece e como os consumidores devem utilizá-lo. Nesse contexto, a ocultação de informações desempenha um papel essencial: ela garante que o contrato exponha apenas o necessário, enquanto os detalhes internos permanecem encapsulados dentro do microservice.
Como Sam Newman explica na sua entrevista, “qualquer coisa que você expõe se torna parte do contrato externo”. Isso significa que, se um microservice expõe campos ou endpoints desnecessários, esses detalhes se tornam responsabilidades mesmo que você não tenha planejado intencionalmente. Alterá-los no futuro pode causar quebras nos consumidores, comprometendo a independência de implantação.
Ao ocultar informações, o microservice mantém seus contratos pequenos, simples e confiáveis. Isso permite:
• Estabilidade: Mudanças internas no microservice não afetam os consumidores, pois o contrato permanece inalterado.
• Evolução: Os serviços podem ser reimplementados ou refatorados sem causar interrupções no sistema.
Portanto, a ocultação de informações e os contratos claros e explícitos trabalham juntos para garantir que microservices sejam isolados, previsíveis e sustentáveis.
O que Ocultar e o que Expor?
Newman sugere um modelo prático para lidar com a ocultação de informações:
• O que deve ser escondido: Qualquer detalhe interno da lógica de negócios, estruturas de dados ou decisões de implementação que não sejam essenciais para os consumidores externos.
• O que deve ser exposto: Apenas o que é necessário para que os consumidores interajam com o serviço. Isso geralmente inclui endpoints REST, gRPC ou mensagens de eventos.
Por exemplo, imagine um microservice responsável por gerenciar pedidos em um sistema de e-commerce. Internamente, ele pode usar estruturas complexas para calcular o frete ou aplicar descontos. Esses detalhes não precisam ser expostos. Em vez disso, o que deve ser visível é apenas o resultado dessas operações: o preço final do pedido, exposto por meio de uma API.
Impactos da Ocultação de Informações no Dia a Dia
Para os desenvolvedores, a ocultação de informações é um alívio operacional. Como Newman diz: “Os desenvolvedores não vão para o trabalho diário querendo quebrar sistemas; eles querem fazer um bom trabalho.” Quando os limites de um microservice estão bem definidos e o que é exposto é claro e limitado, os desenvolvedores podem focar na implementação e melhoria contínua do serviço, sem medo de causar um efeito cascata no sistema.
Além disso, isso reduz significativamente o custo da manutenção e da evolução do sistema. Como os contratos são estáveis e bem definidos, as integrações entre serviços são mais fáceis de gerenciar, e os serviços podem ser reimplantados de forma independente.
A Ocultação é a Base!
Quando falamos sobre implantabilidade independente em microservices, é fácil pensar apenas no ato de colocar um serviço em produção. Mas, como Sam Newman aponta, implantabilidade é muito mais do que isso. Trata-se da capacidade de um serviço evoluir, ser atualizado ou substituído sem interromper o restante do sistema. É sobre dar liberdade às equipes para trabalhar de forma isolada, com confiança, sem medo de causar problemas em outros serviços.
Agora pense no que torna isso possível. Em um mundo ideal, cada microservice é como uma unidade independente que pode ser alterada e implantada sem impacto nos consumidores. Mas no mundo real, onde os sistemas distribuídos são complexos por natureza, essa independência só existe se houver limites bem definidos e contratos claros. E, acima de tudo, se os detalhes internos de cada microservice estiverem escondidos — esse é o papel fundamental da ocultação de informações.
Como David Parnas bem coloca, o objetivo do design é fornecer ao usuário “todas as informações necessárias para usar o módulo corretamente, e nada mais.” Em microservices, esse princípio se traduz em expor apenas o que é indispensável por meio de APIs ou eventos, mantendo todo o resto oculto. É essa separação que permite que mudanças internas sejam feitas sem medo de quebrar consumidores.
Depois, há o impacto nas integrações. Imagine que um microservice precise ser atualizado. Se ele expõe mais do que deveria — detalhes internos ou lógicas desnecessárias —, qualquer mudança pode ter um efeito cascata nos consumidores. Isso não é implantabilidade independente; isso é criar um sistema frágil. Ao esconder o que é irrelevante para os consumidores, o microservice se torna flexível. Ele pode mudar, melhorar e até ser substituído, enquanto o contrato com seus consumidores permanece intacto.
E, claro, há o ambiente de produção. Implantabilidade também é sobre resiliência. Serviços que respeitam os limites claros e escondem seus detalhes internos são mais fáceis de monitorar e corrigir em caso de problemas. Imagine um serviço que expõe excessivamente seus detalhes. Qualquer problema interno pode afetar diretamente os consumidores, tornando a recuperação mais complexa. Por outro lado, quando os detalhes estão encapsulados, as falhas podem ser contidas e corrigidas sem que o restante do sistema sofra com grandes impactos.
A Relação Entre Ocultação de Informações e Modularidade em Microservices
Agora que já entendemos os benefícios da ocultação de informações, vamos dar um passo adiante e falar sobre como esse conceito se conecta diretamente à modularidade. Afinal, os microservices são, essencialmente, uma tentativa de aplicar os princípios da modularidade no mundo dos sistemas distribuídos.
Se você pensa em microservices como apenas uma maneira moderna de estruturar sistemas, Sam Newman tem algo a dizer: eles são, antes de tudo, uma arquitetura modular. Mas, como ele ressalta, com a complexidade extra de um sistema distribuído. E é aqui que a ocultação de informações entra como um pilar indispensável.
Modularidade e Ocultação: Dois Lados da Mesma Moeda
A modularidade, conforme explorada por David Parnas, é uma forma de dividir um sistema em partes menores, chamadas módulos, com fronteiras bem definidas. Cada módulo deve esconder suas informações internas, expondo apenas o que é essencial para a interação com outros módulos. Essa separação traz três grandes vantagens: independência, simplicidade e flexibilidade — todas elas fundamentais para microservices.
Agora pense nos microservices: não são módulos? Porém, em vez de serem partes de um único sistema monolítico, são serviços independentes que interagem entre si. A diferença é que essas interações ocorrem em um sistema distribuído, onde a comunicação é feita via APIs, mensagens ou eventos, e não diretamente no código.
Newman destaca ainda que a ocultação de informações reduz o “peso” dos contratos entre os serviços. Quanto menos detalhes um serviço expõe, menos expectativas ele cria em seus consumidores. Assim, é possível fazer mudanças internas com mais liberdade, sem o medo constante de quebrar outros serviços.
“Qualquer método, estrutura ou funcionalidade que você expõe se torna parte do seu contrato externo. Uma vez que os consumidores dependem disso, torna-se difícil mudar.”
Aqui existe um ponto interessante: mesmo décadas antes da era dos microservices, David Parnas já discutia conceitos que ecoam até nossos dias e são extremamente válidos. Veja como suas ideias se traduzem no mundo dos microservices:
1. Fronteiras Bem Definidas: Parnas argumentava que cada módulo deve ter limites claros. Em microservices, isso se reflete em APIs bem projetadas e no conceito de “bounded contexts” da arquitetura de domínio.
2. Ocultação de Implementação: Para Parnas, esconder detalhes internos era essencial. Nos microservices, isso significa limitar o que cada serviço expõe, seja por APIs REST, gRPC ou eventos.
3. Redução de Suposições: Parnas afirmava que um módulo deve fazer o menor número possível de suposições sobre outros módulos. Nos microservices, isso se traduz em contratos claros e mínimos entre serviços, permitindo maior independência.
Se Parnas pudesse observar os microservices de hoje, ele provavelmente veria neles a evolução natural da sua visão sobre modularidade. Porém, como Sam Newman aponta, trabalhar com módulos em um sistema distribuído não é tão simples quanto em um monolito. As interações entre microservices são mais complexas, exigindo cuidado extra na definição de fronteiras e contratos.
Portanto, a modularidade em microservices é viabilizada pela ocultação de informações, que garante que cada serviço mantenha seu isolamento e funcione como uma unidade autônoma. Sem isso, a modularidade se perde, e os microservices se tornam apenas partes fragmentadas de um monolito distribuído.
E, como vimos, tanto Parnas quanto Newman concordariam que sem isso, qualquer promessa de flexibilidade e escalabilidade dos microservices cai por terra. Vamos falar sobre isso no próximo tópico. 👇🏼
Como um Microservice Pode Violar o Information Hiding?
Você já se deparou com um microservice que parece funcionar bem, mas, de repente, uma pequena mudança causa um caos em todo o sistema? Talvez um campo que foi “improvisado” na resposta de uma API quebrou algum consumidor, ou uma regra interna foi exposta e agora todo mundo depende dela. Esse é o tipo de problema que surge quando o princípio de ocultação de informações não é respeitado. Mas como, exatamente, isso acontece?
Tente visualizar mentalmente um microservice que gerencia pedidos em uma loja online. Ele é responsável por processar o status de um pedido e informar ao consumidor se o pedido foi entregue, está em trânsito ou aguardando processamento. Até aqui, tudo bem. O problema começa quando o serviço começa a expor mais informações do que deveria. Ao invés de devolver apenas algo simples como:
{
"status": "Delivered"
}
Ele decide incluir alguns campos extras, como:
{
"status": "Delivered",
"internalOrderId": "abc123",
"processingStep": "validation_completed",
"deliveryCode": "D001"
}
Talvez a intenção inicial fosse facilitar o trabalho de outro serviço que consome esses dados, mas, na prática, isso cria uma armadilha. Os consumidores começam a usar campos como processingStep
ou deliveryCode
, que foram pensados apenas para uso interno. Agora, se o time responsável pelo microservice decide refatorar a lógica ou mudar o nome de processingStep
para algo mais adequado, como currentStage
, vários serviços consumidores vão quebrar. E, de repente, uma mudança que deveria ser interna vira um problema gigantesco.
Como um Microservice Pode Expor Regras de Negócio Internas?
Pense nesse cenário: você tem um serviço responsável por gerenciar pedidos em um e-commerce, e ele possui uma regra interna que diz:
“Se o status do pedido for ‘Processing’ por mais de 24 horas, o pedido será cancelado automaticamente.”
Essa é uma regra totalmente interna, que deveria ser aplicada e gerenciada apenas pelo próprio microservice de pedidos. Mas, em algum ponto, essa regra pode acabar vazando e sendo utilizada por consumidores externos de algumas formas:
A Regra Está na Resposta da API
O microservice de pedidos expõe um endpoint, por exemplo, /orders/{id}
, que retorna o status do pedido. Mas, em vez de manter os campos internos escondidos (que fazem parte das regras), ele inclui informações desnecessárias na resposta, como:
{
"orderId": "12345",
"status": "Processing",
"timeInStatus": "25 hours"
}
Parece um payload inofensivo… Mas o campo timeInStatus
não deveria estar ali. Ele é um detalhe interno usado pelo microservice para monitorar quanto tempo um pedido está em um determinado estado. No entanto, ao expor isso na API, o consumidor pode começar a inferir a regra de negócio e a usá-la no próprio código, criando mensagens como:
“Seu pedido está em processamento há mais de 24 horas e será cancelado em breve.”
O problema aqui é que o consumidor agora codificou uma dependência direta dessa regra interna. Se o microservice de pedidos mudar o limite de tempo de 24 horas para 48 horas ou decidir não cancelar o pedido automaticamente, o consumidor continuará agindo como se a regra antiga ainda fosse válida.
A Regra Está Codificada em um Evento
Outro caso comum ocorre em sistemas baseados em eventos. Imagine que o microservice de pedidos publica um evento para outros serviços sempre que o status de um pedido muda. O evento pode conter algo como:
{
"orderId": "12345",
"status": "Processing",
"timeInStatus": "25 hours"
}
Aqui, novamente, o campo timeInStatus
é exposto, permitindo que os consumidores externos infiram que, após 24 horas em Processing, o pedido será cancelado. O microservice de notificações, por exemplo, pode usar esse campo e criar sua própria lógica para enviar mensagens ao cliente, como:
“Seu pedido será cancelado automaticamente em breve.”
Se o microservice de pedidos mudar a lógica para não cancelar mais pedidos automaticamente ou ajustar o limite de tempo, o evento original não será suficiente para comunicar essa mudança, e o consumidor continuará agindo com base na lógica antiga e totalmente incorreta.
Por Que Isso É Um Problema?
Quando consumidores externos começam a usar regras de negócio expostas — seja por meio da API, eventos ou comunicação informal —, eles criam um acoplamento indesejado com a lógica interna do serviço. O microservice perde a liberdade de evoluir, porque qualquer mudança na regra pode quebrar os consumidores. No exemplo acima:
• Se o limite de tempo mudar de 24 para 48 horas, o serviço de notificações continuará enviando mensagens erradas.
• Se o microservice de pedidos decidir reprocessar pedidos em vez de cancelá-los, os consumidores não saberão dessa mudança e continuarão agindo com base na lógica antiga.
Tá ficou claro o problema, vamos analisar a solução.
Como Evitar Isso?
A chave para evitar esses problemas é esconder as regras de negócio. O microservice de pedidos, por exemplo, poderia:
• Não expor detalhes como timeInStatus
ou qualquer outro campo que permita inferir a lógica interna.
• Publicar apenas eventos de alto nível, como OrderCancelled, em vez de incluir detalhes do processo de cancelamento.
• Encapsular a regra e retornar apenas o resultado final. Por exemplo:
• Em vez de permitir que o consumidor infira quando um pedido será cancelado, o microservice pode incluir um campo explícito como:
{
"orderId": "12345",
"status": "Processing",
"actionRequired": "None"
}
Ao fazer isso, o consumidor não precisa se preocupar com a lógica interna ou como o tempo em Processing é tratado. O microserviço encapsula essa lógica, mantém o controle e pode evoluir sem medo de quebrar outros serviços.
Qual é o ponto que quero trazer?
Como esses problemas acontecem na prática? Muitas vezes, é uma questão de pressa. Talvez um desenvolvedor precise entregar rápido e pensa: “Ah, vou adicionar esse campo na resposta para resolver logo o problema do consumidor”. Ou, talvez, o time esteja preocupado em parecer “útil” e decide expor mais dados do que o necessário, pensando que isso vai ajudar outras equipes. O problema é que, uma vez que você expõe um campo ou uma lógica, isso vira parte do contrato do serviço, e tirar isso depois é como tentar colocar o “gênio de volta na lâmpada”. É complicado.
A chave para evitar essas situações é simples, mas poderosa: exponha apenas o que é essencial. Se o consumidor precisa saber o status do pedido, dê a ele apenas o status, não como você chegou a essa conclusão ou os detalhes internos do seu sistema. Se você tem uma regra de negócio, encapsule essa lógica e devolva apenas o resultado dela, como “Ação requerida: Reprocessar Pedido”. Não exponha o porquê ou o como.
Os limites arquiteturais devem encapsular decisões. Essa encapsulação reduz o efeito cascata de mudanças, protegendo o sistema de efeitos colaterais não intencionais. - Mark Richards & Neal Ford - Fundamentals of Software Architecture
Ocultar informações é sobre projetar sistemas que possam mudar e evoluir sem causar impactos desnecessários em quem consome seus serviços. Afinal, ninguém quer receber ligações ou mensagens de pânico porque uma pequena mudança interna causou um efeito cascata em todo o sistema.
Ferramentas: API Gateway e AsyncAPI
Agora que já falamos bastante sobre a importância de contratos claros, vamos dar uma olhada em como ferramentas podem ajudar a construir e gerenciar esses contratos de forma eficiente, não vou citar o OpenAPI mas ele também é uma opção para construir contratos. Essas ferramentas não apenas organizam a comunicação entre os serviços, mas também ajudam a proteger informações internas e manter as interfaces simples e claras. Vamos explorar como elas fazem isso?
API Gateway
Pense no API Gateway como a “porta de entrada” para seus microservices. Ele atua como um intermediário, garantindo que os consumidores (outros serviços ou clientes externos) só vejam o que é necessário, escondendo completamente os detalhes internos do serviço.
Como o API Gateway ajuda?
1. Camada de Abstração: Ele cria um ponto único de acesso para as APIs dos seus microservices, permitindo que você controle o que é exposto e o que permanece oculto. Por exemplo:
• Se o seu microservice tem vários endpoints internos, o Gateway pode consolidá-los e expor apenas um endpoint externo que representa a funcionalidade desejada.
2. Ocultação de Informações: Mesmo que o microservice passe por alterações internas, como mudanças na lógica ou no nome de endpoints internos, o consumidor não precisa saber disso. O Gateway traduz as solicitações externas para os serviços internos, mantendo o contrato público estável.
3. Contratos Claros: Ele permite definir contratos explícitos e bem documentados para os consumidores. Isso evita que detalhes desnecessários ou internos sejam expostos acidentalmente.
Exemplo prático:
Imagine que você tem um microservice que gerencia pedidos com três endpoints internos:
• /orders/create
• /orders/update
• /orders/internal-status
Um API Gateway pode consolidar isso em um único endpoint público:
• /orders
(usado para criar e gerenciar pedidos).
O consumidor nunca saberá da existência de /orders/internal-status
, porque o Gateway esconde completamente essa parte.
AsyncAPI: Contratos para Sistemas Baseados em Eventos
Se o API Gateway é uma ótima solução para APIs REST e síncronas, o AsyncAPI é o equivalente para sistemas baseados em mensagens e eventos. Ele ajuda a definir e documentar contratos claros para serviços que se comunicam de forma assíncrona, como aqueles que usam Kafka, RabbitMQ ou MQTT.
Como o AsyncAPI ajuda?
1. Documentação Padronizada: Ele permite descrever tópicos de mensagens, formatos e fluxos de comunicação, garantindo que todos os serviços saibam exatamente o que enviar ou receber.
2. Ocultação de Informações: Assim como o API Gateway, o AsyncAPI ajuda a limitar o que é exposto em eventos. Por exemplo:
• Um serviço pode publicar um evento como OrderCreated com campos como orderId e customerName. Detalhes internos, como internalStatus ou auditLog, nunca precisam estar na mensagem.
3. Contratos Explícitos: Com AsyncAPI, você pode formalizar os contratos entre produtores e consumidores de eventos, reduzindo o risco de mal-entendidos ou dependências não intencionais.
Exemplo prático:
Imagine que o serviço de “Pedidos” publica um evento sempre que um novo pedido é criado:
{
"event": "OrderCreated",
"data": {
"orderId": "12345",
"customerName": "John Doe"
}
}
Com AsyncAPI, você documenta exatamente como esse evento funciona, garantindo que os consumidores saibam o formato e os campos necessários. Qualquer detalhe interno, como logs ou etapas intermediárias, permanece encapsulado.
API Gateway e AsyncAPI em Ação: Trabalhando Juntos
Ambos são fundamentais em arquiteturas modernas. Enquanto o API Gateway gerencia a comunicação síncrona (REST/gRPC) e esconde os detalhes internos dos endpoints, o AsyncAPI faz o mesmo em sistemas orientados a eventos.
No final, o objetivo é o mesmo: manter contratos claros, esconder informações desnecessárias e garantir que seus serviços sejam resilientes e fáceis de evoluir.
Um resumo deste artigo para você!
Chegamos ao final, e agora é hora de refletir sobre tudo o que conversamos.
Lembra-se do conceito de modularidade de David Parnas? Ele nos ensinou que um sistema bem projetado é aquele onde cada módulo (ou, no nosso caso, cada microservice) esconde suas decisões internas, expondo apenas o que é estritamente necessário. Esse simples ato de esconder informações gera um impacto profundo em como projetamos, desenvolvemos e mantemos sistemas.
Primeiro, pense no prazo de desenvolvimento. Quantas vezes você já viu projetos atrasarem porque as equipes estavam presas em dependências intermináveis? O Information Hiding resolve isso ao permitir que cada time trabalhe em seu próprio microservice de forma independente, confiando apenas em contratos bem definidos. É como uma fábrica organizada: cada grupo tem sua função, e ninguém precisa saber como o outro opera internamente. O resultado? Menos gargalos, menos conflitos e entregas mais rápidas.
Agora, considere a capacidade de compreensão. Imagine abrir o código de um microservice e perceber que ele depende de detalhes internos de outros cinco serviços. Isso não só dificulta a manutenção, mas torna o sistema como um todo difícil de entender. Quando aplicamos o Information Hiding, cada microservice é uma “caixa preta” bem definida, fácil de entender por si só. Assim como você só precisa saber onde está a caixa de correio da casa para enviar uma carta, em um microservice você só precisa saber o que a API pública promete — o resto é irrelevante.
E então chegamos à flexibilidade. Essa talvez seja a maior vantagem. Quando você esconde as informações e mantém o contrato estável, pode mudar tudo o que está por trás dele: refatorar a lógica, trocar o banco de dados ou até mesmo reescrever o microservice inteiro. Ninguém além do próprio serviço precisa se adaptar. Isso é o verdadeiro desacoplamento — a liberdade para inovar sem causar um efeito dominó no sistema.
Ocultar Informações é Desacoplar Microservices
No final, tudo se resume a uma ideia poderosa: ocultar informações é importante para desacoplar microservices. E microservices desacoplados são mais fáceis de alterar, mais rápidos de evoluir e mais resilientes a mudanças. Essa separação é o que permite que cada serviço seja independente, tanto em sua lógica quanto em sua implantação.
Então, aqui está o desafio: olhe para os seus microservices. Eles estão realmente escondendo o que deveriam? Seus contratos são claros e limitados? Se a resposta for “não” para qualquer uma dessas perguntas, talvez seja hora de reavaliar.
Obrigado por ler até o final! Fico por aqui! Até o próximo post! 😄
Implantabilidade… que palavra estranha! Será que ela existe mesmo no dicionário? Vamos conferir.
De acordo com o Dicionário Online de Português, “implementabilidade” refere-se à possibilidade de execução de uma ação, ou seja, algo que é possível de implementar.
Embora “implantabilidade” não seja um termo comumente encontrado nos dicionários tradicionais, ele é utilizado em contextos específicos, como na tecnologia e inovação, referindo-se à capacidade de um produto ou serviço ser facilmente implantado e adotado por seus usuários.
Quando um microservice expõe métodos, estruturas de dados ou funcionalidades, ele cria contrato com os consumidores externos. Esse contrato é um acordo sobre como o serviço deve se comportar, especificando:
• Quais métodos podem ser chamados (ex: APIs disponíveis).
• Quais dados devem ser enviados e recebidos (ex: formatos JSON, XML).
• Quais respostas ou comportamentos o consumidor pode esperar.
Mas o que significa esse contrato ser implícito ou explícito? Vamos detalhar.
Contratos Explícitos: Tudo Está Claro
Um contrato explícito é formalizado e documentado, deixando claro para o consumidor quais funcionalidades o microservice oferece e como utilizá-las. Ferramentas como OpenAPI (antigo Swagger), gRPC e até mesmo bons manuais de documentação de API ajudam a criar contratos explícitos.
Exemplo:
Imagine que você está desenvolvendo um sistema de pedidos:
• O serviço de “Pedidos” (Service B) tem uma API documentada que especifica:
• Endpoint: /orders/{orderId}
• Método: GET
• Entrada: { orderId: string }
• Saída: { status: string, deliveryDate: string }
Nesse caso, o consumidor sabe exatamente o que enviar e o que esperar como resposta. Esse contrato explícito facilita a comunicação e reduz mal-entendidos.
Vantagens do contrato explícito:
• Clareza: Os consumidores sabem exatamente o que o serviço oferece.
• Previsibilidade: Mudanças nos contratos podem ser comunicadas com antecedência (exemplo: versionamento de API).
• Menos riscos: Testes de contrato podem ser automatizados para garantir que o serviço ainda cumpre o acordo.
Contratos Implícitos: O Perigo Escondido
Um contrato implícito ocorre quando um serviço não documenta formalmente suas interfaces, mas os consumidores assumem como ele deve funcionar com base no comportamento observado. Esse tipo de contrato é perigoso porque os consumidores dependem de detalhes que podem mudar sem aviso.
Exemplo:
No mesmo sistema de pedidos, imagine que o consumidor descobre que o serviço retorna uma estrutura extra não documentada, como:
{
"status": "Delivered",
"internalCode": "D001"
}
Embora o campo internalCode
não tenha sido documentado, o consumidor decide utilizá-lo em sua lógica. Isso cria uma dependência implícita de um detalhe interno que o serviço nunca quis expor.
Se, em uma atualização, o serviço decide remover o campo internalCode
(já que ele não fazia parte do contrato explícito), o consumidor quebra. O que parecia ser uma alteração segura no microservice agora causa problemas em outro serviço.
Problemas do contrato implícito:
• Falta de controle: Os consumidores podem depender de detalhes que você nem sabia que estavam sendo usados.
• Dificuldade de evolução: Alterações internas podem ter impactos inesperados em consumidores externos.
• Frustração: Times perdem tempo tentando corrigir dependências invisíveis.
Por Que Isso Afeta a Liberdade de Evolução?
Sempre que um contrato (explícito ou implícito) é estabelecido, o microservice passa a ter um compromisso com os consumidores. Isso significa que qualquer mudança no serviço precisa ser feita com cuidado para não quebrar o contrato.
Se o contrato for explícito, você tem maior controle e previsibilidade, pois sabe exatamente o que está sendo usado pelos consumidores. Já um contrato implícito cria uma “gaiola invisível”: você não sabe o que pode mudar sem causar problemas, o que torna o serviço mais difícil de evoluir.
Resumo:
• Quanto mais contratos implícitos existirem, mais limitado será o microservice.
• Contratos explícitos oferecem maior liberdade, pois as expectativas estão bem definidas.
Como Evitar Contratos Implícitos?
1. Documente Suas APIs: Use ferramentas como OpenAPI para formalizar o contrato.
2. Restrinja o Que é Exposto: Exponha apenas os dados e funcionalidades necessários. Evite campos desnecessários.
3. Faça Testes de Contrato: Automatize a validação para garantir que o serviço cumpre o contrato formal.
4. Comunique Mudanças: Sempre que for necessário alterar o contrato, use versionamento (ex: /v2/orders
) para não impactar consumidores antigos.
A chave para manter a independência de implantação de um microservice é minimizar os contratos implícitos e garantir que os contratos explícitos sejam claros, pequenos e fáceis de manter. Assim, você cria sistemas mais resilientes e evita problemas de acoplamento indesejado.
Fala rafa, mto bom o artigo como sempre, poderia fazer um artigo sobre API first