Tiny Pattern – Publish/Subscribe

0

The Publish/Subscribe pattern is a great way to decouple services.  If you are working on a painfully high-coupled system, I’ll bet you should be using this pattern much more that you do now.

The idea is simple: “Don’t tell me what to do.  I’ll watch what you’re doing and see if I care.”

Let’s see what it is, when to use it, and a tiny, but valuable, implementation in Java.

What it is

 

This is a really trivial pattern.  There’s one class that runs the show: EventManager and it only has two methods: Subscribe() and Publish().

Any number of ‘Publishers’ can create event objects and ‘publish’ them to the EventManager.  Any number of ‘Subscribers’ and get notified when a particular event happens.

Events are easily represented by different Event classes.  Each event type can have data attached to the event germane to what happened.

When to use it

Let’s start with an example.  Pretend you have a shopping cart service for your Tea Cup website.  The big boss wants an email every time some one orders the Dainty Hippo Tea Cup set.  Simple, you say.  All you need to do is:

  1. Add a new send email method to InternalEmailService
  2. Add a couple lines in ShoppingCart::addToCart() looks for Dainty Hippo Tea Cup set and calls the email service

But wait … that’s smelly.  You just introduced a dependency between the ShoppingCart service and the InternalEmailService.  (Not to mention adding a hard coded product ID into the code!)

But what alternative is there?  You have to send this email when that action happens.

The alternative is Publish/Subscribe and it’s a super easy pattern to apply.  Here’s the steps:

  1. Add a new send email method to InternalEmailService
  2. Add a ItemAddedToCartEvent class (if you don’t have one already)
  3. Have the InternalEmailService subscribe to ItemAddedToCartEvent

Now what do you have?  First, you have a very handy, general purpose event:  ItemAddedToCartEvent.  I’ll bet there’s all sorts of uses for such an event.  In fact, as you extend your Publish/Subscribe pattern you have several events.  The documentation benefit alone is worth the price of admission.  Anyone can take a look at the list of Events and see key things that are happening in the business logic.

Next, your ShoppingCart service does not need to care about any emails being sent to the big boss.  In fact, your ShoppingCart service doesn’t need links to all sorts of other services.  On top of that, you can trigger additional processing from events in the ShoppingCart service without polluting it at all.

Finally, you have isolated this weird email requirement to one spot in the system.  You can put it in and take it out with a little code in one source file.

Can we have a Tiny implementation?

Absolutely.  Implementations of this pattern range from trivial to massive asynchronous computing environments operating on a messaging bus.  In fact, many over-engineer this pattern ending up with something too burdensome to use on everyday problems.  There is nothing wrong with adding a trivial implementation to solve big business problems.

Let’s take a look at a ‘Tiny’ solution.

This is indeed a Tiny Pattern Implementation.  There’s just two methods: subscribe() and publish().

As always, a unit test provides the best documentation:

How to prevent abuse

Using it in the wrong way

Many times, when we have a clever new technique and we look for as many places to apply it as possible.  But this is one pattern that is easily abused.  You might feel like decoupling all your classes and use this rather than calling methods.  That’s a really bad idea.

The purpose of Publish/Subscribe is to keep things decoupled that can be decoupled – not to artificially decouple classes.  Another way to state this is:

If classA can complete it’s charter without classB, then classB should subscribe to events in classA. If classA needs information or processing from classB, then Publish/Subscribe is inappropriate.

Fortunately, following a couple of rules will prevent trouble:

1. Phrase all events with past-tense verbs.  Never name an event with an imperative.

In our example, the event is named, ItemAddedToCartEvent.  This name clearly shows that something happened in the system and others are welcome to know about it.  That is a great event name.

But what if the event is named ‘SendEmailToBigBoss?’  Oooops.  That’s not an event, that’s a command.  This worse than having ShoppingCartService call InternalMailService, because it’s just overhead.

2. Never introduce a sequence dependency between subscribers.

In our tiny implementation, the list of subscribers to an event is a Set, so iteration is random.  That was no accident; it’s to prevent abuse.  If you need your subscribers to execute in an order, then you have an implied dependency or coupling between services.  Things like that are best served by calling the methods directly.  In fact, if you have such a dependency, I’ll bet you already violated rule #1.

Down the road

Let’s say you adopted this pattern correctly and universally.  What’s it going to feel like?

Let’s face it, the more interdependencies between services in a business system, the hard it is to make changes or additions.  The human mind can only track so many interrelationships and dependencies.  Without this pattern your code going to go haywire.  Trust me, it doesn’t take long before your team’s velocity begins to suffer, frustration increases and job satisfaction decreases.

Why is that?  Many changes in business systems are approached as “I need service B to do X.  So update services A, C, D, and E to call ServiceB:X().”  Over time a mess is created.

If you approach it as “I need service B to do X.  What event should trigger this?”  Now services A, C, D, and E are completely untouched by this new requirement.  You might have to add a new Event, but that become less frequent as the system matures.  In the end, only service B worries about service B’s charter.

When faced with adding new features you no longer panic.  Your breathing is more controlled.  Your heart rate remains stable.  Your outlook on life is all unicorns and daisies.

Let’s say you adopted this pattern correctly and universally.  What’s next?

One of the first things you can do make subscribers asynchronous.  If your event publisher really doesn’t care if there are any or who the subscribers are, then it wouldn’t really care if/when they finish.  So, why not run then asynchronously.  Heck, this pattern is a fantastic first step to reorganizing your system to follow the Reactive Manifesto.

Take our shopping cart example.  Should ShoppingCart:addToCart() really have to wait for an email to get sent?  Of course not.

If you started with our Tiny Implementation, you could convert to asynchronous processing without changing the publisher or the subscriber.  Just add an appropriate Executor Service in the EventManager.

After that, you could distribute the processing across servers by introducing a Message Bus like RabbitMQ.  Sure, you might have to change the content of the Events a little, but the design of your total business system is unaffected.

But whatever you do, don’t over-engineer your Publish/Subscribe pattern.  Just use a Tiny Implementation.

Share.

About Author

Steve Sando

Steve works with successful software startups and tech companies throughout Silicon Valley. Most recently he has been developing content migration tools for large websites. He has a deep passion for all things software engineering, from design concepts, to team management, to final delivery.

Leave A Reply