Message Systems in Programming: Part 1 of 7 – Introduction

Introduction

Messaging systems are used to communicate in larger code bases by helping decouple classes that need to know about changes or happenings in certain areas of the code. One of Object Oriented Programming‘s core concepts is encapsulation. How you decide to allow objects to talk to each other has pro’s and con’s for each method and it’s good to know your options as you can use many together in effective hybrid approaches.

This seven part series will cover the 5 common ones you’ll often encounter.

We’ll cover:

  • Callbacks
  • Events
  • Pub Sub
  • Promises
  • Streams

I go over the what and why of each one, common use cases it’s used for, and some of the gotchas. The following techniques are language and MVC framework agnostic. While most of this article references JavaScript, Dart, Go, Python, TypeScript, and Lua examples, much is applicable, and influenced by, other languages.

That said, Promises and Streams tend to be most heavily used on the server-side and tend to be more interesting to Node.js developers for orchestration reasons.

Contents

  1. Nomenclature Caveats
  2. Why a Messaging System?
  3. What is a Messaging System?
  4. Callbacks
    1. Callback Scope
    2. Callback Message Types
    3. Callback Pros
    4. Callback Cons
  5. Events
    1. Custom Events
    2. Wrappers
    3. Event Bubbling
    4. Stopping Event Bubbling
    5. Event Cancelling
    6. Event Pros
    7. Event Cons
  6. Publish Subscribe
    1. Pub Sub Examples
    2. Pub Sub Pros
    3. Pub Sub Cons
  7. Promises And Deferred
    1. Done Yet?
    2. Solving Lots of Code
    3. Scroll to Find Code
    4. Nested Event Handler Registration
    5. Handling Race Conditions
    6. Aborting Sequence
    7. No More Cleanup Code
    8. Shielded From Exceptions
    9. Next Tick
    10. Promise Pros
    11. Promise Cons
  8. Streams
    1. Strongly Typed Streams
    2. Streams Being ‘Done’ vs. Infinite
    3. Filtering
    4. Merging and Piping Streams
    5. Note On Observables
    6. Cold vs. Hot
    7. Stream Pros
    8. Stream Cons
  9. Conclusions
  10. Why Write This and Other Random Notes
  11. Special Thanks

Nomenclature Caveats

At the time of this writing, I’m mostly describing JavaScript in this article. As such, there is little difference between the words “Objects” and “Classes”. As there are 50 million ways to approach creating classes and OOP based structures in JavaScript, I’ll use the 2 terms interchangeably a lot.

While events are a euphemism for a message payload, I use messages, pub sub, and messaging system interchangeably throughout this article.

Pub Sub is a broad term and doesn’t really imply a specific architecture. Like OOP, there are a variety of implementations throughout the JavaScript community. Additionally, every form of messaging system described here are actually a form of publish subscribe. However, there is a common pattern amongst many JavaScript pub sub libraries where they have extremely simple implementations without much filtering, nor logic put inside of a broker. Most are also one to many by default. That, and Underscore’s basic one is what you’ll see implemented a lot in Backbone code bases, so I use that since it’s a great, simple example to learn from if you’ve never learned about the pro’s and con’s of global pub sub libraries.

Events themselves are often used in both Promises and Streams as the message. The reason for this is that it’s more flexible for method signatures to have a single object and then add/modify the data you need to those objects. That way, you can change the internals of the functions, but not have to change the function signatures. This makes refactoring easier to do. In typed languages, you can filter messages based on Event type. That said, most examples for smaller projects in both Promises and Streams utilize simple primitive values such Numbers and Strings. Considering languages like Java and Dart give you compile time and runtime strong-typing with the use of Generics, Event classes are quite powerful in those languages, and thus Streams tend to get more perks there.

Why a Messaging System?

As code bases grow, decoupling or “making the code not so intertwined” becomes challenging. Some engineering design patterns have been developed over the decades to help programmers fight back, and make Object Oriented Programmed code bases scalable.

Lots of classes. Lots of developers. Less spaghetti mess.

The 2 main ones are Model View Controller / Presenter architectures, and Publish Subscribe (also known as pub sub) messaging systems. It’s important to point out that while these 2 systems are almost always used with each other, they are not mutually inclusive. You don’t have to use MVC when using messaging systems, nor do you have to use messaging systems when using MVC. They just often end up being used together for larger applications to give the various actors a prescribed way of talking to each other. Games tend to forego MVC and use some form of Component Entity system, yet still often utilize a messaging system, albeit more targeted for performance reasons.

It’s more common to see messaging systems built natively into programming languages to provide an easy way for various objects to talk to each other without having references to each other. As you and your team add more code, it is easy to tap into existing or new messages without having to care about many other classes. Usually a single mechanism, or message bus/broker, is responsible for registering and sending messages. C# has System.Message, CoronaSDK has Runtime, Director’s Lingo has sendAllSprites, and Flash has stage.sharedEvents.

If you want various bits of code to talk to each other without multiple dependencies, you need a common messaging system everyone can use with an opt-in system for identifying themselves for those who need to know.

What is a Messaging System?

All messaging systems are supposed to allow one to one or one to many communication. One to one communication is quite easy in programming. You give one class or bit of code a reference to another, give them methods to talk to each other, and voila, they can have a private chat.

Without a messaging system, classes have to know about each other, whether through reference passing or dynamic string reference:

// JavaScript communication example
objectA = {
	sayHello: function()
	{
		return "Hello!";
	},

	callHeyThere: function(someObject)
	{
		var response = someObject.heyThere();
		console.log("objectA got a response:", response);
	}
};

objectB = {
	heyThere: function()
	{
		return "Hey There!";
	},

	callSayHello: function(someObject)
	{
		var response = someObject.sayHello();
		console.log("objectB got a response:", response);
	}
};

objectA.callHeyThere(objectB);
// objectA got a response: Hey There! 
objectB.callSayHello(objectA);
// objectB got a response: Hello!

… or strong-typing through class Composition:

class ClassB
{
	String heyThere() => "Hey there!";
}

class ClassA
{
	void callHeyThere()
	{
		ClassB b = new ClassB();
		String greeting = b.heyThere();
       		print("ClassA got a response: $greeting");
	}
}

void main()
{
	ClassA a = new ClassA();
	a.callHeyThere();
}

The more references you add, however, the less flexible the reference passing or Composition approaches become. It increasingly becomes more challenging to untangle if you wish to make modifications the more objects you add.

Messaging systems help prevent tight coupling from happening and allow you to grow your code base, yet retain flexibility for refactoring.

The components of a messaging system consist of:

  • a sender
  • a receiver
  • a way to register yourself to hear about messages
  • ways to remove yourself if you don’t want to hear about messages anymore
  • some form of payload that represents the actual message

All messaging systems implement these in various ways, and many native and library implementations vary on how they implement the above parts.

Read more in Part 2 – Callbacks.

3 Replies to “Message Systems in Programming: Part 1 of 7 – Introduction”

  1. I’d add thunks to the list. It’s like callbacks, except passing in the callback is latter, and can be done many times. Thunks are amazing: very promise like (which I’d grown to love), but by staying at a level of dealing with higher-order functions rather than objects I find there’s a lot less syntactic cruft, a lot less creating functions for a .then().

  2. Boy do you deserve so much respect for figuring out that major choices devs have in Javascript land. A bit epic but so understandable I kept on your tongue until the last sentence.

Comments are closed.