Fundamentos para arquiteturas baseadas em eventos (EDA)

Este capítulo possui uma versão mais recente:

Saber ouvir quase que é responder.
Pierre de Marivaux
Em um mundo cada vez mais conectado, onde produtos e sistemas têm cada vez mais sensores e poder de processamento, com modelos de negócios cada vez mais adaptados ao comportamento individual, ganha mais relevância a capacidade de reconhecer, assimilar e reagir adequadamente a ocorrência de eventos de diversas naturezas.
0
Você concorda?x

A tecnologia da informação está revolucionando os produtos. Antes compostos apenas por peças mecânicas e elétricas, os produtos se tornaram sistemas complexos que combinam hardware, sensores, armazenamento de dados, microprocessadores, software e conectividade de inúmeras maneiras. (Porter e Heppelmann)

Sob a perspectiva de sistemas computacionais um evento é um registro de algo que já aconteceu. Uma venda realizada, uma entrega efetuada, um pagamento efetivado, etc.

O aumento exponencial na quantidade de sensores e na conectividade tem feito “explodir” a quantidade e  granularidade de eventos identificados em sistemas computacionais que precisam ser assimilados e tratados. Carros modernos, por exemplo, além de todos os sensores que monitoram o comportamento de seus componentes, também registram o comportamento dos condutores, identificam outros carros e as condições da pista. Neles, o software embarcado toma decisões “inteligentes” em tempo real e, eventualmente, conecta-se a outros software na “nuvem”, para realização de ações consolidadas.

A demanda por formas eficientes de lidar com eventos também é marcante em operações com cartões de crédito e sistemas de votação, detectando fraudes; em sistemas de atendimento, qualificando o processo; em sistemas de vendas, habilitando up-selling cross-selling, apenas para citar mais alguns exemplos.

Sistemas modernos precisam reconhecer, assimilar e tratar eventos de maneira eficiente e eficaz e essa realidade fez surgir uma série de padrões e práticas arquiteturais. As arquiteturas que utilizam esses padrões e práticas são identificadas como event-driven architectures (EDAs). Elas se destacam por serem escaláveis, adaptáveis, responsivas e, geralmente, performáticas.

Diferença entre responsividade e performance

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

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.

A performance, 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.

Aplicações desenvolvidas com arquiteturas event-driven são compostas por componentes desacoplados para produção e tratamento de eventos, sempre de maneira assíncrona.

O desacoplamento dos componentes de arquiteturas event-driven facilita a definição de ownership, reduzindo riscos de difusão de responsabilidade, e se destacam pelo evolvability. Entretanto, é inegável a complexidade frente a práticas e padrões ortodoxos.
0
Concorda?x

Request-driven versus Event-driven

Os benefícios gerados por arquiteturas event-driven chamam a atenção mesmo em cenários onde lidar com eventos não é necessidade primária.

A maioria das aplicações LoB (Line-of-business), por exemplo, operam, tradicionalmente, em modelo request-driven. Ou seja, onde requisições são realizadas através de interfaces com usuários, suportadas por APIs, encaminhadas para handlers que, por sua vez, fazem consultas ou atualizações em bases de dados, normalmente de forma síncrona. Esse modelo é plenamente alinhado com a realidade do domínio e geralmente apropriado. Entretanto, apresenta desafios bem conhecidos para suportar o aumento da escala sem comprometer a eficiência.

Os benefícios de arquiteturas event-driven têm levado times de tecnologia e de negócios a adaptar seus modelos de operação para que sejam compatíveis com elas, visando melhorar a responsividade e resiliência de seus sistemas. O varejo online, por exemplo, vem “desacoplando” processos de venda, segregando eventos em “pedido realizado”, “pagamento efetivado”, “produto separado”, etc.

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.

É recomendável adotar arquiteturas event-driven, em substituição a request-driven, em cenários que demandem responsividade (varejo online, por exemplo), com alta complexidade e dinamismo nas interações. Entretanto, o aumento da complexidade não terá suficiente compensação para operações  mais simples e obrigatoriamente síncronas.

Dois tipos de EDA

De forma abstrata, podemos classificar arquiteturas event-driven em explícitas e implícitas.

Arquiteturas event-driven são explícitas quando os componentes que produzem eventos “conhecem” todos os componentes que os consomem. Nesses cenários, não é incomum que os componentes produtores acionem os consumidores diretamente, com código. Evidentemente, tal abordagem aumenta o acoplamento.

Em implementações EDA implícitas, não há qualquer “conexão fixa” entre produtores e consumidores de eventos. Assim, componentes consumidores precisam especificar, de alguma forma, os tipos de eventos em que estão interessados. Os produtores “publicam” os eventos aos consumidores através de algum mecanismo de “assinatura”, exercendo menor controle.

Dos dois tipos de EDA, a abordagem implícita é, evidentemente, mais poderosa, entretanto, incrementalmente mais complexa, pois, na medida em que os sistemas crescem, é mais desafiador monitorar e avaliar resultados.
0
Concorda?x

Duas topologias fundamentais

Há duas topologias fundamentais para implementaçã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.
0
Concorda?x

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.

Sagas

Em aplicações monolíticas, geralmente, temos um frontend conectando a um único serviço que, por sua vez, se conecta a apenas um banco de dados que serve como a “grande fonte da verdade”. Por mais complexas que sejam as operações, podemos usar, quase sempre o recurso de transações do banco de dados para garantir a preservação da consistência.

Imaginemos, entretanto, que não seja possível estabelecer uma “fonte única de verdade” ou, ainda, que a arquitetura da aplicação utilize mais que um banco de dados (conforme contexto delimitado).  Vamos considerar, por exemplo, o desenvolvimento de um serviço para apoiar a compra de produtos associados a viagens. Nesse contexto, desejaríamos ajudar nossos clientes na aquisição de bilhetes aéreos, no aluguel de carros e na reservas em hotéis.

No “mundo perfeito”, sempre que um cliente resolvesse viajar, poderíamos disparar a compra do bilhete aéreo, a reserva do hotel e do carro de forma paralela. Nosso trabalho seria apenas consolidar as confirmações em nosso “banco de dados” para poder fornecer ao cliente as informações de cada serviço contratado. Mas, o que aconteceria se:

  • Não houvesse hotel disponível para a cidade escolhida pelo nosso cliente?
  • Não houvesse a opção de carro que o cliente precisa?
  • Não houvesse opção viável de voo?

Na prática, provavelmente, a falha de qualquer uma dessas operações implicaria no cancelamento da viagem. Entretanto, perceba que, agora, não temos uma “transação” para dar rollback. A saída empírica é executar uma atividade compensatória (cancelamentos) anulando os efeitos produzidos nos serviços que foram completados com êxito.

No nosso exemplo, se tivessemos êxito em reservar o hotel e o carro, mas houvesse uma falha na compra do bilhete aérero, teríamos que “voltar” aos serviços fornecidos pelo hotel e pela locadora de automóveis para cancelar reservas. Para tornar o cenário ainda mais desafiador, precisaríamos entender as implicações destes cancelamentos para evitar multas, por exemplo. A solução é implementar o padrão Sagas.

Saga é uma transação de longa duração que pode ser escrita como uma sequência de transações independentes e intercambiáveis. Todas as transações na sequência devem ser completadas com êxito. Caso contrário, ações compensatórias são executadas para desfazer os efeitos da execução parcial.

Em nosso exemplo, a saga “Viagem do Elemar” seria composta por três transações independentes: 1) Compra da Passagem Aérea; 2) Reserva do Hotel e 3) Reserva do Carro.

Cada transação na Saga, tem uma ação compensatória capaz de “desfazer” uma transação parcial bem-sucedida.

A implementação mais simples para a saga seria através de um componente orquestrado: saga events coordinator

Trata-se de uma especialização da topologia proposta para trabalhar com um componente mediador. O início das atividades, para o SEC, pode ocorrer com um evento inicial e as respostas dos serviços especializados ocorre com eventos de conclusão.

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, matendo 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 (o NuBank, por exemplo, utiliza Datomic para registrar transações). 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.

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.

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” é 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