Large-scale apps: modular approach via packages vs folders

I’m wondering why people break down large apps into packages. What is the advantage of going that route instead of a folder-based approach?

So you could either have packages such as myapp-avatar, myapp-settings, myapp-listings, or you could create folders named avatar, settings, and listings, and put client/server/lib/routes within those.

The folder route seems simpler/cleaner to me. Is there some advantage of using packages that I’m not thinking of?

1 Like

I’m not sure about others, but for me it personally came down to having control over the load order and controlling scope.

I have the radical view that your app shouldn’t be any more complicated than it has to be, so use packages for the stuff you’ll reuse.

It’s one of the things I like about the Rails community, there isn’t this fetish of segregating and layering whenever and wherever possible.

And I always get something like “but what if I become the next Facebook???”

1 Like

If you become the next Facebook you will have much bigger problems than code quality :smile:

2 Likes

I’m not sure about others, but for me it personally came down to having control over the load order and controlling scope.

Can you elaborate on that a bit? With packages, can you specify an exact load order?

Yes, with packages you can specify the load order exactly whereas with folders you end up trying to nest files deeper in the folder structure or using character prefixes to get your load order correct… Don’t get me wrong, creating a package for part of your app is a lot more work but it can solve many of the hassles involved with creating large applications.

And the fact is that you can, of course, use packages in folders, which gives you the benefits of private packages, control of load order and separation of “components”.

myAppsPrivatePackages
         |
         +--myComponent1
         +--myComponent2
         .
         +--myComponent99
               |
               +--code.js
               +--package.js
               .

Then, set the PACKAGE_DIRS environment variable to myAppsPrivatePackages before running meteor.

If you have a number of shared private packages used between several apps, just split them off into another folder or folders and set PACKAGE_DIRS to a colon-separated list of directories to search:

export PACKAGE_DIRS=/pathto/mySharedPackages:/pathto/myAppsPrivatePackages:...
2 Likes

Creating packages is not really that much work. For example, to declare one global variable across the entire app, and have it accessible from anywhere in the code (not a simple thing to achieve with folders), I created a “core” package

meteor create --package myapp:core

Then, export the variable. This is the entirety of package.js (you don’t need tests):

Package.describe({
  name: 'myapp:core',
  version: '0.0.1',
  summary: 'MyApp core',
  git: '',  // not public
});

Package.onUse(function(api) {
  api.addFiles('core.js');
  api.export('MyApp');
});

Then in core.js, just export your global:

MyApp = {};
1 Like

Telescope is a bit special since it’s an open-source app and meant to be ran by many different users, but a big advantage of switching to a package-only model is that you can now meteor add and meteor remove distinct part of the app, like notifications, newsletters, etc.

I suppose having the ability to turn parts of your app on and off could be useful even in a more traditional app. But it does require architecturing your app so that it works properly even without these parts.

Other than that, controlling load order like others said.

Overall for a normal app I would probably recommend a hybrid approach, where you store any self-contained logic (say, like an automated newsletter) in packages, but keep the core code in the main directories.

Imo it’s kind of a question of how “generic” the component is.

For example, something like notifications pushed to the user should really just be packaged with some kind of configuration ability.
If it’s something more specialized, I would say it’s probably easiest just to add it into your application directly.

As an aside, I heard that putting things in package rebuild faster.

And as a bigger aside, there’s this super-interesting approach of loading dependencies in a reactive way at run-time:

https://atmospherejs.com/meteorflux/reactive-dependency

So in a package, unless you use api.export(), any variables or functions are automatically limited to the scope of the package?

Yes.

More specifically, if you declare it using var, it is file scoped, and without it, it is package scoped. If you declare it without var and export it with api.export() then it is application scoped.

Cool. And if I declare it with var and export it with api.export(), then what happens? Still application-scoped? Or you can’t export file-scoped vars?

You cannot export your variables if they are declared using var, meteor will just print out an error and will not build.

2 Likes