JavaScript IoC: Stop Running

You may have heard of Inversion of Control (IoC), an architectural pattern that enables excellent modularity and separation of concerns.

girl-running-from-zombie-signYou may have even run away, screaming.

I’m guessing it’s because IoC is popular in some enterprisey languages, such as Java and C#.  Or is it simply because IoC frameworks suffer from vendor lock-in, steep learning curves, and architectural limitations?  Or maybe you assumed that IoC only helps with server-side code?

In JavaScript, we can easily achieve Inversion of Control without frameworks or containers.  “Inverted” components and applications are extremely easy to write, test, understand, maintain, and extend.  And since they can be written in plain JavaScript, they’re incredibly flexible and compatible with most frameworks and libraries.  They’re also very modular and composable, which makes them incredibly scalable.

Since IoC is just as powerful in browsers as in node, you’ve run out of excuses.  Stop running.

Note: In this series of blog posts, we’ll use ES6 syntax while we explore several inverted code patterns. As we do, we’ll demonstrate how easy it is to create scalable front-end and back-end applications.

What is Inversion of Control?

Inversion of Control (IoC) isn’t a framework or a library.  It’s an architectural pattern.  In an inverted architecture, the direction of control has been flipped.  One or more top-level modules dictate the relationships between components, rather than letting components dictate their own relationships.

Once control has been inverted, it’s easy to use patterns that isolate asynchrony and side effects, resolve impedance mismatches, and naturally improve testability.

Inverted components and applications can be written in several styles, such as OO, FP, or FRP, but should follow some general patterns.  For instance, inverted components don’t request (e.g. `import` or `require()`) their dependencies.  Instead, they expect their dependencies will be provided to them.  This pattern is called Dependency Injection (DI), and we’ll investigate several types.

When IoC is  fully embraced, DI becomes less and less important as you discover and implement patterns that remove the need for dependencies.  It’s not uncommon to write inverted applications where most of the components have no dependencies at all.

Side bar: Don’t bother to read the wikipedia article about IoC. It almost completely neglects the architectural aspects of IoC and focuses entirely on vendor-specific frameworks and Dependency Injection. You won’t learn anything there about using IoC in your JavaScript projects.

Industrial Revolution = Inversion of Production

IoC reminds me of the Industrial Revolution.  In the pre-industrialized world, Craft production was the traditional way – actually, the only way – to build products.  Gradually, mass production enabled companies such as Ford, Singer, and International Harvester to build complex products that were vastly cheaper, more reliable, and producible at scale.

I could draw several analogies between mass production and IoC.  For instance, they both take advantage of componentization and standardization.  But I think the one that helps me best explain IoC is how the assembly line revolutionized production.

Pre-revolution

Before the assembly line, complex products were built by artisans (a.k.a. “craftspersons”) who learned their craft deeply.  Artisan gathered their own materials, built their own tools, and carefully constructed their final products one at a time.  Modern artisans now use standardized tools, but still often build custom tools in the forms of fixtures, harnesses, and templates.  When working in a team, they interact with each other directly, utilizing each other’s’ expertise as needed.

Similarly, in traditional, non-inverted software architectures, components gather their own resources (think: async/promises), use standardized or custom-built tools (think: `import _ from “lodash”` or `import { isValidPhone } from “./validation/phone”`), and interact with each other directly (think: `import template from “./template”`).

Perhaps, you’ve seen or written something vaguely similar to this sample module:


import _ from "lodash"
import { isValidPhone } from "../validation/phone"
import template from "./template"
import { DbConn } from "../Db"
import SmsService from "../SmsService"

const createMessage = _.template(template)

// Accepts a userId and returns a promise for a [userId, message]
// pair (suitable for logging) if the SMS message is successfully
// sent. The promise rejects with an Error if the user's phone
// number is invalid.
export const sendSms = userId => {
    const service = new SmsService()

    return DbConn.User.byId(userId)
        .then(throwIfInvalidPhone)
        .then(createMessage)
        .then(message => service.send(message, user.phone))
        .then(() => ([ userId, message ]))
}

const throwIfInvalidPhone = user => {
    if (!isValidPhone(user.phone)) {
        throw new Error(`Invalid phone for user ${ userId }.`);
    }

    return user
}

Post-revolution

Conversely, assembly line workers know little or nothing of how products are built.  They are highly specialized.  They perform one tiny part of the process and they do it well.  Each assembly line worker expects that all of her tools and materials will be pre-provided and immediately accessible.  It’s somebody else’s job to ensure she has an adequate supply of materials.  Similarly, she doesn’t know or care about the source of her tools, just that they look and behave as she expects.  She performs an extremely specific task on each product, so she is able to process several products a day.

Several of these unsophisticated assembly line workers, under the direction of a few engineers, could build the same products as an artisan, but orders of magnitude faster.  (Let’s defer arguments about the quality of the products for now.)

In IoC architectures, inverted components don’t gather their own resources (no async/promises); something else does that.  They might require a few standardized tools (think: `import _ from “lodash”`) or custom tools (think: interfaces, not `import`s).  They don’t interact with other components directly (no `import template from “./template”`).

Our inverted component might look like this:


import _ from "lodash"

// Accepts a template and a telephone validation function and
// returns a function that converts a User to an SMS message.
export const smsMessage = (template, isValidPhone) => {
    const createMessage = _.template(template)
    const validate = throwIfInvalidPhone(isValidPhone)

    return _.compose(validate, createMessage)
}

const throwIfInvalidPhone = isValidPhone => user => {
    if (!isValidPhone(user.phone)) {
        throw new Error(`Invalid phone for user ${ user.id }.`);
    }

    return user
}

Not Fair!

Wait-what?!?  This module doesn’t even do the same things that the previous module did!  How is this a fair comparison?  It doesn’t fetch the user, it doesn’t send the message, and it doesn’t transform the return value into a `[ userId, message ]` tuple.

Correct, it doesn’t.  Remember, IoC allows extreme modularity, so we prefer to write extremely modular (single-purposed) components.  Async behavior, such as IO, should be separated from application logic.  I’ve seen this philosophy called “pushing side effects to the edges”.  Interface adaptation, such as the output transformation into a tuple, should also be performed on behalf of components to ensure true separation of concerns.

In some respects, IoC is a means to do many of the things we already aspire to achieve in software: modularity, separation of concerns, reuse, removal of side effects, etc.  By inverting the architecture, the means to achieve these becomes more obvious and usually simpler.

You have to admit, the IoC module is much simpler.  I used a closure, a common JavaScript Functional Programming pattern, to capture the injected dependencies (`template, isValidPhone`), but you can probably imagine an OO pattern where we provide the dependencies as parameters to a constructor.  Don’t worry, we’ll show some OO examples later.

What’s next?

Ok, so where would these other behaviors be performed?  And how/where are the dependencies injected?  Stay tuned for future posts in this series to find out.

About the Author

Hi!  My name is John Hann.  I’ve been coding since the dark ages (1978).  I first learned about IoC from Brian Cavalier back in 2010 and have been discovering and refining JavaScript IoC patterns ever since.  He also converted me to Functional Programming, and now my JavaScript is so much simpler.   I’m known as @unscriptable just about everywhere.  I like anything related to science, and keep myself fit by running and mountain unicycling.

Resources

Dependency Injection – wikipedia article about DI

Functional Programming – wikipedia article about FP