Event Driven Architecture Pattern | by Anuradha Wickramarachchi | Towards Data Science

This is the most common distributed asynchronous architecture used to develop highly scalable system. The architecture consists of single-purpose event processing components that listen on events and process them asynchronously.
There are two main topologies in the event-driven architecture.
Mediator TopologyBroker Topology
Mediator Topology
Broker Topology
Mediator topology has a single event queue and a mediator which directs each of the events to relevant event processors. Usually events are fed into the event processors passing through an event channel to filter or pre-process events.
The implementation of the event queue could be in the form of a simple message queue or even a message passing interface in a large distributed system which involves complex messaging protocols such as Rabbit MQ. The diagram below demonstrate the architectural implementation of the mediator topology.
This topology involves no event queue. Event processors are responsible for obtaining events, processing and publishing another event indicating the end. As the name of the topology implies event processors act as brokers to chain events. Once an event is processed by a processor another event is published so that another processor can proceed.
Consider the below diagram.
As the diagram demonstrates, some event processors just process and leave no trace and some tend to publish new events. This is quite similar to what we see in a NodeJS application. Steps of certain tasks are chained in the manner of callbacks, when one task ends, the callback is triggered. And all the tasks remain asynchronous in nature.
The following illustration demonstrates the NodeJS architecture as represented in the official documentation.
   ┌───────────────────────┐┌─>│        timers         ││  └──────────┬────────────┘│  ┌──────────┴────────────┐│  │     I/O callbacks     ││  └──────────┬────────────┘│  ┌──────────┴────────────┐│  │     idle, prepare     ││  └──────────┬────────────┘      ┌───────────────┐│  ┌──────────┴────────────┐      │   incoming:   ││  │         poll          │<─────┤  connections, ││  └──────────┬────────────┘      │   data, etc.  ││  ┌──────────┴────────────┐      └───────────────┘│  │        check          ││  └──────────┬────────────┘│  ┌──────────┴────────────┐└──┤    close callbacks    │   └───────────────────────┘ 
All the incoming connections are pushed into the poll and they will be treated asynchronously. This does not block the other calls being processed. Thus the architecture provides a greater scalability still being single threaded. We can run several Node processes and balance load on top of them and direct requests accordingly.
Further reading: https://nodejs.org/en/docs/guides/event-loop-timers-and-nexttick/
Netty (JAVA) — https://netty.io
Vert.X (JVM Languages) — http://vertx.io
Spring Reactor (JAVA) — https://spring.io/guides/gs/messaging-reactor/
ReactPHP (PHP) — http://reactphp.org
The pattern lacks atomicity of transactions since there is no guarantee of the execution sequence of the events. This is because event processors are implemented to be highly distributed, decoupled and asynchronous. The results are expected to be provided at a future time, mostly in the manner of a callback.
Testing of the systems with event driven architecture is not easy due to the asynchronous nature of the processing
Since the tasks are asynchronous and non blocking in nature the executions are parallel. Thus this outweigh the cost of queueing mechanisms providing great perofrmance.
Even though the systems are highly scalable the development effort is great. This is because unit testing and component testing is difficult due to asynchronous nature, which makes the system scalable. The systems can be made to run parallelly due to the decoupled nature of event processors, improving the scalability and parallelism.