Event-Based Architecture: Getting Decoupled

Every JavaScript developer knows that events are quite normal in JavaScript and that an event-based system can be quite fun and simple to use. Most even use event mechanisms almost every time they write JavaScript, but have you tried taking it to the extreme? What does it look like when you have an application architecture that rarely has two objects communicating directly with one another?

Seriously have you ever tried it? In my last post, I talked about dependency injection and the final portion of that article was about how I used it in my latest project. I explained that I’m using a central application object as an event hub, but rather than objects referencing it directly, I was injecting it via constructors. Well, here I’ll be talking a little bit more about how I’m using that event hub and probably very little about dependency injection.

Event Hub

For the new Minecraft server manager, I decided to try out Marionette, which extends Backbone to offer some new functionality and eliminate a lot of boilerplate code (especially from views). I’ll be doing an extensive series on it some other time, but for right now I’ll just point out a few minor components, particularly Application and EventAggregator.

Marionette introduces Application, which is an object that is designed to be the central hub of a Backbone application which implements a module system and many other nice pieces. I only use the EventAggregator that is built into it as the vent property. The EventAggregator is essentially just an advanced form of Event from Backbone.

I use Application.vent as the location where all of my modules communicate with one another. This isn’t the only means of communication between components, but, if it can help make something more reusable, then I try to use this instead of directly coupling objects. I’ll talk about this more later.

System-Wide Events

Since our event hub is attached to a piece of the application that is known throughout the system, we can broadcast messages to nearly any part of the system. I feel like the biggest challenge with this is choosing which events to emit and naming them.

This can be pretty simple. Just choose the events that you know something else in the system will need to respond to. But this means that when you expand the system, you might need to add more events in. Personally I try to think ahead to see if there are any events that something else might want to use in the future. This is this part that can be a bit difficult, but for anyone who wishes to extend the system, it’ll have been worth it. You can consider these events as “hooks” for others to plugin their own functionality into without having to modify much, if any, of the existing code base.

The naming is only difficult if you actually care, which you should. Consistency in names is extremely important for code readability, and system-wide event names are just as important, if not more important, than naming a property or method of an object.

Examples

Why not show you an example or two of what I’m talking about? In the Minecraft server manager, I use WebSockets to communicate between the server and clients about things that are happening with the Minecraft servers. I have a single module that handles these communications with the back end.

If a client asks for a Minecraft server to start running, then the back end will alert all of the clients that the server is running. The above object will receive that notification and use the event hub to let the rest of the world know. Another component listens for this event and adds the new running server to a collection that specifically holds the running servers. This component then triggers an event that lets the system know that a server is started. The tab manager hears about this event and creates a new tab for that server.

You just heard about several components that all communicated with one another but not a single one of them had any clue the other existed. Their communication was based entirely on events controlled by the event hub. You can completely remove pieces of this system and there will be no errors (but also very little functionality) because there are no dependencies, except on the event hub, which is injected, so you can use any event system that has an on, off, and trigger method. If the event system you want to install doesn’t have this interface, you can always create an adapter.

Conclusion

Though traditionally, you may not be exposed to events as a way to architect an entire system, they work quite well, especially in persistent client-side applications. On the server side with technologies such as Java or PHP where the “application” handles a single request and then dies, it doesn’t work too well, but if you’re using something like Node.js where the application is constantly running and waiting for responses, it can work out pretty well. If it makes sense for an application, try it and see if you like it. You might be surprised at how nicely it works. God bless and happy coding.

Author: Joe Zimmerman

Author: Joe Zimmerman Joe Zimmerman has been doing web development ever since he found an HTML book on his dad's shelf when he was 12. Since then, JavaScript has grown in popularity and he has become passionate about it. He also loves to teach others though his blog and other popular blogs. When he's not writing code, he's spending time with his wife and children and leading them in God's Word.