A while back my colleague Jonathan Schabowsky recently wrote a paper called The Architect’s Guide to Event-Driven Microservices that’s turned out to be very popular, and for good reason. In it he explains the benefits of combining event-driven architecture and microservices, and candidly describes how decomposing applications admittedly makes life a little more…as he put it…”interesting.”
Many organizations migrate to microservices for one reason: Agility. The ability to very quickly create and modify components in a way that offers bottom line business value is mission critical in a world where your competitors are a click away and time to market is everything. So, you’re a developer and you have been tasked with delivering on this migration…what do you need to know to be successful?
The first thing to understand is the fallacies of distributed computing:
They’re all relevant, but I’ve bolded the ones that are especially important in the world of microservices, because the smaller you make each microservice, the larger your service count, the more the fallacies of distributed computing impact stability and user experience/system performance.
The challenge is that microservices require connectivity/data in order to perform their roles and provide business value, and data acquisition/communication has been largely ignored, so much so that the tooling has severely lagged behind. For example, API management/gateway products only support synchronous, request/reply exchange patterns, which when coupled with the fallacies noted above exacerbates the challenges of distributed computing. Many of these challenges can be minimized by properly implementing an Event-Driven Architecture.
Unlike service-oriented design, which are synchronous and blocking, event-driven design is non-blocking and uses lots of callbacks. Event-driven design is not a new concept, and you may even be familiar with it from using programming patterns such as the “Reactor Pattern” or the “Observer Pattern” which is one of the “Gang of Four” design patterns. The concepts used by these programming patterns can be applied to event-driven microservice architectures using architectural patterns, such as Publish-Subscribe, to achieve service decoupling, independent scalability, and one-to-many bi-directional communications.
The first step towards adopting the event-driven mindset is to change the way you think about designing and architecting solutions. Initially the tendency is to think about all interactions between services as a series in a sequence of request/ reply service calls. In fact, if you or your team uses the terminology of “invoking,” “requesting,” or “calling” then it is a sure sign you are still thinking in the request/reply paradigm.
Instead, try these: “What events should my service process?” and “What events will my service emit?”
Once you adopt event-driven thinking, you need to make the shift from orchestration to choreography. It is common for developers to think in terms of “service A will call service B which will call service C” and then implement that model through a chain of invocations (a->b> c) or by creating an orchestrator service such that x->a, then x->b, then x->c. Both approaches will cause chaos when the realities of distributed computing kick in, especially when you start to scale.
The alternative is to follow the philosophy of choreography. Services should react to changes in their environment and the benefits are immense:
This list of benefits doesn’t come for free; there is no silver bullet…
Consistency of state then becomes an area of focus, because a service, being temporarily down, means that the event state changes may not be processed immediately. Fundamentally, how do we deal with this negative side effect?
Eventual consistency is the idea that consistency will occur in the future, and it means accepting that things may be out of sync for some time. It’s a pattern and concept that lets developers avoid using nasty XA transactions. It’s the job of the eventing/messaging platform to ensure that these domain change events are never lost before being appropriately handled by a service and acknowledged.
Some think the only benefit of eventual consistency is performance, but the real advantage is the decoupling of the microservices since individual services are merely acting upon events that they are interested in.
As developers, we want the ability to easily write clean code which enables us to deliver functionality quickly. Thus, the development language and associated tooling becomes important. While event-driven architectures allow us to decouple our microservices and choose different technologies to implement each one it is recommended to choose a standard language/framework that best fits your team’s skill sets and deviate only when necessary.
For many organizations this skill set includes experience using the Spring Framework with Java (and other Spring supported, JVM-based languages). This experience has led to Spring Boot becoming the most popular framework for building microservices. Its adoption continues to grow and best of all, it has built in support for event-driven microservices through Spring Cloud Streams. Spring Cloud Streams is a framework for building highly scalable, event-driven microservices connected using shared event brokers. On top of providing an implementation framework, Spring Cloud Streams also defines vendor agnostic terminology for the event-driven community, including defining source and sink applications that send or receive events over channels.
While event-driven microservices may seem difficult initially, they are the future of most microservices and IT strategies. Become a ninja at their development by following these steps and make your life easier and simpler!