Ben Nadel
On User Experience (UX) Design, JavaScript, ColdFusion, Node.js, Life, and Love.
I am the chief technical officer at InVision App, Inc - a prototyping and collaboration platform for designers, built by designers. I also rock out in JavaScript and ColdFusion 24x7.
Meanwhile on Twitter
Loading latest tweet...
Ben Nadel at the jQuery Conference 2010 (Boston, MA) with:

Managed Dependencies vs. Dependency Injection In RequireJS

By Ben Nadel on

In my journey towards understanding modular JavaScript application architecture, I've been using a lot of RequireJS. The RequireJS framework facilitates the organization and then the subsequent loading of individual JavaScript classes. When it comes to defining classes, RequireJS provides us with two opportunities for dependency loading: managed dependencies - those loaded by the RequireJS framework; and injected dependencies - those loaded by the application. When I first started using RequireJS, I found myself getting hung-up on when to use which approach. While some use-cases are quite clear, others are much less black-and-white.

To set the context of this discussion, take a look at the following JavaScript class defined using the RequireJS framework:

my-class.js - Our JavaScript Class Being Managed By RequireJS

  • // Define our class within the dependency management system.
  • define(
  • [
  • "dependency-1",
  • "dependency-2"
  • ],
  • function( Dependency1, Dependency2 ){
  •  
  •  
  • // Define the actual class constructor and methods.
  • function MyClass( argument1, argument2 ){
  •  
  • // ... more code ...
  •  
  • }
  •  
  •  
  • // -------------------------------------------------- //
  • // -------------------------------------------------- //
  •  
  •  
  • // Return the class constructor to define the class
  • // representation within the dependency management system.
  • return( MyClass );
  •  
  •  
  • }
  • );

I've omitted most of the code in order to draw attention to point of the conversation. This JavaScript class has four dependencies:

  • Dependency1
  • Dependency2
  • Argument1
  • Argument2

The first two dependencies are managed and loaded by the RequireJS framework itself. This means that when another part of the application requests the MyClass() module, RequireJS will handle the loading of Dependency1 and Dependency2. When the MyClass() JavaScript class is later instantiated, however, the last two dependencies - Argument1 and Argument2 - have to be managed and loaded by the application itself.

So, when you're building your modular JavaScript application, which type of dependency loading should you use?

After some trial and error, my rule of thumb has become this: If a given dependency is an "instance," it should always be loaded using dependency injection (ie. a constructor argument). The RequireJS framework exists outside of your business domain; as such, it shouldn't be charged with having to instantiate anything - instantiation requires business logic, which is the sole purview of the application.

As I started to form this opinion, the one use-case that really tripped me up was the use of the "text!" plugin to load remote HTML templates. Should RequireJS load the template? Or should my application load the template (using RequireJS) and then inject the template dependency during subsequent class instantiation?

Eventually, I embraced the idea that a remote HTML template was a "definition," not an "instance." As such, the RequireJS framework should be used to load HTML template dependencies as part of a class definition.

The other use-case that felt confusing, at first, was that of Singleton objects. This would include classes that could only be instantiated once as well as objects that did not require any explicit instantiation (ex. a hash of key-value pairs). Single-instantiation objects were relatively easy to classify - they still had to be explicitly instantiated by the application; as such, they should be injected into dependent classes during the invocation of the constructor.

But what about a static collection of key-value pairs? Such an object does not truly need to be instantiated. But, does this mean that it should be loaded by the RequireJS framework?

This one has me a bit stumped! My gut is telling me that the Application, itself, should be responsible for injecting static hashes during class instantiation. But, seeing as I have yet to define a module that consists of a static hash, I can't reason from any experience.

To use my rule-of-thumb as a guide - to fight my strictly emotional reaction - I would say that static hashes should be loaded directly by the RequireJS framework. They are not instantiated and therefore fall outside the domain of business logic.

The more I use RequireJS as a modular JavaScript dependency management system, the more I love it! And, now that I've thought more deeply and effectively about how objects should be loaded within the RequireJS framework, I think my passion for it will only continue to grow.



Looking For A New Job?

100% of job board revenue is donated to Kiva. Loans that change livesFind out more »

Reader Comments

You have a good approach here: anything that is "static" or a singleton is a great candidate as a module. Instances of something should be created in application space, but the constructor functions can be modules.

Reply to this Comment

@James,

Thanks! I'm really loving this RequireJS stuff you created! I think it's completely reinvigorated my interest in JavaScript - helping me take my vision, understanding, and deep-thinking to the next level! You rock :)

There's something that I love about dealing with Constructors (as module definitions). I think it was just how I was first taught JavaScript way back in the day. It feels like it offers a level of control that is not necessarily as elegant when done with object literals.

Of course, to each their own. I'm just trying to be consistent with how I think about this stuff.

Reply to this Comment

@Dusty,

Unless I'm reading it wrong, the Wikipedia page appears to support what I am saying:

"In its simplest implementation, code that creates a dependent object supplies dependencies to that object via constructor arguments or by setting properties on the object."

In what I am talking about, the Application is supplying the dependencies via the constructor.

If you're talking about what RequireJS is doing when it loads a module, I wouldn't know how to classify that. That's what I was referring to when I said "managed dependencies." I don't think that can be classified as dependency injection because it's not acting on an object - it's just using an asynchronous callback.

Reply to this Comment

@All,

I've slightly extended this conversation to the context of sub-classing in a RequireJS context:

http://www.bennadel.com/blog/2320-Extending-Classes-In-A-Modular-JavaScript-Application-Architecture-Using-RequireJS.htm

No real additional insights - just talking about how modules that provide both class definitions and "static functionality" can start to blur the lines between what *I* consider a definition vs. an instance.

Reply to this Comment

I suppose it almost fits the definition of Dependency Injection... but I still don't think I'd call it that. It's definitely not the intention of the RequireJS lib.

I think you're missing the 'injector' part, which should be able to instantiate objects based on some criteria.

RequireJS is a class definition importer, not an object instance injector. The items you call out as things that can't be injected are the only things that DI injects in other language domains (Java, C#, AS). These languages don't have an analog to RequireJS, because it's done by the compiler.

On a side note, I'm told that AngularJS has Dependency Injection... I'm just starting up a project with it, so I hope to find out soon.

Reply to this Comment

@Dusty,

Ah, I think I see our disconnect. I'm *not* calling RequireJS a dependency injector. Just the opposite. I'm saying that the Application (which instantiates classes and provides them to subsequent class _constructors_) is the dependency injector.

RequireJS, I'm saying is just a dependency manager - not dealing with instantiation at all. Just loading file definitions.

Reply to this Comment

@Dusty,

I've heard good things about AngularJS, but have not tried it yet. Sounds very interesting, though.

Reply to this Comment

Hi Ben! One rule of thumb I have is that if I would want to unit test any of the code, I would want to use dependency injection. Or pass in objects in the constructor. That means if you ever find yourself using "new" in your modular code for a non-native JavaScript type, you broke the rule.

Reply to this Comment

Post A Comment

You — Get Out Of My Dreams, Get Into My Comments
Live in the Now
Oops!
Comment Etiquette: Please do not post spam. Please keep the comments on-topic. Please do not post unrelated questions or large chunks of code. And, above all, please be nice to each other - we're trying to have a good conversation here.