Chapter 5. Message exchange patterns
Chapters 2 and 3 looked at patterns that can help you build services and their interfaces, like Edge Component and Service Instance. Chapter 4 covered ways of protecting and monitoring your services. Chapter 5 is the first of three that covers the different aspects of service interactions. After all, getting services to interact and enable business processes was the reason for using SOA to begin with.
As figure 5.1 illustrates, this chapter’s focus is on the interaction of services with their “customers”—the service consumers. A service consumer is any component or piece of code that interacts with a service. The patterns in this chapter deal with the basics—the message exchange patterns. Chapter 6 looks at service consumers and chapter 7 takes a look at patterns related to service composition and integration.
Figure 5.1. This chapter focuses on connecting services with user interfaces. It’s the first chapter in this book that takes a look at the service consumers.

The SOA definition in chapter 1 says that “each service exposes processes and behavior through contracts, which are composed of messages at discoverable addresses.” This makes service interaction very simple—you just send a message in and get a message back, right? Why do we need a whole chapter, or even two, on service interactions?
It’s true that messages are the basic building blocks of service interactions, but there are many ways to interact using these building blocks. People similarly use sentences as the building blocks for communications and interactions. When you call a sales rep, several interactions are possible:
- You can ask a specific question and get a reply (the Request/Reply pattern in section 5.1).
- You can leave a message with a question and a telephone number, and the sales rep will get back to you later (the Request/Reaction pattern in section 5.2).
- The sales rep can call you and let you know about new products (the Inversion of Communications pattern in section 5.3).
- You can have a long correspondence with the sales rep, sending emails back and forth until your issue is resolved (the Saga pattern in section 5.4).
What’s true in real life is also true for services.
Unlike most of the other patterns in this book, these core interaction patterns existed before SOA was even conceived—what this chapter will do is look at these interaction patterns from the perspective of SOA and SOA’s quality attributes. We’ll look at what it takes to make an interaction pattern like asynchronous communication work in a way that both complies with the SOA principles and retains the SOA benefits.
The following patterns are discussed in this chapter:
- Request/Reply—Enable a service consumer to interact with a service simply
- Request/Reaction—Temporally decouple the request from a service consumer and the reply from the service
- Inversion of Communications—Handle business events in an SOA
- Saga—Reach a distributed consensus between services without transactions
Let’s start with the most basic communications form—synchronous communications. The pattern is called Request/Reply.
Request/Reply is probably the oldest, and most described, pattern in computer science. Gregor Hohpe and Bobby Woolf offer a good description of Request/Reply in Enterprise Integration Patterns (Addison-Wesley Professional, 2003), where they describe the pattern as answering the following question: “When an application sends a message, how can it get a response from the receiver?”
The idea behind Request/Reply in SOA is not very different. The reason to discuss the pattern in this book, however, is that there are still a few issues worth emphasizing when using Request/Reply with SOA. I’ll talk about them as part of the solution discussion. First let’s look at the problem.
When you develop single-tier software that runs inside a single process in a single memory space, it’s relatively easy to get components to interact. When a requestor component wants something from another component (a replier), it can easily gain a reference to that replier, such as by instantiating it. The requestor can then invoke a method on the replier and get the reply as a reference or an address in memory where the reply resides.
In SOA, which is an architectural style for distributed systems, the other component is generally in another memory space and more likely than not on another machine—see figure 5.2.
Figure 5.2. Objects instantiated within a process versus services. With a local object, making a request from one component to another is simple—you get a reference to the other component and you make a request by calling it. In SOA, the requestor and consumer aren’t in a single address space. They’re also likely not to be on the same computer, and maybe not even on the same LAN. Making a request under these conditions is a lot more complicated.

Note
The first thing you want to do is find a way for services to interact with their consumers.
![]() |
How can you enable a service consumer to interact with a service simply? |
There are several alternatives for service interactions detailed in this chapter: asynchronous Request/Reply (Request/Reaction pattern), long-running interactions (Saga pattern), or events (Inversion of Communications pattern). They’re all more powerful than the Request/Reply pattern, but that extra power comes with a price—they’re all more complex than Request/Reply both to implement and to support.
There’s a place for sophistication, but sometimes you want to have a simple synchronous interaction between two remote components.
![]() |
Send a request message from the consumer, handle the request synchronously, and send a reply message from the service. Both the request and the reply belong to the receiving service. |
The Request/Reply pattern, illustrated in figure 5.3, is the most basic interaction pattern, so there aren’t any special components needed to make it happen. What you do need is a piece of logic that accepts a request, processes it synchronously, and returns a reply or a result. One thing to pay attention to is that both the request and reply messages belong to the contract of the service and not the service consumer (which is a common error for SOA novices).
Figure 5.3. The Request/Reply pattern defines request and reply messages in the service’s contract. When the service gets a request in the appropriate format, it processes it synchronously and returns the reply message to the service consumer.

The Request/Reply pattern only covers the message exchange; a complete interaction also needs communications infrastructure. You could utilize the Service Bus pattern (discussed in chapter 7), which handles exposing services on reachable (or even discoverable) endpoints as well as routing replies.
The roles of the request and reply are rather obvious. The request holds the intention or the task that the service is expected to perform, along with the input needed to perform it. The reply holds the results of performing the task.
The main problem with the Request/Reply interaction style is that it’s suspiciously reminiscent of remote procedure calls (RPCs)—that DCOM/CORBA, distributed- object stuff. You should be wary of modeling the services’ contracts on the RPC mindset—this can have several unfortunate effects on your SOA, ranging from poor performance to completely nullifying SOA. Instead of using the RPC approach, you should try to model your contracts on a document-centric approach. What in the world is a “document-centric approach”? Good question.
In a nutshell, document-centric means that the message contains enough information to represent a complete unit of work and doesn’t instruct the service on how to handle the message. In contrast, RPC calls tend to be command-oriented and geared toward sending just the parameters needed to perform the action; they have some stateful expectations from the service side as well as implicit expectations about what’s going to happen on the consumer side. Document-centric messages don’t make these assumptions; having a complete unit of work means that the service has enough information or context in the message to understand all the state it needs. This also means that document-centric messages are usually more coarse-grained than their RPC counterparts.
Note
There’s a third message type called event messages. We’ll discuss it in the Inversion of Communications pattern in section 5.3.
The following table outlines three ways document-centric messages can contain more context.
Two things to note are that the message can combine more than one type of context, and the same document can be exchanged back and forth between a service and its consumers, possibly adding detail as it moves, to allow complete business processes.
Table 5.1. Options for providing context within a document-centric message
Context |
Explanation |
---|---|
History | The message can contain the interactions up to this point, sort of like bread-crumbs in the Hansel and Gretel tale. In an ordering scenario, if the first step was to get customer data and the current step is to set the order (each step being performed by another service), the message would contain the customer information when it goes to the ordering service. |
Future | The message can include the options the consumer can take to complete the interaction. If you think about an ordering scenario, if the previous step was to reserve the order (see the Reservation pattern in chapter 6), the return message could include the information needed to confirm the reservation. |
Complete future | Another way to provide context is for the message format to contain the complete details needed for the interaction. For the ordering example, this would mean that the message would have a skeleton to support all the order and related details, and the parties involved would fill in the blanks as the interaction progresses. |
The technology mapping for the Request/Reply pattern is rather trivial. All the technologies I can think of enable you to implement the Request/Reply pattern in one form or another.
Most technologies make it extremely easy to expose objects remotely, which encourages RPC style-interactions; they make it hard to get to document-centric interaction. The code in listing 5.1 is an excerpt from the New Project wizard for the WCF service library in Microsoft’s Visual Studio 2010. The sample code shows a developer how to take a simple class and expose its methods as web services.

On the surface, this code may seem like a good example for the Request/Reply pattern (except maybe for the naming). A service consumer can send the MyOperation1 message with a string in it and get the “Hello” concatenated to the string as a reply. But the MyOperation1 implementation is a classic RPC interaction.
The situation is a little better for the second method (MyOperation2). Here a simple document is passed to the method. But the sample code handles that document in an RPC way too, and doesn’t return a document as a reply.
This approach isn’t unique to.NET—as another example you can consider the REST style. Whereas the REST principles promote the document-centric approach, the basic HTTP verbs are PUT, GET, POST, and DELETE, which again make novices think about CRUD interfaces.
A document-oriented approach results in richer messages that contain some context if not the whole of it. Consider the XML excerpt in listing 5.2.
Listing 5.2. A sample document-centric reply
<feed xmlns='http://www.w3.org/2005/Atom' xmlns:gd='http://schemas.google.com/g/2005'> <id>http://www.google.com/calendar/feeds/johndoe@gmail.com/private-0c1e3facdd1a4252aad07effeb7d68cc9/full</id> <updated>2007-06-29T19:22:12.000Z</updated> <title type='text'>John Doe</title> <link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://www.google.com/calendar/feeds/johndoe@gmail.com/private-0c1e3facdd1a4252aad07effeb7d68cc9/full'></link> <link rel='self' type='application/atom+xml' href='http://www.google.com/calendar/feeds/johndoe@gmail.com/private-0c1e3facdd1a4252aad07effeb7d68cc9/full'></link> <author> <name>John doe</name> <email>johndoe@gmail.com</email> </author> <generator version='1.0' uri='http://www.google.com/calendar/'> CL2 </generator> <gd:where valueString='Neverneverland'></gd:where> <entry> <id>http://www.google.com/calendar/feeds/johndow@gmail.com/private-0c1e3facdd1a4252aad07effeb7d68cc9/full/aaBxcnNqbW9tcTJnaTT5cnMybmEwaW04bXMgbWFyY2guam9AZ21haWwuY29t</id> <published>2007-06-30T22:00:00.000Z</published> <updated>2007-06-28T015:33:31.000Z</updated> <category scheme='http://schemas.google.com/g/2005#kind' term='http://schemas.google.com/g/2007#event'></category> <title type='text'>Writing SOA Patterns</title> <content type='text'>shhh...</content> <link rel='alternate' type='text/html' href='http://www.google.com/calendar/event?eid= aaBxcnNqbW9tcTJnaTT5cnMybmEwaW04bXMgbWFyY2guam9AZ21haWwuY29t' title='alternate'></link> <link rel='self' type='application/atom+xml' href='http://www.google.com/calendar/feeds/johndoe@gmail.com/private-0c1e3facdd1a4252aad07effeb7d68cc9/full/aaBxcnNqbW9tcTJnaTT5cnMybmEwaW04bXMgbWFyY2guam9AZ21haWwuY29t'> </link> <author> <name>John Doe</name> <email>johndoe@gmail.com</email> </author> <gd:transparency value='http://schemas.google.com/g/2005#event.opaque'> </gd:transparency> <gd:eventStatus value='http://schemas.google.com/g/2005#event.confirmed'></ gd:eventStatus> <gd:comments> <gd:feedLink href='http://www.google.com/calendar/feeds/johndoe@gmail.com/private-0c1e3facdd1a4252aad07effeb7d68cc9/full/aaBxcnNqbW9tcTJnaTT5cnMybmEwaW04bXMgbWFyY2guam9AZ21haWwuY29t/comments/'> </gd:feedLink> </gd:comments> <gd:when startTime='2006-08-14T20:30:00.000Z' endTime='2012-03-28T22:30:00.000Z'></gd:when> <gd:where></gd:where> </entry> </feed>
This listing shows the result of requesting a full calendar from Google Calendar. In addition to the calendar details (title, update date, owner name, title, and so on) you get all the listings with their full details as well as a pointer to get each calendar entry directly. The result uses Google’s GData protocol, which in turn builds on the Atom Publishing Protocol (APP). Note that the contract for accepting this XML is also simpler than that in listing 5.1, because you just need to handle a single XML parameter. The consumers aren’t bound to specific operations that can change over time.
To sum up this section, the Request/Reply pattern is supported by all the technologies that allow remote communications. The choice between RPC and document-centric approach is a design decision that isn’t enforced by the technologies. That has to be done by the developers or architects of the solution.
The Request/Reply pattern is a simple pattern that connects a service consumer with the service that it wants to interact with. As a basic pattern, it doesn’t solve a lot of quality attribute concerns, except for providing the functionality needed (getting the consumer and the service to interact).
One quality attribute that can be important is simplicity. Because Request/Reply is a simple pattern, it’s easy to implement and support and thus helps reduce the complexity of the solution.
Table 5.2 lists sample scenarios in which you might consider using Request/Reply.
Table 5.2. Request/Reply pattern quality attributes and scenarios
Quality attribute |
Concrete attribute |
Sample scenario |
---|---|---|
Time to market | Development ease | During development, exposing a new capability (already developed) in a service should take less than half a day to implement and test. |
Testability | Coverage | During development, each capability of a service should have 100 percent test coverage. |
I mentioned earlier that Request/Reply is the basic synchronous communications pattern. The next interaction pattern takes a look at implementing asynchronous communications under the SOA constraints and principles.
Synchronous communication, as described in the Request/Reply pattern (in the previous section), is very important, but it isn’t enough. The synchronous nature of Request/Reply means that the service consumer needs to sit and wait for the service to finish processing the request before the consumer can continue with whatever it was doing. There are situations where the service consumer doesn’t want or can’t afford to wait but is still interested in getting a reply when it’s available.
Clear as mud? Let’s take a look at a concrete example so I can better explain.
In contemporary border-control systems, when travelers get to the immigration officer, the officer searches for the traveler’s details in the system (swipes the passport, types in the password number, and so on) and then looks at the passport and tries to match the face to the passport holder. In the last few years, countries around the world have begun the move to e-passport systems. E-passports contain several elements, including an RFID chip, machine-readable code, and a couple of biometric samples (usually a photo of the face and fingerprints).
Figure 5.4 shows a high-level view of the flow for issuing an e-passport.
Figure 5.4. An enrollment process. When the UI asks the e-passport service to issue a passport, the service has to interact with several other services to fulfill the request.

As you can see, one of the steps in the flow is to enroll the person in the biometric database (which is part of the Biometric service). While it isn’t apparent from just looking at the interaction, the enrollment task can take quite some time to complete because internally the Biometric service also checks for duplicates, which is essential in ensuring the integrity of the database and preventing mistakes as well as intentional impersonations. This step involves comparing each sample (each face, for example) against every other sample already in the database, which could contain hundreds of millions of records (the population of the country).
Making this type of request using the Request/Reply interaction pattern is problematic because the wait time between the request and the reply is too long. It may be even worse if you decide to do the duplicate checks in a nightly batch.
This situation isn’t unique to e-passport systems. Similar situations occur in other systems. When you buy shares in a trust fund, for example, the transaction doesn’t happen immediately, but you probably want to know when it’s been completed. Another example is requesting a travel-planning system to locate the best deal for your next vacation. Here’s the problem:
![]() |
How can you temporally decouple the request from a service consumer and the reply from the service? |
One option is to solve the temporal coupling on the client side. To do this, you spawn a new thread before you send a request to the service; you then let that thread wait for the reply while the rest of the UI stays responsive. .NET has a component called BackgroundWorker that performs this separation and allows the UI to dispatch long-running work without blocking the UI thread.
This solution has its drawbacks. For one, the “waiting” isn’t resilient—if the service consumer happens to crash, the reply would be lost when the consumer wakes up again. Plus, the thread takes up resources on the consumer—what would happen if the request takes hours or days? Additionally, it’s a matter of responsibility. The service is the one that has a task that’s time-consuming—it should be the service’s responsibility to solve the matter and not throw it at the consumers.
Another approach to solving the temporal decoupling is to circumvent it and break the interaction. When you order an item online, for example, you don’t sit and wait until the system ships the item to you. Instead, the system lets you know that the item was ordered. Registering the order takes much less time than fulfilling the order.
The downside here is that you don’t know if the item has shipped unless you check the order status from time to time. Again, as in the previous approach, it’s your responsibility as the service consumer to solve the shortcomings of the service.
There are interaction solutions that support complex interactions, like the Saga pattern (which we’ll discuss in section 5.4). Implementing the Saga pattern will solve this issue, but it’s like killing a fly with a cannon. It’s overkill when all you really need is a delayed replay.
When Saga is overkill, breaking the integration works, but it hurts the service consumers, and you want to avoid client-side integration because of its bad implications. What you really want to do is somehow implement asynchronous communications over SOA, and do that in the simplest manner possible. This is what you need to do:
![]() |
Introduce the Request/Reaction pattern and implement asynchronous communication between service consumers and the service. Implement the message exchange as two one-way messages—a request from the consumer and a reply from the service side. |
The idea behind the Request/Reaction pattern, illustrated in figure 5.5, is to have two distinct interactions between the service consumer and the service. The first interaction sends the request to the server, which may return an acknowledgment, a ticket, or an estimate for finishing a job to the consumer. Once the processing is complete, the service has to initiate an interaction with the service consumer and send it the reply or reaction.
Figure 5.5. The Request/Reaction pattern defines both request and reply messages in the service’s contract. When the service gets a request, it processes it and prepares a reaction. When the reaction is ready, the service sends the request back to the consumer.

Note
The service has to manage the knowledge about where to return the reply—we’ll discuss that later.
The Request/Reaction pattern is more aligned with the basic premise of messaging because it lifts the time coupling. In contrast, Request/Reply is more aligned with RPC.
Figure 5.6 shows the use of the Request/Reaction pattern with the biometric service. Now when the biometric service receives an enrollment message, it reacts with an “enrolling” message notifying the client that the request has been received. Once the service finishes the enrollment either successfully or with an error, it will prepare an enrollment reply with the enrollment records and send it to the client.
Figure 5.6. The passport-issuing process using the Request/Reaction pattern. Now the biometric service returns two messages. First it returns an acknowledgment that it is processing the message; then, when the process is finalized, it returns a status.

Note
In the scenario illustrated in figure 5.6, it makes sense to use the Saga pattern (discussed in section 5.4) to roll back the other services if the duplication check in the biometric service finds a duplicate identity.
The Request/Reaction pattern is used in the Decoupled Invocation pattern (discussed in chapter 2). The difference between the two patterns is that Request/Reaction decouples the response from the request; the Decoupled Invocation pattern also decouples the processing of the message.
The interaction semantics of the Request/Reaction pattern are limited. If the e-passport scenario included the possibility of canceling the enrollment (if, for example, the RFID provisioning failed), it would be problematic coordinating this with a bunch of requests and reactions. In these long-running interactions, you may want to consider more advanced patterns, such as the Saga pattern described in section 5.4.
The Request/Reaction pattern offers more flexibility than the Request/Reply pattern, but this flexibility comes with a price. The Request/Reaction pattern is more complicated than Request/Reply, and it requires more work on the service (or edge) side.
Let’s look at some of the implementation details that you’ll need to take care of.
The basic way of implementing the Request/Reaction interaction pattern is to use two one-way messages. If you’re using web services, it would mean two HTTP channels. If you’re using messages, you’d need a queue (endpoint) for each of the involved parties.
The first hurdle is the temporal decoupling. Because the request and the reaction (reply) are separated in time, other messages can get in between. This means that you need to provide a way for the service to know where to send the reaction. It also means that both the service and the service consumer need a way to correlate the request and reaction messages—see the “correlated messages” sidebar for more details.
Correlated messages
One challenge of asynchronous messaging comes from the fact that the reaction message and the request aren’t directly related. The reaction can arrive quite some time after the original request was sent. What you need in this case is a way to identify that the two messages are related.
The mechanism that solves this problem is known as a correlation identifier, and as the name implies, it involves adding a token to messages that the service consumers and services can use to identify related messages. This isn’t very far from the idea of session cookies in a web application. The correlation identifier can include a message ID, a token for the conversation, and so on.
Correlation is supported by a wide variety of the WS-* standards. For instance, WS-Addressing has a relationship message ID header that can be used for correlation. Another example is WS-BPEL, which has even better support for correlation by letting developers define multiple correlation sets and the content of those sets.
Both Java and .NET offer solutions to deal with one-way messages. The Apache Axis2 Java library even provides the infrastructure to implement the complete Request/Reaction pattern out of the box. The following listing shows the consumer-side code needed to send an asynchronous message.
Listing 5.3. Client code sending a message using the Request/Reaction pattern
boolean useTwoChannels = true; ... OMElement messageBody = helper.FormatmMessage(data,type); Call msgSender = new Call(); msgSender.setTo( new EndpointReference(AddressingConstants.WSA_TO, "HTTP://www.example.org/ServiceName)); msgSender.setTransportInfo(Constants.TRANSPORT_HTTP, Constants.TRANSPORT_HTTP, useTwoChannels); Callback callback = new Callback() { public void onComplete(AsyncResult result) { //code to handle the Reaction goes here } public void reportError(Exception e) { //code to handle errors.. } }; msgSender.engageModule(new Qname("addressing")); msgSender.invokeNonBlocking("MessageName", messageBody, callback);
From the architectural point of view, the reaction is a message that’s sent by the service. From the implementation point of view, though, it can also be implemented by pulling from the service consumer.
Implementing Request/Reaction on top of Request/Reply isn’t too complicated. Figure 5.7 illustrates the steps. When the service consumer sends a request, it will get as a reply the address of the reaction (the URI in this case). The consumer will also get a time token designating when the answer will be expected. Once that time has elapsed, the consumer will make a second request to the service, this time asking for the reply (for example, using the GET command).
Figure 5.7. Implementing Request/Reaction on top of Request/Reply. The request’s return message explains where to find the reaction the estimated time or arrival (ETA). Sometime after the ETA, when the Service Consumer isn’t busy, it can go to the Reaction address on the Service and obtain the reaction itself.

Note
You can use the Active Service pattern, discussed in chapter 2, in the consumer to keep track of time.
The time to go down this path (of using pull instead of push) is when you can’t create an active independent endpoint on the consumer side. Again, the preferred approach is to get the Request/Reaction pattern right. If you can’t do that, you can implement the pull approach and still conform to the general idea behind the pattern, which is to offer flexibility and temporal decoupling.
I’ve mentioned that temporal decoupling and the flexibility it brings are the main quality attributes that drive using the Request/Reaction pattern. The pattern can also help with the performance quality attribute. When sending a message to the service doesn’t block the consumer, it allows the consumer to allot CPU cycles to other problems (such as handling requests from other services). Compare that with the blocking Request/Reply pattern, which holds resources on the consumer side while it waits for the reply.
Table 5.3 presents a couple of sample scenarios where Request/Reaction is more applicable than other patterns.
Table 5.3. Request/Reaction pattern quality attributes and scenarios
Quality attribute |
Concrete attribute |
Sample scenario |
---|---|---|
Flexibility | Temporal coupling | Under normal conditions, the system should notify the ordering party about order shipment within two hours of shipping the package. |
Performance | Responsiveness | Under normal conditions, the UI won’t hang while long operations are performed (such as searches and course recalculations). |
The Request/Reply pattern demonstrates synchronous communications between service consumers and services. The Request/Reaction demonstrates asynchronous communication. What we need to do now is check whether we can communicate using an event-driven architecture without violating any SOA constraints and assumptions.
The Request/Reply and Request/Reaction patterns are geared toward interactions where the consumer wants to get information or an action from a service. In order to get the action or information, the service consumer is willing to pay the coupling price associated with knowing about the other service, its service capabilities, and the protocol (contract) it uses to expose these capabilities.
But what happens when the potential consumers don’t know that they need to go and ask a service for new information? Will the service let them know? Will the service be willing to pay the coupling price?
This situation may at first sound unlikely to happen, but let’s look at a few examples. You’ll see that it’s a common enough business situation, and may be the norm.
Suppose you wanted to create a service for an airline that will proactively take care of delayed flights. When a flight is expected to arrive late, you’d want to find new flights for passengers who won’t make their connections, free up their places in their current connecting flights, and adjust the rates for these flights.
To do that, you’d have to interact with several services—some of them would be part of your system (such as a service that tracks all the active flights), and some would be external to your system (such as services that provide weather reports and airport statuses). Figure 5.8 shows delay information that you can get from the FAA in the United States.
Figure 5.8. Arrival and departure delays information as provided by the FAA (http://www.fly.faa.gov/flyfaa/usmap.jsp). This can be a source of information for an airline traffic control system.

Figure 5.9 shows a Delays service and a few of the services it can consume to work its magic.
Figure 5.9. Some of the services that a Delays service would need to interact with. The Delays service drives some of the services directly (such as Reservations and Schedules) but it’s driven by data coming from the other services (Weather, Operational Picture, and Airports).

Note
If you do an internet search for business events, you’ll notice that airline examples are quite popular but there are many other more down-to-earth (pardon the pun), run-of-the-mill IT examples. Just think about someone wanting to know when stock prices reach a certain level, or someone who needs to know every time an order larger than a certain value is placed. Similarly, an inventory system will need to know to order new parts when the supply gets below a certain threshold, and dashboarding and business activity monitor (BAM) solutions need to know about problems they should be reporting on.
While SOA seems to be rooted in Request/Reply, you’ll also need to find a way to support business events within the SOA constraints and tenets. In other words,
![]() |
How can you handle business events in an SOA? |
One option is to stick with the base SOA approach and have the service that generates the event actively send a message to all interested services. Note that the source service has to know about all the interested services, which would include understanding their contracts, to support this scenario. This is problematic because it introduces needless coupling between the event source and other services. In the previous example, the Weather service would have to know about the Delays and Operational Picture services. Similarly, if the Airport service wants to know about the weather so that it can update the airport status, you’d have to change the Weather service to notify that service as well. You need to keep in mind that unlike a classical Request/Reply scenario, the source service here doesn’t care about the target services.
Another option is to allow the interested services to poll for updates. Every event basically has a time to live when it’s still available in the current state of the providing service. An interested service can poll the event-generating service and find out about the interesting events. The advantage of this approach over the previous option is that now the dependency direction is correct. The services that do the polling are the ones interested in the information. The problem with polling is that if the polling interval is too long, you’ll miss important events, and if it’s too short, you’ll cause unnecessary network loads. (You can overcome this problem—I’ll talk about this as a variation on the solution.)
You can alleviate the service’s coupling problem in the polling option by externalizing the relationship from the services. One way to do this is by using the Orchestration pattern (discussed in chapter 7), which involves an external workflow engine. The event source can then have a single dependency on an endpoint of the workflow engine. The workflow engine knows about all the interested parties and forwards the messages to them.
This is a step in the right direction because the services aren’t coupled and it’s easy to make changes to the workflow and add additional services. The downside is that it federates the logic between the services and the workflow.
We’ve considered three different solutions, and each has some advantages, but maybe we can do better? I think we can.
The solution to handling business events has been there in the background all the time. If you want to add events, why not adopt an architectural style that’s built around events and incorporate that into SOA? As it happens, we don’t have to reinvent the wheel—there is already such an architectural style, and it’s called event-driven architecture (EDA).
An event is any significant change that happens within the event generator or within a component that’s observed by the event generator. Event specifications in EDA are structured entities akin to SOA contracts and messages. An event specification consists of a header and a body, where the header contains the metadata and the body contains the actual information about the event. Unlike traditional messages, events don’t have a specific destination.
EDA is similar to publish/subscribe, but it also has several differences such as the historical perspective that’s gained by treating events as streams instead of isolated occurrences.
To accommodate an event-like message exchange pattern within SOA, you can do the following:
![]() |
Implement the Inversion of Communications pattern by supplementing SOA with EDA—you can allow services to publish streams of events that occur in them instead of calling other services explicitly. |
The Inversion of Communications pattern, illustrated in figure 5.10, basically reverses the direction of the information flow. Instead of the service consumers calling on the service to get information, the service reaches out to the consumers with updates. This change in roles requires two components within the service, or rather within the edge (because they aren’t really business-oriented).
Figure 5.10. In the Inversion of Communications pattern, the service’s edge accepts and filters incoming events in addition to “standard” requests. When the service has some reply or reaction to an event ready, the edge also packages and dispatches it as an event to service consumers.

The first component is for event propagation. Events should be packaged in the format agreed upon for the SOA initiative (or, if there is no common contract, according to the service’s contract) and distributed (the following technology mapping section discusses this).
The second component, the event handler, enables the service to act as a service consumer for events sent by other services. The first task for the event handler is to filter incoming events for relevancy. This is important, because many of the events received might not be relevant, especially if the infrastructure between services isn’t smart enough to route or manage subscriptions. The second role of the event handler is to route the relevant events to the components of the service that can react to the events—the components that in a Request/Reply model would get the new information as requests.
One thing you’ve probably noticed is that even though the Inversion of Communications pattern talks about events, it doesn’t include a subscription-management component on the edge. That’s because subscriptions management requires too much effort that isn’t really related to the services, like routing and persistent subscriptions. An alternative to subscriptions on the service is to move the responsibility to the consumer or infrastructure (or both). To do that, you can provide known names (URIs, queues, and the like) where events can be found, and then have any interested services listen to them.
Let’s look at the Delays service mentioned in the problem description. Figure 5.11 shows that now the Airports, Weather, and Operational Picture services push their changes to the Delays service instead of the other way around. This has a positive effect on network traffic, because the Delays service no longer has to worry about missing an important change in the three services it monitors. Also note that applying the Inversion of Communications pattern does not mean you have to move all your interactions to events. In this example, the Delays service still has Request/Reply interactions with the Schedules and Reservations services. If the Delays service identifies a delay, it can try to reserve places on later flights for people who would miss their connections.
Figure 5.11. The relations between the services shown in figure 5.9 when using the Inversion of Communications pattern. Now the Weather, Airports, and Operational Picture services push their changes to the Delays service.

One thing to note when you combine the Inversion of Communications pattern with the Request/Reaction or Request/Reply patterns is that in addition to replying to the service consumer (or as an alternative to doing this), the service should also raise an event informing listeners of the effects of handling the request, so that other subscribed services can handle the effects of the change.
Inversion of Communications is about implementing EDA on top of SOA. Up to now, we’ve looked at the simple side of that, which involves handling sporadic or isolated events. But a very strong concept that EDA defines is event streams. This means you don’t look at each event on its own, but rather at a chain of related events over time. Event streams can give you both trends and a historical perspective. Used well, this can give you real-time business intelligence and business-activity monitoring. The Aggregated Reporting pattern discussed in chapter 7 shows an application of this capability.
Another pattern you can combine with Inversion of Communications is the Parallel Pipelines pattern (discussed in chapter 3). This combination can be used to provide an SOA implementation of a staged event-driven architecture (SEDA). In a nutshell, SEDA can provide a way to increase the concurrency and throughput of a solution in a relatively simple way.
The downside of using Inversion of Communications is the added complexity of designing the whole system using events. The way to deal with this problem has already been mentioned—don’t use Inversion of Communications exclusively; rather, combine it with the other message-exchange patterns mentioned in this chapter.
One other thing to watch out for and avoid when using the Inversion of Communications pattern is a vicious event circle, where an event triggers a chain of events that gets back to the original event source and causes it to refire the same or a similar event. I haven’t yet seen it happen in real business scenarios, but the possibility exists. The way to handle this problem is logging and monitoring, such as by using the Service Watchdog pattern discussed in chapter 3.
Moving to Inversion of Communications also makes it more complicated to debug processes. When something goes amiss, you need to trace the problem back to the butterfly whose wings initiated the chain reaction that led to the problem. The way to counter this is via centralized logging throughout the development process (and possibly in production) that enable you to replay the system. This is more complicated than following a direct call stack.
Another challenge of moving to Inversion of Communications is adding it in the middle of an SOA initiative, when you already have services deployed that utilize simpler message-exchange patterns. I can’t provide general guidance on the interaction remodeling because it’s very situation-specific, but as with the SOA initiative itself, the secret here is to perform the transition gradually.
The other set of challenges related to the Inversion of Communications pattern has to do with the implementation details. After all, many SOA infrastructures (most obviously HTTP) don’t support events or multicasts. Let’s see if we can clear up these obstacles.
There are several technology mapping options for implementing the Inversion of Communications pattern.
The first option, which is also the most natural fit, is to use an ESB. Most ESB implementations can accommodate all of the common message-exchange patterns, including publish/subscribe. The next listing shows how you could configure a subscription on Apache ServiceMix (an open source ESB). To configure the subscription, add a subscriptions section (sm:subscription) in the configuration section of a component (sm:activationSpec).
Listing 5.4. Configuration excerpt including subscription for a “picture” component
<sm:activationSpecs> <sm:activationSpec componentName="sub" service="foo:Subscriber"> ... <sm:subscriptions> <sm:subscriptionSpec service="cop::picture"/> </sm:subscriptions> </sm:activationSpec> </sm:activationSpecs>
When you want to implement the Inversion of Communications pattern with an ESB, you delegate the responsibility of passing the events and of managing subscriptions to the infrastructure, and you can concentrate on planning the events and the other business activities.
You can get even looser coupling by using a messaging infrastructure (or ESB) that supports topics, even though this isn’t a common service infrastructure for SOA. Topics are more loosely coupled because the subscribers don’t know who the publisher is—they just know about the topic that they find interesting. The problem with that approach is that the subscribers don’t know who the publisher is, so the infrastructure needs to make sure only authenticated and authorized services can post events.
Now let’s consider the more problematic infrastructures, like HTTP (RESTful services) and plain TCP. There are two options here.
The first option is to write the necessary infrastructure as part of the edge component of each service. In other words, develop your own logic to persist subscriptions and actively send each generated event to all the interested subscribers. Although it’s technically feasible, I don’t recommend going down this path unless you’re a middleware vendor. It’s better to focus on your core business and business value for your solution and not try to develop a delicate piece of infrastructure you aren’t likely to get right on the first try.
The second option, which I find more interesting, has to do with a push (well, actually pull) application that you probably use daily—blogs and blog newsreaders. When I publish a new event (post) in my blog, it isn’t immediately sent to my blog subscribers. In fact, it’s never actively sent. Instead, the new event is added to an events stream (RSS or Atom feed) that contains the most recent events. The subscribers, who manage the subscription on their side without any regard to me (loose coupling), decide how often they need to poll my event steam so that they don’t miss important events. That decision is based on how many items I keep in my feed, the frequency of new events, and the latency they can afford in handling the events. Note that consumers who need low latency from event occurrence to notification will probably need the online event notification and won’t be able to use this method.
As you’ve seen in the Request/Reply pattern (section 5.1) the Atom Publishing Protocol is a popular choice for formalizing collection in RESTful web services, as are the JSON versions, like OData and GData.
An event’s time to live
Whether you use feeds or a queue-based approach for publishing events, you need to consider the event’s time to live (TTL). By TTL, I mean the time during which the event should be available to consumers before it becomes irrelevant.
When you use events in a programming language, the TTL is inherent (“You snooze, you lose”). If a consumer isn’t there when the event is raised, that’s the consumer’s problem. In SOA, it’s wiser to allow temporal decoupling between the time the event was raised and the time it’s consumed. This temporal decoupling allows increased autonomy and loose coupling for both the event generator and the event consumer. The flip side is that you now have to consider the TTL of events to prevent the processing of obsolete information, too much latency, and performance problems.
The TTL changes depending on the business meaning of the event, so there aren’t any firm rules. Two rules of thumb I can give are that the TTL for cyclic events, like stock price updates, is usually the cycle frequency, and the TTL for one-time events, like a new order, tends to be much longer.
One point mentioned briefly in the previous section was that the EDA part of the Inversion of Communications pattern allows you to treat events as a stream rather than as isolated instances. Event streams can enhance your solutions even more if you add additional architectural concept known as complex event processing (CEP). As its name implies, CEP involves taking a look at event streams and examining them for complex patterns. This is probably best explained through an example.
Listing 5.5 shows a sample query in an embeddable CEP engine I wrote a few years ago (it was based on C# LINQ). The query examines a stream of login events and raises an alert whenever there are three failed logins in a row from the same user.
Listing 5.5. A continuous query to raise an event on three consecutive failed logins
var loginRecords = engine.GetEventSource<Login>(); engine.AddQuery(() => from names in loginRecords.Stream group names by names.Name into logins from login in logins let next = logins.FirstOrDefault(t = > t.LoginTime > login.LoginTime) let nextNext = null == next ? null : logins.FirstOrDefault(t => t.LoginTime > next.LoginTime) where !login.Successful && (null != next && !next.Successful) && (null != nextNext && !nextNext.Successful) select login, HanleAlert);
There are many commercial CEP engines from companies like SAP, TIBCO, and IBM, as well as few open source options like Esper from EsperTech.
The Inversion of Communications pattern presents a good opportunity to introduce CEP to a project, but that isn’t the main reason to use the pattern. As usual, we’ll finish our discussion of the pattern by exploring some of the motivations for using it.
Inversion of Communications is a powerful pattern. Events-based interaction greatly helps increase the autonomy and composability of a system, and the reuse within a system. This is great news for SOA, so much that Gartner called EDA and SOA “Advanced SOA.” While it’s important to remember the challenges involved in the implementation of Inversion of Communications, like complicated debugging and the added work of designing events, it’s an important pattern to have in your toolkit because all of its benefits.
Table 5.4 identifies some scenarios that might make you think about using the Inversion of Communications pattern.
Table 5.4. Inversion of Communications pattern quality attributes and scenarios
Quality attribute |
Concrete attribute |
Sample scenario |
---|---|---|
Flexibility | Decoupling | Services should know as little as possible about each other. |
Reuse | Interfaces | All services should support some common service APIs in addition to any specific requests they may serve. |
Changeability | Add feature | Assuming the development for a new capability is done, you should be able to integrate it into the system in three weeks or less. |
The Inversion of Communications pattern wraps up the basic message-exchange patterns by showing how you can do eventing or publish/subscribe within SOA. The last message exchange pattern we’ll cover in this chapter is the Saga pattern, which enables you to get transaction-like behavior between services.
In chapter 2, we talked about the Transactional Service pattern as a way to make a service handle requests in a reliable manner. But using the Transactional Service pattern only solves one part of puzzle. Let’s take another look at the scenario that we looked at in chapter 2 and see what we still need to do.
Figure 5.12 shows an Ordering service that processes an order. The interesting issue here is in steps 2.3 and 2.4. Within the internal transaction of handling the request, the Ordering service has to interact with two other services: it requests a bill from an internal Billing service, and it orders something (parts or materials) from an external Supplier system.
Figure 5.12. Sample message flow in an e-commerce scenario (talking to an Ordering service). The front end sends an order to an ordering service, which then orders the parts from a Supplier service and asks a Billing service to bill the customer. Note that all the handling of the Place Order message (step 1.0) is done within a single local transaction (steps 2.0 to 2.5).

There are two major problems lurking here. Consider what will happen if instead of committing the internal transaction at step 2.5, the Ordering service decides to abort its (internal) transaction. Also, consider how the ordering service would go about getting some commitment from the other services so that it could continue its work based on that commitment. You may want to get a confirmation from the supplier that the ordered items have been secured before you confirm the order to the customer.
The obvious solution to the two problems mentioned in the previous section is to extend (or flow) the Ordering service’s internal transaction into the other services. This extended transaction is known as a distributed transaction.
Using distributed transactions, the ordering service would have to call both the Billing service and the Supplier system as part of a single transaction, and if all the services agree to commit, the whole transaction is committed and completed together. This sounds really, really great, and we even have the technology to do that—technology that predates SOA by many years.
But, and there’s always a but, what if the supplier can only complete its part of the transaction after a senior manager authorizes the deal? Can you hold all your internal locks while you wait for that manager to return from vacation in the Bahamas sometime next week? Probably not. And what if the supplier also happens to be a competitor. It might prolong the transactions to disrupt your business—you’re holding locks on internal resources while you wait for the supplier to complete the transaction.
This specific scenario might be too far-fetched, but the point is that you can’t make assumptions about how other services operate. This is especially true for services you don’t own. You can read about other reasons to avoid cross-service transactions in the Transactional Integration antipattern (in chapter 8).
Even if you think that cross-service transactions aren’t problematic as a concept, you’ll probably agree that long transactions aren’t very good. The more conversational the interaction between the services gets, the more you need to think about alternatives to atomic transactions. In figure 5.12 there are two messages going out from the Ordering service, which might be borderline in terms of the number of interactions. But business processes can sometimes involve much more elaborate conversations.
A lot of messages flowing back and forth between services isn’t recommended, because it increases latency and the chances of failure. Nevertheless, few and sparse interactions aren’t realistic either. Services rarely live in complete isolation; interoperability is one of the reasons for using SOA in the first place. This means you need a way to handle complex service interactions in a reliable way without bundling the whole thing in one lengthy atomic transaction.
To sum up the problems,
![]() |
How can you reach distributed consensus between services without transactions? |
I think by now it’s clear that using a single transaction isn’t an option. If all the services involved are under your control, you might want to break the long process into multiple steps and run each step in its own transaction. Smaller distributed transactions are definitely a step in the right direction, but you’re still bound by cross-service transactions, and because everything isn’t bounded by one single transaction, you have problems like canceling the effect of a first step if something fails in the third or fourth step.
Another option is to model the contract so that you’ll never need this kind of complex interaction. You can minimize interactions between services if you increase the granularity of the services. But there’s also a limit to how large you want your services to be—you don’t want to end up with a single monolithic service that does everything. And just like objects, services need to be cohesive and adhere to the Single Responsibility Principle. When you do that, you can contain some interactions within the service boundary, but you still need to handle cross-service interactions to implement business processes.
The option you’re left with is to break the service interaction—the business process—into a set of smaller steps, and model that into a long-running conversation between the services.
The Saga interaction pattern is about providing the semantics and components to support the long-running conversation mentioned at the end of the previous section.
![]() |
Implement the Saga pattern and break the service interaction (the business process) into multiple smaller business actions and counteractions. Coordinate the conversation and manage it based on messages and timeouts. |
Hector Garcia-Molina and Kenneth Salem defined the term “saga” in 1987 as a way to solve the problem of long-lived database transactions. Hector and Kenneth described a saga as a sequence of related small transactions.[1] In a saga, the coordinator (a database in their case) makes sure that all of the involved transactions are successfully completed. Otherwise, if the transactions fail, the coordinator runs compensating transactions to amend the partial execution.
1 Hector Garcia-Molina and Kenneth Salem, “Sagas,” in SIGMOD’87: Proceedings of the 1987 ACM SIGMOD International Conference on Management of Data (1987), 249–59.
What made sense for databases makes even more sense for service interactions in SOA. Figure 5.13 illustrates how you can apply the saga notion to SOA. You can break a long service interaction into individual actions or activities and compensations (in case there are faults or errors).
Figure 5.13. In the Saga pattern, a service consumer and one or more services hold a long-running conversation within a single context (a saga). Once the parties reach some consensus, the conversation is committed. If there are problems during the conversation, the interaction is aborted, and the involved parties perform corrective steps (compensations). (* The coordinator may be a component on its own, external to the consumer.)

The first component to notice in figure 5.13 is the initiator. The initiator triggers the Saga pattern by creating the context, which is the reason for the interaction. It then asks one or more other services (participators) to perform some business activities. The participators can register for coordination (depending on how formal the Saga implementation is). The participants and initiator exchange messages and requests until they reach some agreement or they’re ready to complete the interaction.
This is when the coordinator requests all the participants (including the initiator) to finalize the agreement (prepare to commit) and commit.
If there was a problem during either the interaction or the final phase, the activities that occurred have to be undone. In regular ACID transactions you can roll back, but in a saga you have to perform a counteraction, called compensation, which may not be the exact opposite of the activity that must be undone. If the result of the original activity caused the service to cross some threshold, it may not wish to undo the action it took. Or it may be impossible to undo the effect, such as if canceling the action requires something from the service that requested the action in the first place (maybe a cancellation fee) or if too much time has passed which makes it impossible to undo the effect. As another example, if the result of a saga was to launch a missile, the compensation would be to abort the mission and blow up the missile in midair—you can’t just pull the missile back into the pod.
The Saga pattern is sometimes also referred to by the name “Long-Running Transaction.” It’s true that you can conceptually think of a saga as a single logical unit of work and that it does make use of transaction semantics. But a saga doesn’t really adhere to the transaction tenets like atomicity or isolation, mostly because the interaction is distributed both in time and space. For instance, when you call a compensation, it might be too late to undo the original action, so that there might be consequences like cancellation fees or partial deliveries. The “Saga” term better reflects the fact that the interaction is lengthy and that the messages are related.
Let’s take a look at what the ordering scenario in figure 5.12 might look like when you utilize the Saga interaction pattern. Figure 5.14 demonstrates a scenario where the supplier is out of stock of the ordered items. In this case, both the ordering and billing need to be canceled. You also need to notify the front end that there was a problem and let the supplier know that you closed the interaction.
Figure 5.14. The e-commerce scenario from figure 5.12 remodeled using the Saga pattern. The interaction with the Billing service and the Supplier system is now coordinated in a saga. The Ordering service can handle problems in a more robust way by canceling the order and notifying the front end instead of hoping for the best.

In this Saga pattern version of the ordering scenario, all the services involved (Ordering, Billing, and the Supplier system) send notifications about their ability to complete the saga or not. For instance, the Supplier system emits a fault message to let the Ordering service know that it had a problem processing the “place order” request. When the coordinator component inside the Ordering service gets the fault message, it requests that the other parties (the Ordering service itself, and the Billing service) compensate, and once that’s done it notifies the Supplier that the interaction has completed handling the fault.
The front end is notified about the failure during the compensation of the Ordering service. It isn’t a task of the coordinator.
The interaction in figure 5.14 has the service consumer and services controlling the interaction internally. One good way to do this is to use the Workflodize pattern (discussed in chapter 2) so that each service has an internal workflow that follows the sequence and different paths of the interaction. Another pattern related to the Saga pattern is the Reservation pattern (see chapter 6).
Another approach you can take to implement the Saga pattern is to use an external coordinator for the conversation—see the Orchestration pattern in chapter 7 for more details. The semantic difference between an internally coordinated Saga implementation and an externally coordinated Saga implementation is that with external coordination the coordinator holds the “big picture” of what the saga is trying to achieve, whereas with internal coordination you can get coordination without any one service having the complete picture. Internal coordination is more flexible, but it’s harder to manage.
The main effort involved in implementing the Saga pattern is deciding on the business activities and compensations. You can use techniques such as business process modeling to determine what these activities might be (Business Process Modeling and Notation, BPMN, is discussed in the section on the Orchestration pattern in chapter 7).
Even though the main effort in implementing the Saga pattern is on the business side, modeling business processes and activities that will support long-running conversations, there are also a few technological aspects that have to do with the messages and protocols—let’s take a look at them.
At a minimum, the Saga pattern requires you to add compensation messages to any state-altering message that can participate in a saga. Again, it’s important to emphasize that the compensation may not be able to undo the original activity, but it does have to try to minimize the effects of the activity.
The internal processing of the compensation messages varies depending on what needs to be done to cancel the effect of the original message. It’s usually better to set statuses to canceled rather than to delete records, especially at the database level, because the original action might have triggered other business processes and actions that rely on those records. For instance, if as a result of a message you added an order, another service might have produced a bill. Chances are that the billing also occurred within the same saga, but you might not know or control that within the Ordering service. Making a change that leaves traces behind it (like setting a status to canceled) is better than deleting a record because it allows you to resolve problems manually if the need arises. Note that in some industries, like banking, you’re required by law to register cancellations as new changes rather than to delete or amend the original records. (See Pat Helland’s “Accountants Don’t Use Erasers” article in the further reading section for more about not deleting records.)
Another message type that’s important for the Saga pattern is the failure message. When you have a simple point-to-point interaction between services, the reply or reaction that a called service sends is enough to convey the notion of a problem. The calling service consumer, which understands the service’s contract, can understand that something is amiss and act accordingly. When you implement the Saga pattern, however, you may have more than two parties involved, and you also have a coordinator. The coordinator isn’t as business-aware as the service’s business logic, but it does define control messages in order to understand the status of the interactions.
As you probably know (or have noticed by now) web services are considered the primary technology for implementing SOA, and the Saga pattern isn’t any different. The WS-* stack of protocols has produced the WS-BusinessActivity protocol as part of WS-Coordination.
WS-BusinessActivity has two variants:
- Business Agreement with Coordinator Completion—The coordinator decides and notifies the participants when to complete their roles within the activity. This approach is a little more ordered.
- Business Agreement with Participant Completion—The participants decide when to complete their roles within the activity. This approach is a little more loosely coupled, with the cost being increased chances for compensation.
WS-BusinessActivity defines an orderly protocol and states for both the participating services and the coordinator. WS-BusinessActivity also defines two coordination types:
- AtomicOutcome—All the participants have to close (commit) or compensate.
- MixedOutcome—The coordinator treats each participant separately.
Figure 5.15 shows the state transitions for a participating service using WS-Business-Activity with participant completion.
Figure 5.15. State diagram from the point of view of a participating service using the completion-by-participants variant of the WS-BusinessActivity protocol. The state transitions can be either the result of decisions by the service (the dotted lines) or by messages from the coordinator (the solid lines).

Another important technology option for implementing the Saga pattern is to use BPEL (Business Process Execution Language) or its WS-* implementation known as WS-BPEL (or BPEL4WS in previous versions). Additionally, you can also use a non-BPEL-compliant orchestration engine. These technology mappings all fall under the external coordinator mentioned previously and are covered in more depth as part of the Orchestration pattern in chapter 7.
The main reason to employ the Saga pattern is to increase the integrity of the system. As I’ve mentioned in the previous sections, transactions are problematic when it comes to distributed environments in general, and they’re even more so when using SOA. Nevertheless, you’ll still want to be able to coordinate the behavior of services and have meaningful interactions. By coordinating the behavior and failure handling, you can introduce reliable, predictable, long-running conversations.
In a distributed environment, it’s relatively hard to know what the outcome of a complex interaction will be, and this is especially true if you use other patterns, like Inversion of Communications (discussed in section 5.3). The Saga pattern introduces some control into the interactions and verifies that the outcome of a complex interaction will be along known paths (either completed or compensated).
The outcome of increased predictability is also increased correctness. When you know how the system is going to behave, it’s easier to construct system tests to verify that the desired outcome indeed happens.
Table 5.5 presents sample scenarios for the preceding quality attributes.
Table 5.5. Saga pattern quality attributes and scenarios
Quality attribute |
Concrete attribute |
Sample scenario |
---|---|---|
Integrity | Correctness | Under all conditions, an order processed by the system will be billed. |
Integrity | Predictability | Under normal conditions, the chances of a customer getting billed for a canceled order shall be less than 5 percent. |
Reliability | Handling failure | When resuming from a communications disconnection, all the processes that were interrupted shall remain consistent. |
Writing compensation logic is relatively complicated. As the timeline advances, the number of changes in the service can get rather large, which makes it harder to achieve predictability when you try to undo an early change. One way to try to cope with that is to implement the Reservation pattern, which you’ll read about in the next chapter.
One distinct characteristic of all the patterns in this chapter is that none of them are new. All the interaction patterns predate SOA by many years. Nevertheless, I’ve spent more than 30 pages discussing them with you, instead of just pointing you to Hohpe and Woolf’s excellent Enterprise Integration Patterns book, which covers these patterns as well. The reason for this is that although these patterns seem relatively simple and well known, each has some aspects that makes them a little complicated when you try to implement them and adhere to SOA principles:
- Request/Reply—This pattern talks about synchronous communications, but in SOA it’s better to use document-based interactions. That’s in contrast to RPC-based interactions, which are the norm in traditional distributed architectures for synchronous communications.
- Request/Reaction—This pattern implements asynchronous communications. Again, it’s a simple pattern, but it can be tricky to implement when you use consumers that don’t support callbacks.
- Inversion of Communications—This pattern implements eventing, but with a few twists such as implementation on transports that don’t support eventing. Another interesting aspect is providing event streams.
- Saga—Sagas are a way for services to reach distributed consensus without relying on distributed transactions.
The next two chapters will look at less basic interaction patterns. Some of them are complementary to the patterns discussed here, such as the Reservation pattern in chapter 6, which complements the Saga pattern, or the Aggregated Reporting pattern in chapter 7 that uses the Inversion of Communications pattern. The other patterns we’ll look at have to do with aspects of interactions and aggregations beyond the underlying message exchange patterns, such as the Composite Front End pattern in chapter 6.
- Gregor Hohpe and Bobby Woolf, Enterprise Integration Patterns: Designing, Building, and Deploying Messaging Solutions (Addison-Wesley Professional, 2003). This book discusses fundamental integration patterns in a general context, and many of them are applicable to SOA as well.
- Google Data APIs, http://code.google.com/apis/gdata/overview.html. Google’s Google Data Protocol (GData) is an example of a document-centric protocol for interacting with services.
- Arnon Rotem-Gal-Oz, “Bridging the Gap Between BI & SOA,” www.infoq.com/articles/BI-and-SOA. This article shows an application of the Inversion of Communications pattern (as well as Aggregated Reporting).
- Matt Welsh, “SEDA: An Architecture for Highly Concurrent Server Applications,” www.eecs.harvard.edu/~mdw/proj/seda/. Combining the Inversion of Communications pattern with the Parallel Pipelines pattern gives an SOA implementation of SEDA.
Pat Helland, “Accountants Don’t Use Erasers,” PatHelland’s WebLog, http://blogs.msdn.com/b/pathelland/archive/2007/06/14/accountants-don-t-use-erasers.aspx.
Pat Helland explains the merits of retaining prior states.