Reducing coupling with dynamic languages

I’m learning Node currently, after years of doing ASP.NET MVC, and a bit of Python on a couple of projects. There are lots of habits to shake off, and there are things that I miss (such as ASP’s outstanding model binding), but there is also a very liberating power in JavaScript, that lets you do things in a much more straightforward and even cleaner way than you would otherwise. There’s a lot less ceremony, and you can focus on what counts. One thing that keeps astonishing me is how I can make my Node modules work together without coupling them.

Node encourages you to put units of functionality into small modules that have a single responsibility, such as formatting localized strings, expressing assertions, or encrypting strings. This also applies to your own applications, that should be split into small units. For example, you would split a commerce app into shopping cart, order management, discounts, payment, etc.

To set-up the necessary communication between those modules in a platform such as ASP.NET MVC, you would usually have them take dependencies on each other. Next time you’d look at your code in nDepend, you’d see a lot more coupling than you would have thought you put in there.

One good question you could ask yourself at this point is: would I be able to swap, say, my order management for another module that I didn’t write? The answer usually is that you had been building interfaces for your services, so you’d just have to write a façade implementing those interfaces for the new service.

In Node, you could have require’d services from one another, but there is a better way: EventEmitter, which is basically a event bus. When your payment system has validated payment, you’d emit a “payment-validated” event, that has the details of the cart and the payment token as part of the event’s payload. The order module’s code would be listening to this event and would create the order based on it.

The important point is that in this case, the payment system doesn’t even have to know about the order module, and the order module just has to know about the event’s signature. You can add other modules that listen to the same events and that do other tasks. For example, you could send notification of payment to your accountant, you could log the event, or send confirmation emails, all without changing any code in the payment module.

At this point, you’re probably going to tell me that event buses are nothing specific to dynamic languages. That’s true, of course, but many details make it more convenient with dynamic languages. For instance, the event payload, if it is strongly-typed, will require that one end or the other will have to take a dependency on whatever code defines its class. If the payload is a dynamic object, no dependency is necessary, and you just have to agree to a loose contract on the shape of the object. Again, there is a lot less ceremony: to add a new event, it’s just one line of code, event payload included:

eventEmitter.emit('payment-validated', {cart: cart, token: token});

Interestingly, in Orchard, a platform that could be described as “semi-dynamic”, a lot is happening through an event bus. That event bus, interestingly, can use interfaces as the event name. You might think that re-introduces the coupling that we’re trying to get rid of, but it doesn’t really as events are matched by the interface name, not by type, and thus no hard dependency is necessary. The interface can be defined on both ends, and it will just work. Model binding makes the system really compelling, as subscribing to an event is done by implementing an interface. It looks almost exactly like the interface-based dependencies .NET developers are used to, but really is an event bus under the hoods. It’s quite smartly designed and implemented, but there’s still more code involved than what you get with Node.

If you haven’t been able to tell, I quite like the simplicity of it all. It’s easy to understand, and easy to implement. What do you think?

If you want a more detailed read about Node’s event-based model, read Rob’s excellent post here:
http://www.wekeroad.com/2012/04/05/cleaning-up-deep-callback-nesting-with-nodes-eventemitter/

1 Comment

  • I have had the same experience as well. Sadly, it is sometimes a struggle to create loosely coupled code that is NOT complex under tight deadlines. Or at least in my case, I have finally recognized that how what I "emotionally" feel is simple turns out to be a lot of ceremony.
    As you said, when you integrate you end creating proxies and interfaces, and maybe a God "parameter" object, or resort to JSON with proxies, interfaces, etc.

    All that said, there are moments when I'm working with Node where I find myself saying "I have to be missing something - this going too quickly!". I guess my lack of experience with the platform makes me suspicious that I'm leaving some glaring security hole open or creating synchronous code without knowing.

Comments have been disabled for this content.