Micro Serviços: Dados, Eventos e Estado

Marcelo M. Gonçalves
7 min readMay 3, 2023

--

A adoção dos event-driven microservices sugere transformações em diversos estágios e áreas de uma organização, incluindo a área de dados. Arquiteturas orientadas a eventos (EDA) baseiam-se em dados e suas transições de estado para garantir a integridade e sucesso nas transações efetuadas. Os dados podem ser considerados a parte mais complexa durante o processo de adoção dos micro serviços.

Em termos de critérios de design e arquitetura de software, a flexibilidade e agilidade podem ser consideradas as características mais importantes de uma aplicação. A capacidade em responder rapidamente a mudanças são a razão para terem tornado a adoção aos micro serviços um padrão pela maior parte das organizações, possibilitando o desenvolvimento de produtos cada vez mais escaláveis.

Em 1967, Melvin Conway publicou um artigo sugerindo que que as organizações produzem softwares os quais refletem a estrutura de comunicação dos times que os constroem. Refere-se a como os designers se comunicam e pensam a respeito do domínio de dados, também ao tamanho e quantidade de times envolvidos no ciclo de desenvolvimento de um produto. A comunicação ditará o design e ao transferirmos o mesmo conceito para a arquitetura de software, Conway entendeu que os acoplamentos são criados e influenciados pela comunicação humana e aceitar a Conway’s Law trata-se de algo superior a simplesmente ignorá-la (Inverse Conway Maneuver).

Além disso, Conway observou que o design inicial de um sistema dificilmente se manteria neste estado permanentemente, devendo sofrer mudanças e nesse caso a valorização da agilidade/flexibilidade logo em estágios iniciais de arquitetura e desenvolvimento implicará em facilidades durante seu ciclo de vida.

A escalabilidade e agilidade são facilmente comprometidas ao adicionarmos acoplamento na camada de dados.

O ponto de partida para descobrirmos o preço a ser pago pela flexibilidade pode encontrar-se nas respostas a perguntas como: Qual a complexidade em distribuir/mover uma base de dados relacional? Quão complexo seria manter o estado e integridade dos diversos micro serviços? E as complexidades de infraestrutura e comunicação (Service mesh layer)? Em se tratando de dados, seria arriscado ingressarmos na jornada aos sistemas distribuídos sem refletir sobre esses questionamentos iniciais.

Gerenciamento de Estado

Eventos favorecem a execução das Business transactions proporcionando consistência entre os serviços envolvidos em uma transação através da consistência eventual. Transações eventualmente consistentes são compostas por passos/ações distribuídas, neste contexto, cada ação responsabiliza-se em atualizar seu próprio estado, disparando os próximos eventos como insumos para execução das próximas ações.

ACID transactions (camada de persistência/dados) e Two-phase commit (2PC) para transações distribuídas, protegem a consistência dos dados e facilitam a execução atômica (commit / rollback) dos processos. A inviabilidade de utilização destes padrões em contextos distribuídos e altamente disponíveis (CAP Theorem) expõe um grande desafio: garantir a integridade dos dados. Neste caso, a adoção das BASE Transactions (em conjunto com Sagas / Process Manager — Choreography / Orchestration) favorecem e facilitam atingirmos a integridade dos dados espalhados pelos diversos serviços ao longo do Landscape do contexto de negócio para determinada funcionalidade. Em alguns casos, processos podem levar longos períodos para concluir sua execução (Long-running Business Transactions), obrigando a criação de mecanismos que lidem com essa condição.

As transições de estado das entidades de negócio necessitam de um mecanismo de gerenciamento, permitindo a ordenação e organização das tarefas executadas respeitando uma ordem cronológica.

Conceitualmente, Sagas podem ser implementadas como máquinas de estado (State machines) compostas por elementos como estado e transição. Uma transação parte de um estado inicial, acompanhado de uma única ou diversas possibilidades de estado finais aceitáveis (sucesso ou falha), finitos e determinísticos. A transição entre os estados ocorre através do consumo de eventos, podendo assumir estados transitórios quando uma transação não possui condições de concluir atomicamente. Ao mantermos estados bem definidos no processo, evitamos problemas de race conditions nas transações em progresso, podendo ser a qualquer momento suspensas ou canceladas sem interferências.

Ao desenharmos micro serviços precisamos identificar os possíveis estados aceitáveis de acordo com o domínio de dados, necessitando serem válidos em termos de regras de negócio. Os dados são artefatos essenciais em qualquer condição e a base para a criação de eventos e composição de máquinas de estado, permitindo uma comunicação desacoplada e assíncrona seja possível no contexto dos sistemas distribuídos.

Quando um passo de uma transação falha, seja por erro técnico / sistêmico ou por violação de uma regra de negócio, precisamos que os passos executados com sucesso, anteriores ao erro, sejam desfeitos (Compensating Transactions).

A Importância dos Dados

Os dados representam a parte mais complexa e também fundamental de um sistema distribuído executando em uma arquitetura orientada a eventos. Dada sua importância e fragilidade, o domínio de dados precisa estar protegido de intervenções externas, necessitando de cuidados ao descrevermos as transações e procedimentos que irão acessá-los.

Eventos são a essência para a consistência eventual ao mesmo tempo em que os dados são a essência para a elaboração dos eventos. A relevância e precisão dos dados durante a criação de eventos de domínio ou de integração irá determinar a qualidade da informação transitada entre os contextos de negócio (Bounded Contexts, em Domain-Driven Design).

Eventos simplesmente trafegam dados entre serviços e contextos, herdando a qualidade implícita dos dados existentes. Em outras palavras, quanto melhor a qualidade dos dados, mais expressivos serão nossos eventos, reduzindo sua complexidade e facilitando a identificação dos mesmos tanto inner quanto interdomínio ao longo do cluster / broker.

Padrões como Event-sourcing utilizam uma sequência de eventos para determinar o estado de uma entidade, aplicando-os em sequência na ordem em que foram criados, podendo valer-se de técnicas como snapshot para ganhar performance.

O processo de distribuição dos dados de uma aplicação, quebrando o domínio de negócio, configura-se altamente complexo a depender do controle de mudanças de estado necessárias para atender os requisitos individuais de cada software. Em uma arquitetura de micro serviços, a comunicação ocorre exclusivamente através de eventos e cada serviço mantém a integridade e gerenciamento em cima dos seus próprios dados, não possibilitando o acesso externo e garantindo validação e transições do estado para propriedades aceitáveis.

A Anatomia dos Eventos

Eventos, sejam de domínio ou integração, representam fatos estruturalmente compostos por atributos relevantes ao seu contexto de disparo, em conjunto produzem sentido a uma cadeia de acontecimentos dos fluxos de negócio. Quando projetados, reproduzem a ocorrência de fatos relevantes ao seu contexto do domínio de negócio. Outros serviços são estimulados através da inscrição na ocorrência destes eventos, reagindo em resposta às projeções de mudanças de estado. A ocorrência de determinados eventos podem estar associados a geração de diversos novos, resultando em efeitos colaterais (side-effects) a partir do processamento de solicitações/comandos de origem.

Eventos formam a base, conceitualmente distintas, tanto para sistemas/serviços EDA (Event-driven Architecture) quanto para Event-based Microservices.

O formato dos dados utilizados para comunicação pode seguir alguma lógica, em termos de estrutura (Apache Avro) ou trabalhar apenas com a flexibilidade de padrões como JSON. Contratos sobre os dados (Event-driven Data Contracts) a serem adotados pelos consumidores/produtores de eventos podem facilitar a manutenção e versionamento sob o preço de torná-los rígidos. A definição estrutural de dados e sua lógica de disparo compõem papéis fundamentais na concepção de novos eventos a depender dos seus tipos. Eventos podem somente notificar a ocorrência de fatos (Event Notification) ou empregar padrões como Event-carried Transfer, refletindo em seu payload os dados necessários para seu significado além do contexto de criação, estendendo-se para diversas fronteiras inter-domínio: eventos de integração.

Diferente das mensagens, a nomenclatura dos eventos deve respeitar regras como nomeá-los com o verbo passado: PriceUpdated, CustomerAdded, OrderCreated, sugerindo que um fato ocorreu, de forma imutável e irreversível.

A Arquitetura de Eventos (EDA)

Claude Shannon, conhecido como o pai da teoria da informação, identificou a principal barreira durante a comunicação: garantir que o consumidor de uma mensagem consiga interpretá-la com precisão a partir da intenção do seu produtor, garantindo que a integridade do conteúdo se mantenha sem subjetividades. Assim, a produção de um entendimento comum da mensagem tanto para consumidor quanto para o produtor eliminará o risco de torná-la incompleta ou mal compreendida.

The asynchronous coffee shop apresenta uma boa analogia ao EDA (Event-driven Architecture).

Em uma arquitetura de eventos, o evento trata-se da mensagem como parte fundamental e unidade de comunicação primária, podendo descrever com precisão a ocorrência de fatos e seus motivadores. Os eventos representam cidadãos de primeira classe (First class citizen), podendo ser combinados e em conjunto relatarem históricos completos dos acontecimentos dentro de um sistema. Arquitetura de eventos, combinados com micro serviços, agregam características em comum para aplicações/serviços como baixo acoplamento, alta disponibilidade e escalabilidade.

Estilos de comunicação: assíncrona (event-based) = micro serviços, síncrona (request-based) mini serviços. A arquitetura de micro serviços exige o desacoplamento na comunicação através do envio de eventos.

Na prática, o processo de adoção do EDA conta com padrões de comunicação como Orchestration, Publish/subscribe Pattern (usando Broker), Choreography, Sagas, Process Manager, a variar de acordo com a necessidade/requisitos de cada aplicação/serviço sendo construído. Na maioria dos casos, encontramos serviços implementando padrões híbridos de comunicação (Choreography-Orchestration Hybrid), experimentando conceitos tanto de micro quanto mini serviços em sua composição.

Produtos como RabbitMQ, Apache Kafka e Azure Service Bus implementam o suporte aos padrões de comunicação assíncrono e modelos publish/subscribe, atuando como Message Broker/Message Bus e promovendo o sucesso na adoção de estilos de arquitetura baseadas em eventos. Na maioria dos padrões de comunicação baseados no envio de mensagens, as queues/channels fundamentam formatos de comunicação temporalmente desacoplados. Em todos os casos, os dados e o contexto são ingredientes chave para o sucesso no trade-off deste tipo de arquitetura a depender de cada projeto.

Considerações Finais

Em qualquer situação, experiência e contexto são essenciais para boas tomadas de decisão. Cada sistema, seja distribuído ou não, possui complexidades particulares e precisamos conhecê-los em detalhes para que seja possível a adoção das melhores práticas e estratégias.

Um grande número de ferramentas e frameworks encontram-se disponíveis e cada qual contendo benefícios, pontos fracos e trade-offs, devendo os casos de uso influenciar na solução final. Nada impede que criemos nossos próprios mecanismos de implementação e controle na ausência de ferramentas que atendam nossas necessidades, responsabilizando-se em garantir-lhes qualidade.

No fim do dia, a cultura da organização ditará o sucesso ou fracasso de iniciativas e estratégias envolvendo arquiteturas e sistemas distribuídos cujo os dados estarão no centro, com o grau de importância a variar pela criticidade do sistema que o consome. Nesta jornada todos os papéis são essenciais, variando do Data/Product Owner aos Devs e QAs para que em conjunto seja possível construirmos softwares cada vez mais íntegros e confiáveis com esforços cada vez menores.

--

--