September 20, 2021
Event-Driven Architecture and Microservices – Meant for Each Other
We’re living in a new age of software development, a cloud-native application age. Applications built for cloud delivery must be highly transportable, very loosely-coupled, highly resilient, and extremely responsive. That’s a lot to ask for. Comparing today’s development environment to what came before helps explain how all of this has been accomplished.
In the Beginning, There Was the Monolith
Anyone who has coded software for more than a few years remembers how applications used to be developed—and still are in some corners. Most of a given application was written as a single block of code. Every function, every Boolean option, every repetitive or iterative process, and every service the application called for were all contained within that code.
The flow of the code began at the beginning and proceeded on down, executing each command within each service in sequence until a decision-point was encountered. Perhaps a specific variable needed to be tested to determine where to proceed next. Or perhaps a user needed to enter a selection or response before processing could continue. Communication between each of the services, processes, functions, subroutines, and libraries was inherent in the processing of the code.
A neatly organized, complete package!
Not only was this an advantage, it was also a critical disadvantage. In a complete monolithic application like this, were anything to go wrong anywhere within the code, the entire application would completely come down. Program error…critical error…fatal error…none of them pleasant and often not easily resolved.
The Solution: Just Add Granularity with a Side of Independence
If a flaw occurring in any service could bring down the entire application, the logical solution would be to isolate each service by running it separately and independently. A failure in any service would only bring that process down, not the entire application, which would keep running until the failed service was re-instantiated and became available.
This thinking, which actually began decades ago, led to the development of microservices—small services that interact with other services to form and run an application. To increase the isolation of each service, a microservice runs in its own process within a container that includes the code for the service, its configuration, all dependencies, libraries, and other resources required to run the code. Containerized services can be individually tested and are deployed as a containerized image instance to the host OS.
There are several significant advantages to creating applications as an assembly of independent containerized microservices:
- Since they are each executed independently, each microservice can contain different code—with differing dependencies created on diverse platforms.
- Microservices can be deployed across varying environments with no modification.
- Running directly on the OS, containers have a much smaller footprint than VM images.
- Scaling out is easily achieved by creating new containers for various tasks.
- The instantiation of a new image (the process for creating containers) is not unlike instantiating a service or web app.
- Containers offer independence, isolation, portability, scalability and control.
By interconnecting containers in a service mesh, you can build cloud-native apps that run reliably across any environments they encounter.
The Nexus Where All Innovations Meet
There is a nexus where all the latest innovations in software development meet. Microservices, containers, DevOps, continuous improvement, continuous development and deployment (CI/CD), event-driven architecture (EDA), and more all coalesce around the achievement of increased agility.
Each microservice in a container is independent from all other microservices, thus increasing application resilience by enabling deployment in pieces. Should a change be required to any particular microservice, it does not require rebuilding or even stopping the entire application. This permits simplified maintenance as well.
Developers can also enjoy a division of labor, forming small teams to build and maintain specific services. They can even build those services in any language since each service runs separately from all others.
Bringing this all together, containerized microservices align with the core concepts of agility. They are very loosely-coupled, so a change to one microservice does not necessitate changes to another. And since microservices are easily reproduced, they are also highly scalable. Should a change be required, only the service requiring the change needs to be modified. And containers are literally the definition of granularity.
There is only one more piece required to bring them all together…communications.
Responsive, Interactive, Event-Driven Communications
In the monolithic architecture of the past, everything happened within the overarching application. With microservices, in contrast, each runs independently from each other. So how do they communicate with each other?
Certainly not in the classic way of waiting for action from a user. Classic code was command-driven; a command was issued by a user, and the system ran the application containing all the required services. To eliminate the need for human intervention, the software would need to be able to detect an event has happened and respond to that event appropriately.
This was the driving force behind the development of EDA. To operate, containerized microservices require the kind of responsive communication provided by EDA, which makes it possible for a significant change in the condition of a component of the system to be recognized by the system. In turn, this triggers further action or actions by the system.
Now, microservices can run and produce a resulting event that is then handled by an event producer. The producer next processes the event and sends it to the event router—which ultimately distributes the event among the one or many event consumers that are responsible for further action.
An Example of an Event-Driven Microservices Architecture
A simple event often requires complex responses. In this illustration, a premises sensor has detected the event of an expensive ring being stolen. Event processors such as this provide the required guidance to deliver deterrence by sounding an alarm while also notifying the ring’s owner and the police so they can respond.
None of these notifications need to be aware of the others, nor wait for them to occur before executing. The immediate action this sequence provides demonstrates the value of loose coupling.
Interconnecting containerized microservices creates cloud-native apps that easily transport to wherever they are needed on the network. To run reliably and consistently, they must have a communications platform that automates all potential responses.
Classic monolithic applications have difficulty achieving this because they can neither scale as well nor provide the required resilience. Cloud-native apps, however, take advantage of EDA to enable them to facilitate the agility that defines the goal of DevOps—to achieve continuous improvement in a dynamic environment in which continuous development and deployment are highly facilitated.
Event-Driven Architecture is just one of the methods our product development teams use to drive your success. Contact 3Pillar Global today to learn how we can do it for you.