I believe that the way the industry is often building asynchronous event-driven applications is misguided and can be greatly improved.
Want to stay ahead of the curve on event-driven applications and Infinitic? Subscribe to https://infinitic.substack.com and never miss a development in this rapidly evolving field.
Online businesses (online food ordering and delivery, ride-hailing, financial applications, e-commerces, etc.) often have business processes implemented with services (Orders, Notifications, Tracking, Payments, Users, etc.) communicating through an event broker (e.g. Kafka, NATS, Solace, RabbitMQ or Pulsar):
Using events has many advantages:
However, this event-driven pattern does not simplify your life.
Asynchronous communication is much more complex than it appears. You must not only operate an event broker, but also write a lot of boilerplate code, as you have to define your queues, define your messages (types and schemas), learn the event broker APIs and write specific consumers and producers, implement error handling, and be extra careful not to break anything during updates.
Vendors like Confluent try to convince you that a central event broker cleans up the mess created by direct inter-service HTTP communications. But in practice, each event-driven service reacts to other service events and produces its own events (this is why it’s called the choreography pattern). So instead of API-driven services calling other ones, we have event-driven services pulling messages from other ones:
Despite being ubiquitous, this pattern has important issues:
As we can see above, event-driven applications based on the choreography pattern are efficient when running, but complex to implement and very complex to maintain.
For these reasons, I think an “orchestration” model is preferable. In this pattern, a service called “orchestrator” or “workflow” is responsible for managing service executions based on the business process definition.
Historically, these orchestrators were rather cumbersome, low-performance software, like BPM engines. But today an orchestrator can be lightweight and event-based to keep all the benefits described above:
Conceptually, this pattern it simpler:
Workflow services are horizontally scalable. But for consistency we should no have race condition when updating a state — either by locking the corresponding row in the database — or by ensuring that a specific process instance will always be handled by the same workflow service instance. I prefer the latter, which is more scalable and allows also to cache the state in-memory.
The benefits of this pattern are:
In practice, nevertheless, this pattern is more complex to implement, and I believe this is why it’s more rarely implemented, despite its many benefits. That’s why we created Infinitic, a framework to make event-driven applications easy using the orchestrator pattern.
This is what an event-driven application using Infinitic looks like:
Where everything in green is provided by Infinitic:
The scheduler has disappeared from the diagram because Infinitic provides its own, simplifying the architecture even further.
As you do not need to know the internals of Infinitic to run it, we can simplify the above illustration to:
By providing all the boilerplate code, Infinitic makes it easier to build an event-based application using an orchestration pattern with the following benefits:
Note that you do not need to switch your entire infrastructure to Infinitic to start using it. You can easily start with an isolated process:
Infinitic is currently based on Apache Pulsar, which has the right features for an ideal implementation (message queues, key-shared subscriptions, delayed messages). Other platforms like Kafka or RabbitMQ could be added later as they add those features.
You do not need to know Pulsar at all to use Infinitic. You just need a running instance. If you are not familiar with it, you will find managed instances at companies such as StreamNative, Datastax, or CleverCloud.
While the choreography pattern for event-driven applications is widely used, the orchestrator pattern offers significant advantages by centralizing process flow management within dedicated workflow services. This approach promotes better decoupling of services, easier comprehension of business process implementations, and simpler handling of complex workflows, transactions, and process versioning. However, implementing the orchestrator pattern can be challenging due to increased complexity. Thankfully, frameworks like Infinitic greatly simplify adoption by providing the necessary boilerplate code allowing developers to focus on implementing business logic while enjoying the benefits of an orchestrator pattern with minimal overhead.