matb33.me

Mathieu Bouchard

Meteor project structure — the way forward

Meteor, my development framework of choice these days, is awesome. Just like any framework, the initial learning curve will slow you down — but unlike other frameworks I’ve used, once you grasp Meteor, you get things done fast. The ratio of idea to lines-of-code is just really high.

On the other hand, no one likes wasting time wrestling with magic file-loading rules. If you’ve ever found yourself needing to nest a lib/ folder inside a lib/ folder, you are told you are doing it wrong. But that’s no comfort when you just want to write code.

Thankfully, there’s a better way.

Meteor 0.6.0+: local smart packages

Meteor did something amazing for my productivity when they released 0.6.0: they reduced the friction to creating local smart packages to near zero. Apart from doing some scaffolding (i.e. creating a folder, making a package.js file, filling it out), it couldn’t be simpler.

And what's the big deal with local packages, you might ask? In short: full control. Every file is explicitly loaded (api.add_files), dependencies are explicitly defined (api.use), and as of Meteor 0.6.5, symbols are explicitly exported (api.export).

Then I had this radical thought: could I run my entire app out of smart packages?

After all, there is a recommended project structure in the Unofficial Meteor FAQ — would I be shooting myself in the foot by diverging from current best practice?

Well I wouldn’t be putting the effort into writing this for no reason. Spoiler: it works really, really, really well. Your root Meteor folder will look a little bit like this:

packages/
public/

Nice. The private/ folder isn’t applicable to this project structure — you add private files in your smart packages, in any subfolder you choose, loading them via api.add_files, and using them with Assets.getText/getBinary.

A Smart Package per Feature

The premise behind this project structure revolves around separating the duties of your app by "Feature". Each package stands on its own, and dependencies are explicitly declared.

Generally speaking, there are two types of packages: generic and app-specific (or just app for short). Most packages you’ve been using are of the generic variety. They are not specific to your app as they can be re-used from project to project.

On the other hand, app packages are indeed specific to your app. The sum of these packages is what you would normally scatter across server/ and client/ folders. These app packages sometimes even depend on a similarly named generic package.

An Example

  1. Suppose I have a generic package zencoder (which is a video encoding SaaS). It knows how to talk to the Zencoder API, but it does not know my credentials (and nor should it).

  2. I would then create an app package named app-zencoder. This package is lightweight — it depends on the zencoder package, creates a singleton instance and configures it with my credentials. That’s where its responsibility ends.

  3. So what’s next? We need to allow our user to upload a video, right? Generic package: filepicker. App package: app-filepicker, where the singleton instance is spawned and credentials stored.

  4. Next, I’d create an app package named app-encoder, depending on app-zencoder that would be responsible for calling out to zencoder for encoding in the manner that makes sense for my app requirements.

  5. Then, an app package to manage uploading, something like app-uploader, which depends on app-filepicker and app-encoder. It would hold Meteor templates, event code, all that’s needed to fire up Filepicker, and pass control off to app-encoder.

  6. I’d probably end this particular dependency chain with one more app package, something like app-video-section that might tie this all together, i.e. it might have Templates and code specific to the video section of the site. Perhaps a click event defined in this package calls out to a method in app-uploader?

The pieces start to fit really well when taking this approach. Sometimes you might start with what seems like an app-only package, but then realize you could genericize it into a generic package, and keep the app-specifics in your app-xxxxxx package. Later on you might even release it on Atmosphere!

So that’s the gist of it.

I’d love to hear success stories with this project structure (that are not my own!), and perhaps tips to make the approach even better. Have at it!