We’re going to take an existing application and apply the principles laid out in Sacha’s screen cast to convert the application to a package based architecture. We’ll be replicating a lot of the conventions used in Telescope such as utilizing a lib and core package.
So before we get going, what’s the point of using a package based architecture? Well, there are a number of reasons that Sacha covers in his screen cast:
- File load order. The load order of files is explicit when using packages. This means you can avoid any load order issues that might occur with a traditional structure.
- Updates. Updates on your application can be handled via Meteor update.
- Customizations are easier. If someone alters the source to add features, this can be done via a package, thus ensuring the core code of the application is not compromised.
- Code organization. With packages, organization of your code is more modular and coupling between components is reduced or eliminated completely.
- Typically each feature has it’s own package.
- Each feature is independent, you can remove a package and the application still works.
Why not packages?
The only real drawback to using a package based architecture is that it adds some minor complexity.
For smaller projects, a package approach might be overkill. However, I’m very impressed with the package approach and feel even for small projects it could be worthwhile despite the added overhead.
What we’ll build
For those of you who have gone through the paging and sorting tutorial, this is going to look familiar. Sorry about the recycled application, but taking an existing application seemed like a good way to demonstrate the differences between a standard and package based architecture.
Note we won’t be doing much explanation or examination of the actual implementation code, mostly we’ll be moving code around and dealing with
package.js files. If you want an explanation of the code, check out the paging and sorting tutorial.
Creating the app
If you’ve gone through the paging and sorting tutorial you can use the existing code you built out from that. If not or if you want to start from a fresh code base you can clone the starting point for this post from GitHub.
Clone the Repo
Note, if you aren’t familiar with Git and / or don’t have it installed you can download a zip of the code here.
A quick over-view of where we’re starting from
Open up the code in your text editor of choice and you’ll see a pretty standard Meteor file structure.
By the time we’re done, our project structure will look radically different:
Start up the app
OK, let’s see where we’re starting from.
You should now see the starting point for our application when you navigate your browser to http://localhost:3000.
An over-view of the packages we’ll be creating
Our goal is to break up the code into a package based structure.
Now our application is a contrived example and as a result we’re going to go a little package crazy with it, we’ll be splitting up the “list”, “add”, and “newest customer” functionality.
This is for illustration purposes, with a real application, likely all the customer functionality would be contained in a single package, i.e.
However, for our purposes, we’ll be creating the following packages:
We’ll be taking an iterative approach so our application will continue be usable throughout the conversion process.
Let’s get started!
Creating the lib package
We’ll be following the same convention as Telescope and creating a
lib package. The purpose of this package is to contain all our core Meteor and 3rd party references. This is also where we set up the global namespace for our application.
First off we need to create some directories and files.
Then we’ll set up the global namespace. We’ll call our application ‘Customer Tracker’ so will go with that for the namespace.
Now let’s move onto the
The main purpose of the
lib package is to reference our 3rd party packages, so let’s see what packages our application currently uses:
Awesome, with the above information we can now fill in
The describe section
This section of the file just sets some basic attributes for the package. The
name attribute is important as that is what we’ll use to refer to the package when we add it to our application, i.e. when we issue the
meteor add command.
The version is also a critical piece of information as it is used by
meteor update to indicate when a new version of the package is available.
Specifying compatible Meteor versions
OK, next we have the
package.onUse block which is basically the definition of the package. The
api.versionsFrom line indicates the version of Meteor that our package requires. We’ve specified
1.0, so we’re saying our package can be used with any version of Meteor 1.0 or higher.
Linking to 3rd party packages and Meteor core packages
Next we specify the packages our custom package uses.
Here’s where the information we gleaned from
meteor list comes into play. We set up a
packages array to contain our list of packages. Note for third party packages the version number is required.
api.use indicates which packages are required by our custom package and makes use of the package array we put together.
api.imply exposes the internal packages of the
lib package to any packages that in turn use
lib. This means for instance that if you use the
lib package in a second package, say
secondpackage has access to
iron:router etc. without having to explicitly include it in it’s own
This is very useful as it means you only need to specify a dependency once and don’t need to worry about conflicting version numbers for the same 3rd party package creeping into different parts of your application’s
package.js files. If you’re a little confused, don’t worry, when we implement the
core package we’ll see a concrete example of
imply which will help to better illustrate why
imply is so useful.
Specifying the files to load
Next we have the
Unlike a traditionally structured Meteor application you need to explicitly specify which files Meteor should load. For
lib, we only have the one file,
core.js and it should be available on both the client and server, thus the
['client', 'server'] array.
Exporting items available outside the package
Finally we need to export any variables we want to have access to outside of our package.
We want access to the
CustomerTracker namespace variable, so we’ve added an export entry for it.
Now comes the exciting part, using the package we created. First thing we’ll do is blank out our existing package file.
Bam, as expected our app is now crashing hard!
Time for our new package to come to the rescue.
And we’re back working!
Also we now have access to our namespace variable.
Our main package file now contains the single custom package we created:
This is pretty cool, we’re now referencing all our third party and Meteor core packages through the
lib package we created.
Another great thing about the way packages work that we’ve illustrated is that if you’re converting an existing application to use packages, you can do it in steps instead of all at once. This means you can continue to add features instead of needing to stop all new development until everything is converted.
Creating the core package
Next up is
customertracker:core. This package will contain the bare skeleton of our application. In our case we’ll include our general layout pages, our router configuration and our custom parameter checking code.
OK, let’s get started by creating some directories and files.
Now let’s move some of our existing code into our new package.
In the case of the custom check code we’ll update it to use the namespace we created in the
OK, all we’ve done is update the code to use our new namespace.
We’ll need to change our publications to reflect this namespace change.
Simple, we’ve just updated
Next let’s update our stylesheet in both our existing application and the package.
All we’re doing is moving the “core” styles into our core package. Since we don’t have much styling going on, it’s only the
body style element that we need to move.
Now that we’ve moved some of our layout and router code out of our application and updated our stylesheets, we’ll see that the UI isn’t looking too hot.
Our styling is gone and our publication for listing customers is failing due to the custom check code not being available.
Let’s update our package file so that we can get our original look and feel back and get the publication working again.
OK, so very similar to the package file we put together for
customertracker-lib. The main point of difference being that we have a single package we’re using, our
lib package. By including it, we are also including all the 3rd party packages from
lib via the
imply line included in
lib that we discussed earlier.
Note once again we are using imply with the
core package as we want packages that use
core to have access to the packages referenced within
core, i.e. all the packages included in
A diagram will give a better illustration of what we’re aiming for:
Let’s update our application to use the new package.
And there we go, our layout and styles are now back in place!
Creating the customer package
Now we’ll start getting into the packages that deal with our customer specific functionality. The
customertracker:customer package is going to contain our collection and fixture code, as well as our routes for the customer functionality.
Once again, let’s start off by creating some directories and files.
Now we’ll move our schema and fixture file out of our existing application and copy over our collection file.
As expected this is going to cause all kinds of trouble for our application.
Before getting started on our package file, we need to make an update to our collection in both the package and existing application.
We’ve moved the collection into our package, while maintaining the
insertCustomer method outside of the package.
So let’s get our package file updated so we can get our app back up and working.
Pretty simple, we’re using a single package, the
core package we created earlier. Then as before specifying the files in our package and exporting the
Customers collection so it can be used outside of our package.
Now to get things working we just need to make use of our new package.
Load order example
Let’s go on a quick diversion and see an example of load order in action. We can manipulate the load order of the
customer package and trigger an error via altering the “client / server”
addFiles call. If we reverse the load order the application will crash.
As the console log suggests, the problem is that if we attempt to load our schema file before the collection file, we’ll be attempting to attach our schema onto a customers collection that is not yet defined.
So this illustrates how you can control the load order of a package, the files get loaded in the order they are specified in the
addFiles array. This can be a much more elegant solution to load order issues compared to a traditional Meteor application where you would need to create nested folders or rename files.
Creating the newest customer package
OK, time to package up some of our UI elements. The
customertracker-newest package will be responsible for the “most recently acquired” section of our UI, i.e.
As usual we’ll start off by creating some files and directories.
And now let’s move some code.
With these changes our application is once again crashing. This is because we are now missing the
We’re going to come up with a better solution for handling this but for now we’ll make a change to the
listCustomers template to get rid of our error.
All we’re doing here is adding a new helper function that returns a boolean value indicating whether the
newestCustomer template exists or not.
We can then use this helper in our HTML file.
With that in place our app is back working without the new customer portion of the UI.
Before updating our package file we need to add the publication for our newest customer functionality.
So first let’s update our existing publication file.
Nothing complicated going on here, we’ve just commented out the publication we’ll be moving.
Now let’s add it to our package.
And now we can update the file.
Simple, we’ve just moved the code we commented out into our package.
Now let’s update
Pretty simple, we’re just adding the necessary files and referencing our
Time to get our newest customer showing up again.
And with that our newest customer component is once again showing up in the UI.
Creating the list package
Next let’s move the list functionality into a package.
This is getting redundant right? But that’s a good thing, we’re getting the hang of creating these packages and so far it’s turning out to be pretty easy. Let’s get some files and directories created.
And now we can move some files.
Again, we’re just moving the relevant files to our package, and as expected this causes our application to crash.
With our list component we have a style element to move, so let’s update our existing style file.
We’re slowly getting rid of our styles, we’ve commented out the
th style and we’ll move this into our package.
Now it’s time to update
Once again we’re using
core and adding the necessary files.
OK, let’s get our app back working.
Sweet, that was easy, onto the next package!
Creating the add package
OK, our last package… once we get this sucker finished off, we’ll be completely package based.
As you can probably guess from the name, this package will handle our add functionality.
Yup, that’s right, time to create some directories and files.
Now let’s move our files.
Now our add customer functionality is broken.
We can move our final piece of styling out of our original application.
Everything in the original file has been commented out, let’s add the button style to our add package.
Now let’s update the package file.
No explanation needed, we’ve seen this all before!
OK, let’s get our add functionality back working.
And there we go, we’re package complete! Our application is working as before but everything is running from packages.
So now that we have everything wrapped up in packages we can do some clean up on our directory structure.
You can also go with
rm -r client etc. or manually remove the folders if you don’t want to install trash.
In any case, however you delete your folders, you should now see that everything is completely within package directories.
Updating our packages to be more modular
There are a few artifacts of the original implementation which limit the modularity of our package based implementation. Ideally we want our ‘feature’ packages, i.e. the ‘list’, ‘add’, and ‘new’ packages to work independently.
This isn’t currently the case however, if we remove our list functionality the application crashes, i.e.
We’ve commented out
customertracker:list, which results in:
Likewise if we remove our add package, we still have an add button on our main list page.
Making our three feature packages modular.
To enhance modularity we will create a main index page for our customer functionality. We’ll also remove the add button from the
listCustomer template and put it in it’s own template.
Let’s start off by creating the index page. A good place for this file will be in the
customer package; it is specific to our customer functionality but not associated with the “list”, “new”, or “add” functionality.
OK, so all we’re doing here is laying out the components we want on our main index page. What’s up with that
render function? Well we want to only render components that exist so we’ll create a helper to handle the rendering of our optional components.
We’ll throw this into our
core package as it’s something that could be useful outside of the customers functionality. For instance if we enhanced our application to include orders, we might want access to the helper in the orders packages. These packages might not reference
customertracker:customer but would certainly reference
Pretty simple we’re just checking if a template exists, if it does we return it, otherwise we return
null. This will prevent attempts to render non existent templates.
So let’s update our
package.js files to take into account our new items.
Now we need to create our add button template and remove the button from the
Simple, we’ve just copied the HTML and JS code for the add button into our newly created files.
Let’s update our package file.
Now we need to remove the add button and newest customer code from our list package.
So we’ve removed the rendering of both the
newestCustomer template and the add button code.
We can now remove the add button handler.
And also the helper method we added earlier to check for the existence of the newest customer template.
Next we need to update our routes since we no longer want our
listCustomers template to act as our root route.
We want our root route to hit our new
customerIndex template instead of directly hitting the
With the router changes we need to update code that refers to the old
listCustomers route to instead refer to the
So here we’ve changed instances of
Router.routes.listCustomers.path to the new route of
Router.routes.customerIndex.path. We’ve also replaced instances of
We need to make a similar change in
Again we’ve replaced instances of
And with that we can now use any combination of the 3 packages.
For instance removing the
list package results in the following.
Pretty cool, our application still works, we can add customers and view the latest customer that was added, but our list functionality is no longer present.
Although very much a contrived example, hopefully this post gives some insight into what’s involved in a package based architecture.
Is it pretty silly to use a package based architecture on a small example application? I’d say yes without a doubt! However, I am very impressed with this approach to building Meteor applications and I think it merits consideration when creating a non-trivial Meteor application.
Using packages has many advantages, primarily around code organization and keeping your code modular and decoupled. The ability to add and remove features via
meteor add/remove is pretty sweet as well. I can see many situations where this would be useful, such as demonstrating beta features to customers.
The fact that a project can be converted in an iterative manner means an initially small application that looks like it will grow can be upgraded to use packages without completely stalling new feature development, this is great! Getting buy in on a refactor that will completely halt new feature development is much more difficult than diverting partial effort towards a refactor.
There are certainly some drawbacks to a package based architecture, it is a little more complicated to develop applications in this manner. You could also run into complexities with package dependencies, for instance circular package dependencies where package A needs package B which needs package A. In someways this is probably a good thing however, as a circular dependency likely points to a fault with your application structure / design and indicates a refactor is in order.
A great next step if you’re interested in further exploring package based architecture is the Telescope source on GitHub. There’s nothing like looking through a real world application.
A couple of great resources that helped with putting together this post include:
- Sacha Greif’s screen cast on package based architecture.
- The hackpad referenced in Sacha’s screencast.
- The writing a package tutorial put together by the Meteor Chef.
Thanks for reading and I hope you enjoyed this post!