Fundamentos para arquiteturas baseadas em eventos (EDA)

Saber ouvir quase que é responder.
Pierre de Marivaux

Sistemas de software apoiam ou automatizam a execução de atividades. A ocorrência de uma atividade pode ser descrita como “evento”. Sempre que um evento acontece,  é possível gerar um “registro” que o descreva. Este “registro descritivo”, por sua vez, pode conter informações como o tipo da atividade,  quando ela aconteceu, além de outros dados relevantes associados.

A venda de um produto, em um site de e-commerce, por exemplo, é um evento. O registro associado seguramente conteria o momento da venda, o produto que foi vendido, as condições de pagamento e entrega, o cliente que realizou a compra, entre outras informações.

A ocorrência de um evento frequentemente estará associada a uma mudança relevante do “estado interno” de um componente.

A captura de dados de um sensor metereológico, em sistemas com IoT para suporte a indústria agro, também é um evento. O registro associado poderia conter a identificação do sensor, o momento da coleta, temperatura e outras informações relacionadas a condição climática.

Se um “registro descritivo” for gerado para cada atividade relevante suportada por um componente, então, pode-se assumir que a operação desse componente irá produzir um “fluxo de eventos” (event stream). Se este fluxo for apropriadamente armazenado, constituirá uma “fonte da verdade atemporal”. Afinal, conterá um registro não apenas das mudanças de estado que aconteceram, mas também as “motivações” (comportamentos) que geraram tais mudanças, habilitando event sourcing.

Definição: Event Sourcing

Event sourcing consiste no armazenamento de todos os registros descritivos de eventos significativos para o estado de um componente, ao longo do tempo, de forma a permitir, por meio da “replay“, a restauração “posições de estado” consolidadas em qualquer momento do tempo.

Se um componente “produzir” os registros descritivos de um evento, publicando-o em algum mecanismo que permita “observação”, então será possível que outros componentes possam “consumir” tais registros,  apropriadamente, disparando atividades complementares ou atualizações internas de estado. Eventualmente, esses “componentes consumidores”, ao regir um evento, também atuem como “produtores”.

EDA e Domain-driven design

A elaboração de arquiteturas event-driven tem sido facilitada pelo uso de Domain-driven Design.

A identificação de contextos delimitados, com suas respectivas responsabilidades, faz emergir naturalmente os “eventos de domínio”, utilizados sobretudo para a comunicação entre contextos diversos, de maneira alinhada as demandas do negócio.

Aliás, uma das técnicas mais populares para “descoberta” do domínio é centrada em eventos: event storming.

Voltando ao cenário de um sistema de e-commerce, a publicação, por parte do componente “carrinho”, da consolidação de uma venda, pode servir como “gatilho” para que o componente “cobrança” efetue

Event-driven architectures implementam as características descritas nos parágrafos anteriores. Elas se destacam, acima de tudo pela redução dramática do acoplamento e melhora da responsibividade.

 

Diferença entre responsividade e desempenho

Frequentemente, há confusão entre responsividade e desempenho.

A responsividade é um atributo de qualidade que tem relação com o tempo que um usuário ou sistema cliente tem de esperar para, após iniciar alguma operação, poder iniciar outra.

O desempenho, também um atributo de qualidade, tem relação com o tempo total demandado por um sistema computacional para processar uma operação completamente.

Assim, um sistema pode ser responsivo sem ser performático.

Desmistificando Event-driven Architectures

Arquiteturas event-driven atendem objetivos de negócio, respeitando restrições, atingindo determinados padrões em atributos de qualidade, adotando padrões e práticas de design que colocam “eventos” no centro.

Definição: Evento

É o reconhecimento formal de um fato que, provavelmente, provocou uma modificação relevante no estado de uma aplicação.

No dia-a-dia, o termo “evento” também é frequentemente associado com “registros descritivos” contendo informações sobre um evento propriamente dito.

Entretanto, é importante que se destaque que dois sistemas, alegadamente event-driven, podem adotar padrões e práticas de design completamente distintas.

Dois sistemas podem ter arquiteturas event-driven, entretanto, com características significativamente disitintas.

The Many Meanings of Event-Driven Architecture

Martin Fowler, em uma palestra para o GOTO, em 2017, fala sobre os “muitos entendimentos” a respeito

Acessar vídeo

Ativações por notificações

Há três “modelos mentais” para ativação entre serviços:

  1. ativação ativa síncrona, via chamada de APIs, quando um serviço precisa recuperar informações sob controle de outro ou, também, quando um serviço deseja “disparar” uma ação em outro serviço.
  2. ativação ativa assíncrona, via envio de mensagens (comandos  ou queries), através de um message-broker
  3. ativação passiva assíncrona, via eventos, através de event-broker

Arquiteturas event-driven baseiam-se, frequentemente, no terceiro modelo.

Definição: Event-broker

Event-broker é um componente responsável por “receber eventos”, armazená-los em uma fila ou em streams, e disponibilizá-los para outros componentes.

Eventualmente, event-brokers, realizam tarefas administrativas como compactação.

Ativação passiva assíncrona habilita responsibividade e resiliência.

Event sourcing

Fazer com que um sistema lance eventos de notificação para toda modificação no estado de uma entidade abre espaço para algumas soluções interessantes. Podemos, por exemplo, usar os eventos de notificação para, além de permitir implementação desacoplada de consistência eventual entre aplicações,  contar a “história” de entidades, através de uma técnica conhecida como Event Sourcing.

A ideia central da técnica é reconhecer que o estado de uma entidade é “explicado” pela ocorrência de uma sequência clara de eventos, em uma ordem determinada.

Com posse do histórico de eventos de notificação de alteração de estado, podemos restituir qualquer um dos “estados históricos” de uma entidade, bastando, para isso, que realizar um replay do seu histórico de eventos até o ponto apropriado.

Em termos simples, mantendo o histórico de eventos armazenado, podemos “recuperar” o estado de uma entidade, em qualquer momento. Podemos também saber quando mudanças ocorreram, quem fez essas mudanças e qual foi a intenção de negócio. Poderoso, porém complexo!

Sistemas que usam event sourcing costumam manter, além da base de eventos, uma outra base com materializações do estado da entidade em algum momento (geralmente o atual).

Essa materialização é útil por ser extermamente fácil de pesquisar. Podemos pensar essas materializações como “fotografias” do estado da entidade em algum momento. Talvez por isso, convencionou-se utililizar a designação snapshot. 

A base de dados utilizada para armazenar eventos geralmente é nosql. A base para os snapshots pode ser quaquer uma, inclusive relacionais. Aliás, Greg Young, que formalizou o conceito de Event Sourcing, criou um banco de dados para armazenar eventos chamado EventStore.

Datomic: desafios e benefícios de um banco de dados imutável

O Nubank utiliza Datomic, uma base de dados imutável, incremental, registrando eventos.

Acessar vídeo

Há diversas implementações de event sourcing em sistemas de mercado. Ou seja, existem diversos sistemas que mantém um histórico de eventos para justificar o estado de suas entidade. De qualquer forma, event sourcing está longe de ser tópico ou prática comum. Há uma diversidade gigantesca de complicações para uma implementação correta e precisa existir uma justificativa muito forte para adoção.

Event-carrier state transfer

Event brokers se convertem em fonte da verdade, substituindo demandas naturais por recuperação de estado através de requisições de APIs, por consolidação de dados em bases locais, mediante processos de seleção, transformação e redução.

Duas topologias para colaboração em EDA

Há duas topologias fundamentais para implementação de colaboração de arquiteturas event-driven. A primeira delas é baseada na ideia de mediadores ativos e orquestração. A segunda, é baseada na utilização de intermediadores (brokers) e coreografia.

Implementações event-driven que utilizam mediadores ativos e orquestração são mais apropriadas para cenários onde o tratamento de eventos precise ser gerenciado, com forte controle sobre falhas e sobre o andamento. Por outro lado, implementações com brokers e coreografia são mais apropriadas para estruturas dinâmicas, fire-and-forget.

Ambas as abordagens assumem que um “evento inicial” será capturado pelo sistema iniciando processamento.

Usando mediadores ativos e orquestração

A principal característica de uma arquitetura event-driven com mediadores ativos e orquestração é a presença de um componente orquestrador (o mediador ativo) que gerencia e controla o fluxo de atividades, acionando componentes processadores, de maneira organizada, em tempos apropriados.

Nessa topologia, explícita, um “evento inicial” é submetido a uma fila que é “escutada” pelo componente mediador. Este componente, por sua vez, “sabe” quais procedimentos que devem ser executados para “tratar” o evento e em que sequência. Por isso, aciona “executores”, notificando-os com eventos através de canais de eventos point-to-point. Toda vez que um componente executor conclui seu trabalho, geralmente notifica o componente mediador.

Os componentes executores comunicam-se apenas com o componente mediador, nunca com outros componentes executores. Enquanto isso, os componentes mediadores são a “fonte da verdade” com relação ao estágio de execução de um fluxo para tratamento de um evento.

De maneira concreta, quando o mediador exerce função ativa, direcionado o workflow, muitas vezes comunica-se enviando comandos para os diversos processadores.

Geralmente, aplicações com arquiteturas event-driven desenvolvidas nessa abordagem têm diversos componentes mediadores, algumas vezes, agrupados processos correlatos, outras vezes, ponderando estratégias para atender atributos de qualidade como disponibilidade, elasticidade e performance.

Mediadores podem ser implementados em código puro ou utilizando tecnologias como Apache Camel e Mule ESB ou Apache ODE. Há também quem defenda a utilização de ferramentas BPM (e BPA).

Usando brokers e coreografia

Diferente da abordagem utilizando mediadores e orquestração, a utilização de brokers e coreografia se destaca pela inexistência de um componente central, responsável pelo sequenciamento dos trabalhos.

Nessa topologia, implícita, componentes executores são acionados de maneira encadeada, onde um componente executor encaminha um evento para um message broker (como RabbitMQ ou outras implementações AMQP), sempre que conclui seu trabalho, acionando outro(s) componente(s) executor(es), e assim sucessivamente, até que todo o processamento necessário aconteça.

A estratégia de cada componente executor ao encaminhar eventos é do tipo fire-and-forget e é muito importante que todo processamento executado seja notificado, com eventos, ao sistema, ampliando chances de extensibilidade ao custo de desperdício de recursos computacionais (com eventos que não são “escutados” por ninguém).

Fire-and-forget

Fire-and-forget é um padrão de mensageria onde o produtor envia uma mensagem para um consumidor sem esperar resposta. Trata-se da forma mais simples de troca de mensagens.

Fire-and-Forget destaca-se pelo baixíssimo acoplamento, afinal o produtor não precisa saber nada sobre seus consumidores, incluindo quantos existem ou o que eles fazem com a mensagem. Esse tipo de interação sem estado resulta em sistemas de mensagens altamente escaláveis.

A simplicidade e o baixo acoplamento, entretanto, tem seu preço: o tratamento de erros não é possível porque não há feedback sobre a “entrega” das mensagens. Assim, implementações Fire-and-Forget devem utilizar mecanismos mais robustos de “entrega” ou aceitarem que algumas mensagens possam ser perdidas. Além disso, é importante ter consciência de que um consumidor terá pouco a fazer caso a mensagem recebida esteja mal-formada ou com dados inválidos.

Três padrões de processamento

Componentes executores assumem, basicamente, três padrões de comportamento:

  1. processamento simples;
  2. processamento de streams; 
  3. processamento de eventos complexos (complex event processing – CEP).

Enquanto o padrão de processamento simples ocorre tanto arquiteturas orquestradas e coreografadas, processamento de streams e de eventos complexos costumam ser implementadas apenas em topologias coreografadas.

O detalhamento desses padrões de execução estão além do escopo desse livro. Aliás, há obras inteiras dedicadas ao tema.

Processamento simples

Componentes executores que operam adotando processamento simples, “escutam” um determinado tipo de mensagem e executam, para cada ocorrência, algum trabalho. Trata-se do padrão mais fácil de implementar e, provavelmente, mais comum.

Processamento de streams

Componentes executores que operam adotando processamento de streams, podem “escutar” diversas ocorrências de eventos mas só executam algum processamento quando algum critério é atendido. Estes executores operam agregando, analisando e processando streams de eventos.

Considere, por exemplo, um componente executor recebendo todas as transações de um determinado cartão de crédito. Caso duas transações presenciais (dois eventos) sejam executadas em localidades afastadas a uma distância maior do que aquela que poderia ser percorrida no intervalo de tempo entre as ocorrências, este executor poderia gerar um evento (agregação) indicando possibilidade de fraude.

Processamento de eventos complexos (CEP)

Componentes executores que operam adotando processamento de eventos complexos, identificam eventos significativos (como oportunidades ou ameaças) a partir de eventos menos expressivos, oriundos de dois ou mais streams.

Contratos para eventos

O acoplamento entre serviços, tradicional em soluções que não têm EDA, é substituído por acoplamento com contratos dos registros descritivos dos eventos.

Uma bom contrato indica muito mais do que “algo aconteceu”. Bons contratos indicam “tudo que aconteceu”, servindo, de fato, como “fonte da verdade” quando uma verificação ocorrer (sem necessidade de pesquisas complementares).

Idealmente, os “esquemas” associados a contratos devem ficar registrado em um registry.

Reatividade natural

Arquiteturas event-driven, quando bem implementadas, são naturalmente reativas. Ou seja, são responsivas, resilientes, elásticas e orientadas a mensagens.

A responsividade é natural ao processamento assíncrono, comum em EDAs.

A resiliência, por sua vez, é garantida pela estabilidade dos mecanismos de mensageria, que “seguram” os eventos caso exista alguma instabilidade nos componentes processadores.

A elasticidade é possível graças ao desacoplamento dos componentes executores dos demaisEm princípio, quando a demanda sobe pela ocorrência de mais eventos, componentes executores mais pesados podem ter novas instâncias criadas e descartadas conforme a escala oscila.

Por fim, o “fluxo dos eventos” é, quando é usado um mecanismo adequado de mensageria, naturalmente orientado a mensagens.

Conway, outra vez!

Arquiteturas event-driven geralmente são particionadas pelo domínio, o que facilita a formação de times especialistas para características do negócio.

Geralmente, a estrutura dos times irá replicar a topologia dos componentes executores e mediadores. Pelo alto acoplamento aferente, a infraestrutra deverá ter ownership de um time dedicado, mesmo com práticas maduras de DevOps, afim de evitar difusão de responsabilidades. O mesmo é válido para componentes mediadores em função do alto acoplamento eferente.

Indicações e contraindicações

Arquiteturas event-driven são poderosas e estão alinhadas com boas práticas para implementação de sistemas modulares, até mesmo com microsserviços. Entretanto, são naturalmente complexas e quando esta complexidade não for bem justificada, a adoção deste estilo poderá representar incremento de custo difícil de justificar.
A capacidade de lidar melhor com cenários dinâmicos e complexos vem com o custo indireto de lidar com consistência eventual. As facilidades para escalar vem com o custo indireto de maior dificuldade para monitorar. As facilidades para adaptar e responder a demandas do negócio vem acompanhadas de maior dificuldade para testes de integração.

// TODO

Antes de avançar para o próximo capítulo, faça as seguintes reflexões:

  1. O sistema em que está trabalhando agora lida, naturalmente, com eventos?
  2. Baseado em sua experiência, com que frequência o aumento da complexidade justificaria “transformar” sistemas request-driven em event-driven?
  3. Sistemas escaláveis são, geralmente, assíncronos. Concorda com essa afirmação?
  4. Em que cenários adotaria Event Sourcing?

 

Referências bibliográficas

BELLEMARE, Adam. Building Event-driven Microservices: leveraging organizational data at scale. Sebastopol, Ca: O’Reilly, 2020.

BRANDOLINI, Alberto. Introducing Event Storming: an act of deliberate collective learning. Faenza, Itália: Leanpub, 2021-. 70% concluído.

BONÉR, Jonas; FARLEY, Dave; KUHN, Roland; THOMPSON, Martin. O manifesto reativo. 2014. Disponível em: https://www.reactivemanifesto.org/pt-BR. Acesso em: 16 maio 2021.

Descomplicando “Event Sourcing”. Intérpretes: Elemar Júnior. Campo Bom, Rs: Eximiaco, 2020. Youtube, color. Série Padrões Arquiteturais. Disponível em: https://www.youtube.com/watch?v=f4GolIiNIvc. Acesso em: 27 maio 2021.

Descomplicando “Sagas”. Intérpretes: Elemar Júnior. Campo Bom, Rs: Eximiaco, 2020. Youtube, color. Série Padrões Arquiteturais. Disponível em: https://www.youtube.com/watch?v=jMBfO52FttY. Acesso em: 27 maio 2021.

ERDER, Murat; PUREUR, Pierre; WOODS, Eoin. Continuous Architecture in Practice: software architecture in the age of agility and devops. Boston, Ma: Addison-Wesley, 2021. (Vaughn Vernon signature).

GARCIA-MOLINA, Hector; SALEM, Kenneth. Sagas. Princeton, Nj: Princeton University, 1986. Disponível em: https://www.cs.cornell.edu/andru/cs711/2002fa/reading/sagas.pdf. Acesso em: 1 jul. 2021.

HOHPE, Gregor; WOOLF, Bobby. Enterprise Integration Patterns: designing, building, and deploying business solutions. Boston, Ma: Pearson Education, 2003.

PORTER, Michael; HEPPELMANN, James. How Smart, Connected Products Are Transforming Competition. 2014. Disponível em: https://hbr.org/2014/11/how-smart-connected-products-are-transforming-competition. Acesso em: 30 maio 2021.

RICHARDS, Mark; FORD, Neal. Fundamentals of Software Architecture: an engineering approach. Sebastopol, Ca: O’Reilly Media, Inc, 2020.

VERNON, Vaughn. Reactive Messaging Patterns with the Actor Model: applications and integrations in scala and akka. Old Tappan, Nj: Addison-Wesley, 2015.

TAYLOR, Hugh; YOCHEM, Angela; PHILLIPS, Les; MARTINEZ, Frank. Event-Driven Architecture: how soa enables the real-time enterprise. Boston, Ma: Person Education, Inc, 2009.

Compartilhe este capítulo:

Compartilhe:

Comentários

Participe da construção deste capítulo deixando seu comentário:

Inscrever-se
Notify of
guest
0 Comentários
Feedbacks interativos
Ver todos os comentários

Fundador e CEO da EximiaCo, atua como tech trusted advisor ajudando diversas empresas a gerar mais resultados através da tecnologia.

Mentoria

para arquitetos de software

Imersão, em grupo, supervisionada por Elemar Júnior, onde serão discutidos tópicos avançados de arquitetura de software, extraídos de cenários reais, com ênfase em systems design.

Consultoria e Assessoria em

Arquitetura de Software

EximiaCo oferece a alocação de um Arquiteto de Software em sua empresa para orientar seu time no uso das melhores práticas de arquitetura para projetar a evolução consistente de suas aplicações.

ElemarJúnior

Fundador e CEO da EximiaCo, atua como tech trusted advisor ajudando diversas empresas a gerar mais resultados através da tecnologia.

+55 51 99942-0609 |  contato@eximia.co

+55 51 99942-0609  contato@eximia.co

0
Quero saber a sua opinião, deixe seu comentáriox
()
x