Home © Rinat Abdullin 🌟 AI Research · Newsletter · ML Labs · About

Essay on Messaging and Design Patterns

Use messaging to tackle complexity of reactive applications.

Development ecosystem is a constantly changing battlefield, affected by various forces: software vendors, technological improvements, buzzwords and new ideas. One of recent changes involves transition towards reactive applications.

Application requirements have changed dramatically in recent years. Only a few years ago a large application had tens of servers, seconds of response time, hours of offline maintenance and gigabytes of data. Today applications are deployed on everything from mobile devices to cloud-based clusters running thousands of multicore processors. Users expect millisecond response times and 100% uptime. Data needs are expanding into the petabytes.

Let's explore one of the ways to approach reactive designs. We'll talk about a specific flavour of in-memory messaging which is present in open-source software projects like:

These systems implement in-memory messaging to tackle complex reactive domains. Benefits are:

  • Break down some application functionality into separate components;
  • Improving testability of these components and the entire application;
  • Explicitly expressing and handling time-based concepts, which would be hard to deal with otherwise;
  • Delivering new features incrementally without disrupting existing codebase;
  • Simplifying concurrency;
  • Delivering systems that can degrade gracefully under load, instead of failing completely.

What is Messaging?

Messages are named data objects designed to capture some concepts and ideas. In code they could look as simple as that:

public class RegisterCustomer
{
  public string FirstName;
  public string LastName;
  public string Email;
}

Messaging itself is about design where we send messages between components to drive system in reactive way. This is much like using emails to drive business workflows in a company. Similar to emails, passing messages is non-blocking - we fire message without any expectations about when it will be handled and by whom exactly.

This definition seems to be both obvious and too vague. Understanding "What messaging is?" does not give a slightest clue on how to apply it and gain some benefits. Blindly implementing system where any component could send anything to everybody is likely to end up in a complicated mess. Actually, this happened many times before. Eventually developers started noticing common patterns that were present in successful projects.

These messaging patterns were small, simple and focused enough to be useful and reusable. They helped to structure complicated software and make it understandable for outside developers. Eventually they got catchy names, too.

Example of Some Messaging Patterns Working Together

Let's try to gain better understanding of messaging by taking a look at design patterns for messaging used in EventStore, GTD Task Manager and Lokad Data Platform:

2013 08 08 inmemory design

Within this specific flavour we can identify distinct building blocks with different roles and capabilities.

  • Queue
  • Main Controller
  • Publishing Bus
  • Subscribing Components

Queue in this diagram is an in-memory message queue aggregating all incoming messages. Messages might potentially come from different threads. Should system be under the load, queue is the place that will hold messages till they can be processed.

Main Controller - main message handling class that is responsible for taking messages from the queue one by one and reacting to them. It serves as main entry point for messages and system coordinator.

Main controller can sometimes be implemented as a finite state machine (FSM) which would handle messages differently in different states. For example, we might discard all external requests while system is in StartingUpState or ShuttingDownState starts up or shuts down, while passing them through to the dedicated handlers in WorkingState.

In this design, even though messages come from different threads, they will be processed by Main Controller on one thread. This is a perfect synchronisation point. Of course, if we find out that certain operations take too much time (e.g. CPU or IO) we could route related messages down to dedicated handlers which would have their own pool of threads.

Bus (publisher) - maintains a list of subscribers interested in different messages. This list is usually defined at application startup and stays immutable since then. When a new message is passed down from the controller to the bus, it will be immediately (and synchronously) handed over to each subscriber.

For example, if we are implementing event-driven reactive desktop application, various view controllers could be implemented as components that:

  • subscribe to interesting events on the bus;
  • update their corresponding views in response;
  • put UI messages back to the main queue when user clicks buttons, enters text or interacts in any other way.

This would allow developers to add more features to the system by implementing new controllers (along with the corresponding views) and plugging them in.

More Messaging Patterns

There, obviously could be other, more specialised messaging patterns like timeout managers, process managers, forwarders or reply envelopes. Each comes with a well-defined role and place in the overall design.

Enterprise Integration Patterns might be a good start for learning more about established terminology and time-proven techniques.

Published: August 08, 2013.

🤗 Check out my newsletter! It is about building products with ChatGPT and LLMs: latest news, technical insights and my journey. Check out it out