Chapter 10. Messaging Patterns

When Smalltalk, the first real object oriented programming language, was first developed, the communication between classes was envisioned as being messages. Somehow we've moved away from this pure idea of messages. We spoke a bit about how functional programming avoids side effects, well, much the same is true of messaging-based systems.

Messaging also allows for impressive scalability as messages can be fanned out to dozens, or even hundreds, of computers. Within a single application, messaging promotes low-coupling and eases testing.

In this chapter we're going to look at a number of patterns related to messaging. By the end of the chapter you should be aware of how messages work. When I first learned about messaging I wanted to rewrite everything using it.

We will be covering the following topics:

  • What's a message anyway?
    • Commands
    • Events
  • Request-reply
  • Publish-subscribe
    • Fan out
  • Dead letter queues
  • Message replay
  • Pipes and filters

What's a message anyway?

In the simplest definition a message is a collection of related bits of data that have some meaning together. The message is named in a way that provides some additional meaning to it. For instance, both an AddUser and a RenameUser message might have the following fields:

  • User ID
  • Username

But the fact that the fields exist inside a named container gives them different meaning.

Messages are usually related to some action in the application or some action in the business. A message contains all the information needed for a receiver to act upon the action. In the case of the RenameUser message, the message contains enough information for any component that keeps track of a relationship between a user ID and a username to update its value for username.

Many messaging systems, especially those that communicate between application boundaries, also define an envelope. The envelope has metadata on it that could help with message auditing, routing, and security. The information on the envelope is not part of the business process but is part of the infrastructure. So having a security annotation on the envelope is fine, as security exists outside of the normal business workflow and is owned by a different part of the application. The contents on the envelope look like the one shown in the following diagram:

What's a message anyway?

Messages should be sealed so that no changes can be made to them once they have been created. This makes certain operations like auditing and replaying much easier.

Messaging can be used to communicate inside a single process or it can be used between applications. For the most part there is no difference to sending a message within an application and between applications. One difference is the treatment of synchronicity. Within a single process, messages can be handled in a synchronous fashion. This means that the main processing effectively waits for the handling of the message to complete before continuing.

In an asynchronous scenario, the handling of the message may occur at a later date. Sometimes the later date is far in the future. When calling out to an external server, asynchronous will certainly be the correct approach – this is due to the inherit latency associated with network I/O. Even within a single process, the single threaded nature of JavaScript encourages using asynchronous messaging. While using asynchronous messaging, some additional care and attention needs to be taken as some of the assumptions made for synchronous messaging cease to be safe. For instance, assuming the messages will be replied to in the same order in which they were sent is no longer safe.

There are two different flavors of messages: commands and events. Commands instruct things to happen while events notify about something which has happened.

Commands

A command is simply an instruction from one part of a system to another. It is a message so it is really just a simple data transfer object. If you think back to the command pattern introduced in Chapter 5, Behavioral Patterns, this is exactly what it uses.

As a matter of convention, commands are named using the imperative. The format is usually <verb><object>. Thus a command might be called InvadeCity. Typically, when naming a command, you want to avoid generic names and focus on exactly what is causing the command.

As an example, consider a command that changes the address of a user. You might be tempted to simply call the command ChangeAddress but doing so does not add any additional information. It would be better to dig deeper and see why the address is being changed. Did the person move or was the original address entered incorrectly? Intent is as important as the actual data changes. For instance, altering an address due to a mistake might trigger a different behavior from a person who has moved. Users that have moved could be sent a moving present, while those correcting their address would not.

Messages should have a component of business meaning to increase their utility. Defining messages and how they can be constructed within a complex business is a whole field of study on its own. Those interested might do well to investigate domain driven design (DDD).

Commands are an instruction targeted at one specific component giving it instructions to perform a task.

Within the context of a browser you might consider that a command would be the click that is fired on a button. The command is transformed into an event and that event is what is passed to your event listeners.

Only one end point should receive a specific command. This means that only one component is responsible for an action taking place. As soon as a command is acted upon by more than one end point any number of race conditions are introduced. What if one of the end points accepts the command and another rejects it as invalid? Even in cases where several near identical commands are issued they should not be aggregated. For instance, sending a command from a king to all his generals should send one command to each general.

Because there is only one end point for a command it is possible for that end point to validate and even cancel the command. The cancellation of the command should have no impact on the rest of the application.

When a command is acted upon, then one or more events may be published.

Events

An event is a special message that notifies that something has happened. There is no use in trying to change or cancel an event because it is simply a notification that something has happened. You cannot change the past unless you own a Delorian.

The naming convention for events is that they are written in the past tense. You might see a reversal of the ordering of the words in the command, so we could end up with CityInvaded once the InvadeCity command has succeeded.

Unlike commands, events may be received by any number of components. There are not real race conditions presented by this approach. As no message handler can change the message nor interfere with the delivery of other copies of the message, each handler is siloed away from all others.

You may be familiar with events from having done user interface work. When a user clicks a button then an event is "raised". In effect the event is broadcast to a series of listeners. You subscribe to a message by hooking into that event:

document.getElementById("button1").addEventListener("click", doSomething);

The events in browsers don't quite meet the definition of an event I gave in the preceding paragraph. This is because event handlers in the browser can cancel events and stop them from propagating to the next handler. That is to say, when there are a series of event handlers for the same message one of them can completely consume the message and not pass it on to subsequent handlers. There is certainly utility to an approach like this but it does introduce some confusion. Fortunately for UI messages, the number of handlers is typically quite small.

In some systems, events can be polymorphic in nature. That is to say that if I had an event called IsHiredSalary that is fired when somebody is hired in a salaried role, I could make it a descendant of the message IsHired. Doing so would allow for both handlers subscribed to IsHiredSalary and IsHired to be fired upon receipt of an IsHiredSalary event. JavaScript doesn't have polymorphism in the true sense, so such things aren't particularly useful. You can add a message field that takes the place of polymorphism but it looks somewhat messy:

var IsHiredSalary = { __name: "isHiredSalary",
  __alsoCall: ["isHired"],
  employeeId: 77,
  …
}

In this case I've used __ to denote fields that are part of the envelope. You could also construct the message with separate fields for message and envelope, it really doesn't matter all that much.

Let's take a look at a simple operation like creating a user so we can see how commands and events interact:

Events

Here a user enters data into a form and submits it. The web server takes in the input, validates it and, if it is correct, creates a command. The command is now sent to the command handler. The command handler performs some action, perhaps writes to a database, it then publishes an event that is consumed by a number of event listeners. These event listeners might send confirmation e-mails, notify system administrators, or any number of things.

All of this looks familiar because systems already contain commands and events. The difference is that we are now modeling the commands and events explicitly.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.129.42.134