Meteor: managing the global namespace

Justin Churchill
Meteor Hammer
Published in
10 min readNov 29, 2014

In Meteor there are multiple “global” namespaces to manage:

  • The global js namespace in node / browser
  • Meteor constructs: Session, Meteor.methods, Meteor.publish/subscribe, Mongo.Collection

This is one of those design patterns that you should only invest time in if you think your app will need to scale beyond a few pages with relatively simple logic. Code organization is extremely important for making it easy to develop as the app gets large. Modularization is basically the #1 design principle of all software engineering, and namespacing enables that.

The javascript global namespace

Managing a global javascript namespace is nothing new; js libraries have been doing it for a while now. JQuery uses $, underscore uses _, etc. In one of these client-only libraries, a pretty basic strategy will work: put all your code in a single function which provides a private scope for your library code. This function should return the global object, should be immediately invoked, and should have its result assigned to the single globally declared variable that will act as the namespace for your library.

// MyApp.js
MyApp = (function(){
var api = {};

// A private method
var printNumber = function(n) { console.log(n); };

// A public method we're putting on the API
api.printThirteen = function() { printNumber(13); };

return api;
}());
// In the console
MyApp.printThirteen(); // prints 13

Great. So what does this have to do with Meteor? One thing to understand is that these immediately-invoked function expressions is how Meteor lets you declare variables with var in a file and have them be local to the file. It wraps all files in an immediately-invoked function expression (IIFE)! That’s cool. Maybe you already knew that though. When you don’t use var, it becomes global. A blog post by Meteor themselves has a statement I found a little confusing:

Use global variables as much as you want: Meteor generates a wrapper around your code so that they are “global” only to the app or package that defined them.

Okay, great. So Meteor is doing the IIFE thing to our whole app or package. This certainly helps in package development. For those who don’t know, a Meteor package is basically a bunch of code that you can plug into your Meteor app that (a) can have semantically-versioned dependencies on other packages, and (b) declares the names of global variable(s) it will be adding to your namespace. So what they’re saying helps because it means I can have any number of global variables in my package, but those won’t necessarily end up being global variables in a consumer of my package.

The thing I found confusing was the encouragement to pollute the global namespace anyway. One reason we don’t do this is so that we can minimize the damage done by global namespace polluters that are not under our control. Here’s an example.

I’m building my app, using as many global variables as I want. One of them is, say, “Graphics”. So throughout my app’s code you’ll see stuff like:

var square = Graphics.shape(Graphics.shapes.square, { size: 15 });

Then one day, I discover this really cool Meteor package called Graphics that I want to use. It also uses the global variable “Graphics”. Now I have a conflict. Of course, there are a number of solutions to this. Maybe I could somehow figure out how to change the global variable that this package gets put in. I could do a find/replace on all my code to change my app’s existing Graphics namespace to something else that doesn’t conflict. In general though, this isn’t really a problem I want to have to ever deal with as I bring in new libraries or packages.

In Meteor, we should follow the same principle as in javascript libraries — a single global variable per app / package.

MyApp = {}; // The only time we ever declare a global variable
MyApp.Graphics = …

This is a proactive solution that will mean we can only possibly get conflicts if (a) we use a package that creates a global variable that happens to be the same name as our app, or (b) conflicts with a global variable created by another package. Both of these possibilities seem highly unlikely to happen.

You can extend these ideas a step further, beyond just the global scope, to arrive at a generally-accepted design principle: make use of namespaces. I don’t think I need to go into why namespaces are good. I think we can agree it’s better to have

MyApp.Collections.Tasks = …;
MyApp.Collections.Lists = …;
MyApp.Collections.Reminders = …;
MyApp.Collections.Alerts = …;

than to have

MyApp.TasksCollection = …;
MyApp.ListsCollection = …;
MyApp.RemindersCollection = …;
MyApp.AlertsCollection = …;

You end up with shorter, more reasonable names, the ability to easily answer the question “what are all my collections?”. Both of these make for much more readable, discoverable code.

How exactly can I achieve this in Meteor?

Because of the order in which files are loaded in Meteor, you have to do a little bit of planning to get your namespaces set up right. You have to guarantee that namespaces will be created before anything gets added to them.

A reasonable goal would be to have our namespaces reflect our project structure. In the example above, I might have a folder at the app root called “collections”, and that would contain four files, one for each collection declaration. Here’s how I wish I could write it:

// In collections/tasks.js:
MyApp.Collections.Tasks = new Mongo.Collection(“tasks”);
// In collections/lists.js:
MyApp.Collections.Lists = new Mongo.Collection(“lists”);
// In collections/reminders.js:
MyApp.Collections.Reminders = new Mongo.Collection(“reminders”);
// In collections/alerts.js:
MyApp.Collections.Alerts = new Mongo.Collection(“alerts”);

But MyApp.Collections was never defined. So let’s fix that. Here’s one way — add a file _namespace.js to the collections folder with the following content:

MyApp.Collections = {};

This works because Meteor promises to load files in the same folder in alphabetical order, and the underscore orders it before everything else alphabetically, causing _namespace to run before tasks, lists, and reminders. However, I don’t love relying on this alphabetical order because it feels extremely fragile and arbitrary. Maybe we can do something else. There is another Meteor rule that says that files in subdirectories are loaded first — that is, the deeper it is, the earlier it runs. To take advantage of this, we can make a folder called “namespace” and put a file in there with the namespace declarations. I’ll call it declarations.js.

// In collections/namespace/declarations.js
MyApp.Collections = {};

This at least feels like a more solid rule to rely on.

But what if I actually had a really large app, and I wanted to split up my collections into separate namespaces under “Collections”? This feels like a completely reasonable thing to want to do, especially outside of just Mongo Collections. For this example, let’s break up our app into a “lists” namespace and a “reminders” namespace. We won’t add more objects, just to keep it simple, although I think it might not be justified to have a whole namespace for a couple of objects. Now my folder structure might be:

// collections/lists
// -> lists.js
MyApp.Collections.Lists.Lists = new Mongo.Collection ...
// -> tasks.js
MyApp.Collections.Lists.Tasks = new Mongo.Collection ...
// collections/reminders
// -> reminders.js
MyApp.Collections.Reminders.Reminders = new Mongo.Collection ...
// -> alerts.js
MyApp.Collections.Reminders.Alerts = new Mongo.Collection ...

Now we have to ensure that each piece of the namespace is getting created top-down. I challenge you to try to use the same rules (alphabetical / deeper subdirectories) to come up with a way to do this elegantly. It can’t be done. At least, I haven’t figured it out.

Here is perhaps a superior solution: let’s write a set of basic namespace management functions.

A more solid approach to namespace management

I’d be totally happy if I could write this inside collections/reminders/alerts.js:

Namespacer.addTo(“MyApp.Collections.Reminders”, {
Alerts: new Mongo.Collection(“alerts”)
});

Namespacer would be some static object that is totally down with me trying to add to a namespace that hasn’t been defined yet. Like, I could run this code as the very first thing in my app, and then I’d have the alerts collection set up and accessible in MyApp.Collections.Reminders. Here’s what might be the basic implementation of addTo:

function addTo(namespace, members) {
var names = namespace.split(“.”),
// defined in Namespacer constructor
currentContext = this.globalScope;

// Add namespaces if they don’t already exist
_.each(names, function(name) {
if (name.length === 0) {
throw “Invalid namespace: “ + namespace;
}
if (!currentContext[name]) {
currentContext[name] = {};
}
currentContext = currentContext[name];
});

// Add members to namespace
_.each(members, function(value, key) {
currentContext[key] = value;
});
};

The other nice thing about this is that I would be able to inject extra logic when adding to my namespaces. How about a check to see that no other piece of code in my app has tried to add the same member to the same namespace twice? This is now doable.

I could do one other cool thing — maybe Namespacer is an object whose constructor takes in the single global variable name for my app. It could then check that all additions to namespaces are at least rooted in the global namespace, and throw an exception otherwise.

You may have noticed that this solution violates one of my most major suggestions to you: to limit the number of global variables you create to exactly one. I’ve created a “Namespacer” global variable!

This isn’t too hard to get around though. Let’s put this inside our app’s lib directory, which is promised to run first.

// In lib/namespacer.js
MyApp = {};
MyApp.Namespacer = function(…) { … };

If you don’t want to take up the namespace “Namespacer”, you can always throw this into some more obscurely-named MyApp._utils object or something.

This will work as long as code in other “lib” folders doesn’t need to do any namespacing operations, since other lib folders would have their code run first (the rule is that all paths containing “lib” go first, but among them, they are sorted by the normal rules). I’m definitely no expert in Meteor, but I can’t really imagine the value of having multiple lib folders in your app that interact with your namespaces — it seems like lib would contain only things related to environment setup, not anything feature-specific.

Usage of your defined namespaces

I wanted to talk about one other thing that tripped me up a little when thinking about this whole dependency thing, which is that if you structure your code correctly, then most of the time, code dependencies don’t matter. Meteor actually alludes to this a little on their docs but doesn’t explain it fully:

It is best to write your application in such a way that it is insensitive to the order in which files are loaded, for example by using Meteor.startup

It’s kind of funny that they say it’s “best” to do it this way. It seems to me that it’s completely necessary. Here’s an example demonstrating the problem you can run into:

// Some code that was run first because of Meteor’s load order
var alerts = MyApp.Collections.Reminders.Alerts;
Meteor.methods(“createAlert”, function(alert) {
alerts.insert(alert);
});
// Other code that was run later because of Meteor’s load order
Namespacer.addTo(“MyApp.Collections.Reminders”, {
Alerts: new Mongo.Collection(“alerts”)
});

In the first bit of code, alerts will be undefined (that’s assuming the namespaces exist, or else we’d hit an error before even getting there). This is a problem. We can restructure our code, though, to make this work:

// Some code that was run first because of Meteor’s load order
Meteor.methods(“createAlert”, function(alert) {
MyApp.Collections.Reminders.Alerts.insert(alert);
});
// Other code that was run later because of Meteor’s load order
Namespacer.addTo(“MyApp.Collections.Reminders”, {
Alerts: new Mongo.Collection(“alerts”)
});

By losing the var declaration and pushing the reference to the namespaced Alerts collection into the function, we’ve taken advantage of javascript’s deferred name resolution. By the time that function ever runs (presumably due to an actual call to that Meteor method), the Alerts collection will definitely have been defined because all of the app’s code will have loaded.

And in realizing this, you can now understand where Meteor’s suggestion to use Meteor.startup comes from. It puts code into a function that promises to run after all of the app’s code has been loaded. Any names you reference in the function you hand to Meteor.startup will only be evaluated when the function is actually executed thanks to deferred name resolution, and at that time they will definitely be available. This is good, because honestly, I really didn’t want to write that entire namespace every time I wanted to reference the Alerts collection:

Meteor.startup(function() {
var alerts = MyApp.Collections.Reminders.Alerts;
Meteor.methods(“createAlert”, function(alert) {
alerts.insert(alert);
});
});

Interestingly, this whole thing is starting to feel a lot like using or import statements from complied languages like Java and C#! What if we structure every file like this:

Meteor.startup(function() {

// Using statements for this file
var Reminders = MyApp.Collections.Reminders;

// Code for this file
Meteor.methods(“createAlert”, function(alert) {
Reminders.Alerts.insert(alert);
});

});

Just an idea. If we’re going to namespace our app so heavily, we’re definitely going to want something like this.

Namespacing Meteor constructs

The global javascript namespace is not the only global namespace to be managed in Meteor. There are a few other places you definitely don’t want conflicts:

  • Session variable names
  • Meteor.methods names
  • Publish / subscribe channels
  • Mongo collection names

I might be missing more, even. They all fall under the same category, though — APIs provided to you by the Meteor framework that ask you to register something by some string key.

If you care about the global namespace, then you should care equally about this. Within an app, if two separate pieces of code add to session, there’s almost no way for you to realize it’s happening unless it manifests as a bug and you eventually figure it out. At least with the other two, you’ll get an error saying that the key has already been registered. Again, though, you don’t want to be dealing with namespace conflicts as they come up; you want to devise a system that guarantees that you’ll almost never encounter them.

Across packages, it seems like the effect could be a little scary. I haven’t actually encountered this myself, so maybe Meteor handles this. Suppose we began using a package which privately uses a Mongo collection named “foo” to save state to the database. If our application was also using a collection called “foo”, we could be none the wiser that there was a conflict, and weird things could begin happening. The idea that a package could affect your app in this way is bad! At least we can be defensive by using appropriate namespacing.

The strategy for these is the same as the Namespacer object from above; write static objects or methods to manage these things. These methods would have an understanding of namespace. Here’s how you might apply namespacing to Session (stolen mostly from this stackoverflow post):

var namespace = function namespace(name) {
var prefix = name + ‘.’;

return {
get: function (key) {
return Session.get(prefix + key);
},
set: function (key, value) {
return Session.set(prefix + key, value);
},
setDefault: function (key, value) {
return Session.setDefault(prefix + key, value);
},
equals: function (key, value) {
return Session.equals(prefix + key, value);
},
namespace: function(name) {
return namespace(prefix + name);
}
};
};

_.extend(Session, {
namespace: namespace
});

// Usage:
var RemindersSession = Session.namespace("MyApp.Reminders");
// Does not conflict with Session.get("x");
ReminderSession.set("x", 1);

I probably wouldn’t use this exact approach; I might try to write something that’s better integrated with “Namespacer” from above. But you hopefully get the idea, and I don’t want to drown this post in code; you can manage Session’s namespace as well by namespacing the key you use, and by wrapping the API to the actual Session to make it simple and concise to use in code.

Namespacing: do it!

--

--