Conversations between Loosely Coupled Systems
Work in progress. Last update: Jan 2017
Asynchronous Messaging is a great way for distributed systems to communicate while maintaining loose coupling. Sending a single message isn't always enough for a meaningful exchange, though: a request expects a response; a certificate exchange may be needed for initial authentication; an order message is supposed to lead to a payment message, and if something goes wrong, additional messages may be needed to handle or correct errors. In these situations, participants engage in Conversations, an exchange of related messages. Examples of conversations occur at many levels: traditional web services are orchestrated into larger services, REST services provide referrals or use HATEOAS to trigger subsequent interactions, low-level networking protocols discover services. Conversations also occur in real world: coworkers schedule a suitable meeting time by exchanging their availability, vendors issue offers to be followed by orders, shipments, and payments.
Designing robust conversations is no simple task, though: lost messages, participants' state getting out of sync, timeouts, and lots of other annoying stuff can happen in distributed systems that communicate over unreliable networks. Developers may have a 1000-page TCP/IP tome on the shelf, but are quietly happy that someone else already figured this out for them. Conversations quickly sneak up on developers, though, even when things start out pretty simple: a basic Asynchronous Request-Response interaction may foresee re-sending the request if no response is received within a specific time window. Doing so may result in a duplicate response being received if the original response was simply delayed, forcing the requestor to detect and ignore duplicate responses. The requestor likely also shouldn't send indefinite retry messages as this may overburden the system. Quickly the simple sending of a message turned up a number of considerations that govern how multiple messages can be exchanged: we need to decide the rules of the conversation.
EIP 2: Conversation Patterns
Conversation Patterns assist developers in designing robust interchanges between loosely coupled components just like Messaging Patterns did for stateless message exchanges. Just as before, each design pattern represents a "mind-sized" chunk of information that not only describes a concrete solution but also elaborates the trade-offs and forces that motivate it. Building on the idea of the pattern icons, informal sketches capture the essence of the conversation while real-life examples provide a precise protocol specification.
Conversation Patterns highlight that Enterprise Integration Patterns consist of more than just messaging patterns: Conversation Patterns are set to become Volume 2 of the "Enterprise Integration Patterns" book series. Each pattern language's base vocabulary allows tackling different kinds of design problems. Whereas messaging patterns follow the flow of a message through a series of components, conversation patterns take the complementary viewpoint by following the stateful exchange of messages between components over time. The introduction of conversation state in EIP Vol. 2 allows the discussion new topics like error handling or resource management, something that the messaging patterns in EIP Vol. 1 weren't able to do.
A Comparison: Subscriptions
A simple example highlights the complementary viewpoint that messaging and conversation patterns take.
A Publish-Subscribe Channel, shown on the left, is a basic messaging pattern that describes how messages are routed to a set of subscribers. The pattern considers a single message to be distributed across multiple subscribers, discussing how each subscriber receiving its own copy of the message makes adding new subscribers side-effect free. A related, but distinct conversation pattern,
The Pattern Language
A pattern language structures a collection of patterns and guides the developer towards a meaningful solution (see [POSA5]). The Conversation Patterns language distinguishes foundational patterns that deal with technical aspects, such as finding a conversation partner, from application-level patterns that serve higher-level goals, such as managing resources or negotiating agreements. Both groups of patterns rely on a common vocabulary to describe and compose conversations.
The foundational patterns roughly follow the life cycle of setting up a conversation:
- Discovery: Before services can interact, they have to identify one or more conversation partners.
- Starting a Conversation: Once the conversation partners are known, a conversation has to be established, including authentication, handshaking, and negotiating conversation parameters.
- Basic Conversations take place between two participants and a small number of message types. They establish a core vocabulary for the description of more complex conversations.
- Intermediaries are simple conversations that have messages flow through a third party.
Based on these foundational conversation patterns, we can tackle common distributed system problems at the application level, organized by type of consideration:
- Resource Management: Acquiring resources from a service represents a form of coupling: if the requestor disappears unexpectedly, the resource provider continues to hold unneeded resources. Resource management patterns deal with resource allocation and deallocation in loosely coupled systems.
- Ensuring Consistency: Things don't always go according to plan in distributed systems where messages can be duplicated and lost, or conversation partners may disappear altogether. These patterns deal with error conditions and help participants reach a consistent state.
- Reaching Agreement: Most business situations depend on multiple independent parties reaching an agreement through offers, counter-offers, negotiations and deals.
All patterns are listed in the Table of Contents.
Real Life Conversations
Conversations between components often resemble conversations in real life. That's the case because real life is inherently unreliable and asynchronous. Humans have therefore learned to interact by exchanging asynchronous messages, such as leaving a voice mail, mailing a letter, or sending an email. A series of such messages forms a conversation, which serves an overarching purpose, such as negotiating a deal or determining a place for dinner. Real-life conversations can span hours, days, or even months, and have to deal with many of the same issues as distributed systems do, including duplicate messages ("I really need that TPS report"), messages crossing in transit ("Ignore this notice if you've already sent your payment"), and coordinating multiple independent resources ("You got the money? You got the goods?"). Therefore, one can learn a good bit about distributed system design from real life.