In an event-driven architecture, when a service performs some piece of work that other services might be interested in, that service produces an event—a record of the completed action. Other services consume those events so that they can perform any of their tasks needed as a result of the event. Unlike with REST, services that create requests do not need to know the details of the services consuming the requests. Here’s a simple example: When an order is placed on an e-commerce site, a single “order placed” event is produced and then consumed by several microservices:
- the order service which could write an order record to the database
- the customer service which could create the customer record, and
- the payment service which could process the payment.
Events can be published in a variety of ways. For example, they can be published to a queue that guarantees delivery of the event to the appropriate consumers, or they can be published to a “pub/sub” model stream that publishes the event and allows access to all interested parties. In either case, the producer publishes the event, and the consumer receives that event, reacting accordingly. Note that in some cases, these two actors can also be called the publisher (the producer) and the subscriber (the consumer).
Why Use Event-Driven Architecture
An event-driven architecture offers several advantages over REST, which include:
- Asynchronous – event-based architectures are asynchronous without blocking. This allows resources to move freely to the next task once their unit of work is complete, without worrying about what happened before or will happen next. They also allow events to be queued or buffered which prevents consumers from putting back pressure on producers or blocking them.
- Loose Coupling – services don’t need (and shouldn’t have) knowledge of, or dependencies on other services. When using events, services operate independently, without knowledge of other services, including their implementation details and transport protocol. Services under an event model can be updated, tested, and deployed independently and more easily.
- Easy Scaling – Since the services are decoupled under an event-driven architecture, and as services typically perform only one task, tracking down bottlenecks to a specific service, and scaling that service (and only that service) becomes easy.
- Recovery support – An event-driven architecture with a queue can recover lost work by “replaying” events from the past. This can be valuable to prevent data loss when a consumer needs to recover.
Of course, event-driven architectures have drawbacks as well. They are easy to over-engineer by separating concerns that might be simpler when closely coupled; can require significant upfront investment; and often result in additional complexity in infrastructure, service contracts or schemas, polyglot build systems, and dependency graphs. Perhaps the most significant drawback and the challenge is data and transaction management. Because of their asynchronous nature, event-driven models must carefully handle inconsistent data between services, incompatible versions, watch for duplicate events, and typically do not support ACID transactions, instead of supporting eventual consistency which can be more difficult to track or debug. Even with these drawbacks, an event-driven architecture is usually the better choice for enterprise-level microservice systems. The pros—scalable, loosely coupled, dev-ops friendly design—outweigh the cons.
When to Use REST
There are, however, times when a REST/web interface may still be preferable:
- You need a time-bound request/reply interface
- Convenient support for transactions
- Your API is available to the public
- Your project is small (REST is much simpler to set up and deploy)
Your Most Important Design Choice – Messaging Framework
Once you’ve decided on an event-driven architecture, it is time to choose your event framework. The way your events are produced and consumed is a key factor in your system. Dozens of proven frameworks and choices exist and choosing the right one takes time and research. Your basic choice comes down to message processing or stream processing. Message Processing In traditional message processing, a component creates a message then sends it to a specific (and typically single) destination. The receiving component, which has been sitting idle and waiting, receives the message and acts accordingly. Typically, when the message arrives, the receiving component performs a single process. Then, the message is deleted.
Original article - Best Practices for Event-Driven Microservice Architecture