Practical Implementation Traits of Service Choreography Based Microservices Integration

Implementation traits

Must-have traits

Decoupled in time

Guaranteed at-least-once-delivery

doInDatabaseTransaction { statement => 
statement.insert("INSERT into ....")
}
messageQueue.publish(new SomeEvent(...))
  1. Our application crashes immediately after the database transaction commits
  2. Our application is restarted immediately after the database transaction commits
  3. The message queue infrastructure is down for a few minutes, meaning we can’t send the event right now
doInDatabaseTransaction { statement => 
statement.insert("INSERT into ....")
messageQueue.publish(new SomeEvent(...))
}

Guaranteed message ordering

  1. Consumers that consume events from another service where consumption results in state transitions in the local context (e.g. projecting state locally from an external bounded context). Such consumers are conceptually stateful in that they care about tracking state across a series of multiple related events over time. In such cases, it’s usually necessary to process the events in the order they were originally produced for the local state to remain consistent with the source. It’s important to emphasise that it is only related events for which the order is necessary (e.g. events emanating from a specific aggregate instance).
  2. Consumers that are conceptually stateless in that they can treat every event they encounter as if it’s completely unrelated to any other event they’ve encountered in the past. Such consumers will typically trigger some kind of one off action, such as sending an email, sending a push notification, or triggering an external API call. An example of this might be where the reaction to an event requires charging a credit card via a third-party payment gateway.

Guaranteed at-least-once-processing

Idempotency

Nice-to-have traits

Consumer-side failure recovery

Decoupled in space

Parallelism

  1. We can improve the performance of our system through horizontal scaling, reducing the latency of eventual consistency.
  2. It’s easier to implement high availability of consumers rather than have single points of failure.
  3. We can avoid a consumer use case being completely blocked when encountering a repeated error in processing a single event. If we’re able to parallelise in some way, we can at least have that consumer use case continue processing some events (as long as they aren’t related to the stubborn one) rather than stopping processing altogether.

Wrapping up

--

--

--

Software engineering nut. Cyclist. Musician. Dog lover

Love podcasts or audiobooks? Learn on the go with our new app.

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
Andrew Easter

Andrew Easter

Software engineering nut. Cyclist. Musician. Dog lover

More from Medium

Nightmare Of a Distributed Monolithic Service As a Micro-service And How EDA Is The Best Approach

Circuit Breaker Pattern

Kafka Stream Business Logic Error Management

Saga Pattern