More on this book
Kindle Notes & Highlights
by
Hohpe Gregor
When designing an application, a developer must know where to put what types of data in order to share that data with other applications and likewise where to look for particular types of data coming from other applications.
These paths of communication cannot be dynamically created and discovered at runtime; they need to be agreed upon at design time so that the application knows where its data is coming from and where the data is going to.
Another exception is messaging system implementations that support hierarchical channels. A receiver can subscribe to a parent in the hierarchy, and then a sender can publish to a new child channel that the receiver knows nothing about. The subscriber will still receive the message.
channels store their messages in memory. However, Guaranteed Delivery (122) makes channels persistent so that their messages are stored on disk. This hurts performance but makes messaging more reliable, even when the messaging system isn’t.
What if an application cannot connect to a messaging system but still wants to participate in messaging? Normally it would be out of luck, but if the messaging system can connect to the application somehow—through its user interface, its business services API, its database, or a network connection such as TCP/IP or HTTP—then a Channel Adapter (127) on the messaging system can be used.
an application that is a client on both messaging systems can build a Messaging Bridge (133) between the two, effectively connecting them into one composite messaging system.
A new application simply needs to know which channels to use to request functionality and which others to listen on for the results. The messaging system itself essentially becomes a Message Bus (137), a backbone providing access to all of the enterprise’s various and ever-changing applications and functionality. You can achieve this integration nirvana more quickly and easily by specifically designing for it from the beginning.
When a Point-to-Point Channel has only one consumer, the fact that a message gets consumed only once is not surprising. When the channel has multiple consumers, then they become Competing Consumers (502), and the channel ensures that only one of the consumers receives each message. This design makes consuming and processing messages highly scalable because that work can be load-balanced across multiple consumers running in multiple applications on multiple computers.
In a stock trading system, the request to make a particular trade is a message that should be consumed and performed by exactly one receiver, so the message should be placed on a Point-to-Point Channel.
The event can be packaged as a Message (66) so that messaging will reliably communicate the event to the observers (subscribers). Then, the event channel is a Message Channel (60). But how will a messaging channel properly communicate the event to all of the subscribers? Each subscriber needs to be notified of a particular event once but should not be notified repeatedly of the same event. The event cannot be considered consumed until all of the subscribers have been notified, but once they have, the event can be considered consumed and should disappear from the channel. Yet, having the
  
  ...more
Only subscribers are allowed to use wildcards; publishers are always required to publish a message to a specific channel. The specific capabilities and syntax for wildcard subscribers vary between the different messaging vendors.
An application may need to transmit many different datatypes, too many to create a separate Datatype Channel for each. In this case, multiple datatypes can share a single channel by using a different Selective Consumer (515) for each type. This makes a single physical channel act like multiple logical Datatype Channels (a strategy called multiplexing). Whereas Datatype Channel explains why all messages on a channel must be of the same format, Canonical Data Model (355) explains how all messages on all channels in an enterprise should follow a unified data model.
If we would like to use Datatype Channels but an existing message publisher simply sends all messages to a single channel, we can use a Content-Based Router (230) to demultiplex the messages. The router divides the message stream across multiple Datatype Channels, each of which carries messages of only one type.
A Format Indicator (180) is used to distinguish different format versions of the same data, which in turn enables these different formats to be sent on the same Datatype Channel.
An Invalid Message Channel is like an error log for messaging. When something goes wrong in an application, it’s a good idea to log the error. When something goes wrong processing a message, it’s a good idea to put the message on the channel for invalid messages. If it won’t be obvious to anyone browsing the channel why this message is invalid, the application should also log an error with more details.
A message that is valid for one receiver on a channel should be valid for all other receivers on that channel. Likewise, if one receiver considers a message invalid, all other receivers should as well. It is the sender’s responsibility to make sure that a message it sends on a channel will be considered valid by the channel’s receivers. Otherwise, the receivers will ignore the sender’s messages by rerouting them to the Invalid Message Channel.
When a messaging system determines that it cannot or should not deliver a message, it may elect to move the message to a Dead Letter Channel.
A developer using a messaging system is stuck with whatever dead message handling the messaging system provides, but she can design her own invalid message handling, including handling for seemingly dead messages that the messaging system doesn’t handle.
Many enterprises use Messaging (53) to integrate multiple, disparate applications.
most business applications already persist data into a database, so little extra effort is required to store data destined for other systems in a database table. Or an application can expose internal functions in a generic API that can be used by any other integration strategy, including messaging.
The Messaging Bridge is a set of Channel Adapters (127), where the non-messaging client is actually another messaging system and where each pair of adapters connects a pair of corresponding channels. The bridge acts as a map from one set of channels to the other and transforms the message format of one system to the other. The connected channels may be used to transmit messages between traditional clients of the messaging system or strictly for messages intended for other messaging systems.
An enterprise contains several existing systems that must be able to share data and operate in a unified manner in response to a set of common business requests.
There are two common Request-Reply (154) scenarios worth noting; both involve a Command Message (145) request and a corresponding Document Message (147) reply. In the first scenario, Messaging RPC, the requestor not only wants to invoke a function on the replier, but also wants the return value from the function. This is how applications perform an RPC (Remote Procedure Call) using Messaging (53). In the other scenario, Messaging Query, the requestor performs a query; the replier executes the query and returns the results in the reply. This is how applications use messaging to perform a query
  
  ...more
Yet, the message contents may be time-sensitive, so if the message isn’t received by a certain deadline, it should be ignored and discarded. In this situation, the sender can use Message Expiration (176) to specify an expiration date. If the messaging system cannot deliver a message by its expiration, it should discard the message or move it to a Dead Letter Channel (119). Likewise, if a receiver gets a message after its expiration, it should discard the message.
A local invocation is more reliable than a remote invocation. If the caller could transmit the procedure’s invocation to the receiver as a Message (66), then the receiver could execute the invocation locally. So, the question is how to make a procedure’s invocation into a message.
Remote Procedure Invocation (50) can be used to send the data, but then the caller is also telling the receiver—via the procedure being invoked—what to do with the data. Likewise, a Command Message (145) would transfer the data but would be overly specific about what the receiver should do with the data. Also, Remote Procedure Invocation (50) assumes two-way communication, which is unnecessary if we only want to pass data from one application to another.
Yet, we do want to use Messaging (53) to transfer the data. Messaging (53) is more reliable than an RPC.
Sometimes, an event occurs in one object that another object needs to know about. The classic example is a Model-View-Controller architecture
where the model changes its state and must notify its views so that they can redraw themselves. Such change notification can also be useful in distributed systems. For example, in a B2B system, one business may need to notify others of price changes or a whole new product catalog.
A subject notifies an observer of an event by calling the observer’s Update() method. Update() can be implemented as an RPC, but it would have all of RPC’s shortcomings.
It would be better to send the event notification asynchronously, as a Message (66). This way, the subject can send the notification when it’s ready, and each observer can receive the notification if and when it’s ready.
When a subject has an event to announce, it creates an event object, wraps it in a message, and sends it on a channel as an Event Message. The observer receives the Event Message, gets the event, and processes it. Messaging does not change the event that’s being announced, but just makes sure that the notification gets to the observer.
Event Message is a key part of implementing the Observer pattern using messaging.
When two applications communicate via Messaging (53), the communication is one-way. The applications may want a two-way conversation.
Perhaps a sender and receiver could share a message simultaneously. Then, each application could add information to the message for the other to consume. But that is not how messaging works. A message is first sent and then received, so the sender and receiver cannot both access the message at the same time.
A channel transmits messages in one direction. What is needed is a two-way message on a two-way channel.
Two applications sending requests and replies to each other are not very helpful. What is interesting is what the two messages represent. 1. Messaging RPC—This is how to implement Remote Procedure Invocation (50) using messaging. The request is a Command Message (145) that describes the function the replier should invoke. The reply is a Document Message (147) that contains the function’s return value or exception. 2. Messaging Query—This is how to perform a remote query using messaging. The request is a Command Message (145) containing the query, and the reply is the results of the query,
  
  ...more
The request is like a method call. As such, the reply is one of three possibilities: 1. Void—Simply notifies the caller that the method has finished so that the caller can proceed. 2. Result value—A single object that is the method’s return value. 3. Exception—A single exception object indicating that the method aborted before completing successfully, and indicating why.
The downside with temporary queues is that when their Connection closes, the queue and any messages in it are deleted. Likewise, temporary queues cannot provide Guaranteed Delivery (122); if the messaging system crashes, then the connection is lost, so the queue and its messages are lost.
Messages are often thought of as completely independent, such that any sender can send a message on any channel whenever it likes. However, messages are often associated. With Request-Reply (154) pairs, two messages appear independent, but the reply message has a one-to-one correspondence with the request message that caused it. Thus, the replier that processes the request message cannot simply send the reply message on any channel it wants; it must send it on the channel on which the requestor expects the reply.
When one process invokes another via Remote Procedure Invocation (50), the call is synchronous, so there is no confusion about which call produced a given result. But Messaging (53) is asynchronous, so from the caller’s point of view, it makes the call, and then sometime later a result appears. The caller may not even remember making the request, or it may have made so many requests that it no longer knows which one this is the result for. Now, when the caller finally gets the result, it may not know what to do with it, which defeats the purpose of making the call in the first place.
a message could have some sort of key, a unique identifier like the key for a row in a relational database table. Such a unique identifier could be used to identify the message from other messages, clients that use the message, and so on.
Each reply message should contain a Correlation Identifier, a unique identifier that indicates which request message this reply is for.
There are six parts to Correlation Identifier. 1. Requestor—An application that performs a business task by sending a request and waiting for a reply. 2. Replier—Another application that receives the request, fulfills it, and then sends the reply. It gets the request ID from the request and stores it as the correlation ID in the reply. 3. Request—A Message (66) sent from the requestor to the replier, containing a request ID. 4. Reply—A Message (66) sent from the replier to the requestor, containing a correlation ID. 5. Request ID—A token in the request that uniquely identifies the request. 6.
  
  ...more
This is called a Correlation Identifier because of the way the caller uses the identifier to correlate (i.e., match; show the relationship) each reply to the request that caused it.
A correlation ID (and also the request ID) is usually put in the header of a message rather than in the body. The ID is not part of the command or data the requestor is trying to communicate to the replier. In fact, the replier does not really use the ID at all; it just saves the ID from the request and adds it to the reply for the requestor’s benefit. Since the message body is the content being transmitted between the two systems, and the ID is not part of that, the ID goes in the header.
The gist of the pattern is that the reply message contains a token (the correlation ID) that identifies the corresponding request (via its request ID).
What the requestor really wants is a reminder of what business task caused it to send the request in the first place so that the requestor can complete the business task using the data in the reply.
A compromise approach is for the requestor to keep a map of request IDs and business object IDs. This is especially useful when the requestor wants to keep the object IDs private or when the requestor has no control over the replier’s implementation and can only depend on the replier copying the request’s message ID into the reply’s correlation ID. In this case, when the requestor gets the reply, it looks up the correlation ID in the map to get the business object ID and then uses that to resume performing the business task using the reply data.
Chaining is only useful if an application wants to retrace the path of messages from the latest reply back to the original request.







