Developer's Guide to Improving Project Structure in Meteor Applications

View all articles

Over the past few years, there has been a dramatic increase in the number of JavaScript frameworks available. Ranging from the tried-and-true AngularJS to the score of frameworks that come out every month, there is an impressive diversity to choose from. One that caught my attention a few years ago is a framework called Meteor. Unlike most frameworks, Meteor aims to be a complete platform for JavaScript app development. For those who are new to Meteor, I encourage you to check it out on their website. This article will not be an introduction to Meteor. Instead, we will explore some simple ways to introduce structure in Meteor projects.

A Developer's Guide to Improving Project Structure in Meteor Framework

A Developer's Guide to Improving Project Structure in Meteor Framework

One of the great benefits of Meteor is that it is very easy to rapidly prototype complex JavaScript applications with it. Because Meteor is a hybrid of front-end templating and rendering coupled with a Node-based server interacting with MongoDB (a unified solution), most of the initial setup necessary for the creation of a full-stack web app is already done for you. However, the ease of development that this provides can be a trap. It is easy to fall into bad practices and end up with a jumble of unstructured code when building a Meteor app. I have a few suggestions for how this can be avoided.

Scaffolding: Manageable Directory Hierarchy in Meteor

First, it is important to maintain the recommended folder structure when building out your application. Meteor allows you to place files anywhere within the project folder by default - you can even have all of your code in a single file if you desire. While this might work for a hackathon project, the complexity incurred by usual production-level, scalable apps is hard to manage without a sound structure.

In order to solve this problem, I recommend checking out Chris Mather’s npm package iron. The package has a variety of configuration options so it will be hard to describe here, but in general it constructs a project structure that will look something like this:

my-app/
|- .iron/
   |- config.json
|- bin/
|- build/
|- config/
   |- development/
      |- env.sh
      |- settings.json
|- app/
   |- client/
      |- collections/
      |- lib/
      |- stylesheets/
      |- templates/
      |- head.html
   |- lib/
      |- collections/
      |- controllers/
      |- methods.js
      |- routes.js
   |- packages/
   |- private/
   |- public/
   |- server/
      |- collections/
      |- lib
      |- methods.js
      |- publish.js
      |- bootstrap.js

However, for some projects a folder and file structure like this can be an overkill. Not every project will need to have this fine-grained a level of organization, with separations for collections, methods, and publish code on the server. For those who don’t have too large of a project or simply don’t want to have to install and learn another npm package, I recommend this basic folder structure:

The key elements are a public folder that contains files like your favicon and other static assets, and the client, common, and server folders. The client and server folders should (of course) contain code that is executed on the client and server respectively. The common folder contains code that must be accessible to both the client and the server. An example of this is Schema code, which we will discuss in a bit.

There are two ways to perform the lowest level of organization: one is by file type, and the second is by function. Organization by file type means that in your client/templates folder, for example, you will have three folders - one for JavaScript files, one for CSS, and one for HTML. The HTML folder will contain all of your template HTML files, for example Login.html, Main.html, and so on. The JavaScript and CSS folders will contain template files of their type, respectively. Organization by function, on the other hand, means organizing by the concept that the files embody. For example, in client/templates, I would have a folder “Login”, with all the JavaScript, CSS, and HTML files associated with the app’s login process. I prefer organization by function as it allows you to be more clear about where you can find certain files or pieces of code. However, it’s not purely black and white, and most individuals and teams hit upon some mixture of these methods to structure their files and folders.

Schema: Your App Needs It Even If Your Database Doesn’t

The second kind of structure that I’d like to discuss is associated with the database. This article assumes that you’re using MongoDB. If you aren’t, the concepts will probably still apply but the specifics won’t. Those using MongoDB know that it allows the way we store our data to be unstructured. Since MongoDB is a NoSQL document-store database, there is no fixed schema for any “type” of data. This freedom means that you don’t have to worry about making sure that all of the objects are standardized to some rigid form, in fact, all of your app’s data could be thrown into a single collection if you desired. However, when it comes to making production-quality apps, this isn’t really as desireable. In order to manage this and add useful features such as validation of write-requests, we can turn to two wonderful Meteor packages: Aldeed’s SimpleSchema and Collection2.

The SimpleSchema package, as the name suggests, allows you to reactively validate objects in your app. Check out the docs on GitHub. The Collection2 package bootstraps off of SimpleSchema and allows you to bring proper schemas to Meteor collections. What this enables is automatic validation of client and server-side write requests to any collection with a schema attached to it. Both of these packages are very deep and customizable so it’s hard to give a complete understanding of them in a few paragraphs. Rather, I recommend you look at the detailed readmes that Aldeed has compiled for the specifics. I’ll simply talk about how I got value out of these packages. One of the best things that they enabled was validation of user’s form input. This comes in handy for validating Meteor User documents (from the Accounts package). By default, Meteor Users have a surprisingly complex implicit schema. Here’s a picture of a part of it from the code that Aldeed was so kind to make available:

Schema.UserProfile = new SimpleSchema({
    firstName: {
        type: String,
        optional: true
    },
    lastName: {
        type: String,
        optional: true
    },
    birthday: {
        type: Date,
        optional: true
    },
    gender: {
        type: String,
        allowedValues: ['Male', 'Female'],
        optional: true
    },
    organization : {
        type: String,
        optional: true
    },
    website: {
        type: String,
        regEx: SimpleSchema.RegEx.Url,
        optional: true
    },
    bio: {
        type: String,
        optional: true
    },
    country: {
        type: Schema.UserCountry,
        optional: true
    }
});

That’s simply the schema for the User’s profile object. Attempting to validate all of the User object would be a mess without a purpose-built package like SimpleSchema . While all of those fields appear optional in the picture, if you wanted a properly validated User schema they probably wouldn’t be, and things like “Schema.UserCountry” actually redirect to other schemas for validation. By attaching this schema to the User object and integrating this reactively to our forms, perhaps with a package like Aldeed’s AutoForm, we can gain a fine degree of control over what does and doesn’t make it into our databases, saving time and effort with an conceptual model of how data is handled in our application that can be pointed to and discussed on concrete terms.

Roles: For the 401 and 403s

The final suggestion I have for structuring and improving a Meteor project is Alanning’s Roles package. This is an authorization system for Meteor, and it allows you to check user access levels for any part of your web app. Permissions are attached to the User profile in the form of strings which are later validated or invalidated when the user tries to access any Meteor methods or data published to the client. For example:

if (Roles.userIsInRole(Meteor.userId(), "admin")) {
     tabsArr.push({
            name: "Users",
            slug: "users"
      });
 }

Although the core of the system is relatively simple, it allows for complex and fine-grained permissions for any part of your application. Since all of the roles are stored as strings, you can set up a system that is as deep as you like - “admin”, “admin.division1.manage”, “admin.division1.manage.group2”, and so on.

The trouble with the freedom that this package allows is that it can be hard to keep track of a highly granular roles system. The package does provide functions such as “getAllRoles” which, as the name suggests, retrieves all of the roles that you’ve created, but it’s up to you to keep track of what all of their meanings are, and when a given role should be used. And for those who are curious what the difference between “roles” and “permissions” are, for the purposes of this package they are essentially no different.

Wrapping Up

Unfortunately, because of the breadth of the article (each of the packages mentioned here deserves its own tutorial) it was not possible to go into detail about any particular package, but I hope I shed some light on how you can work towards a “standardized” Meteor application that you can be confident will work well in production and at scale. If you’re looking for more information, check out the links I posted and take a look at one more thing that didn’t make it to this article, but is useful to have in an Meteor app: the Restivus package, which allows you to easily expose a REST API for your app.

As a disclaimer, I am not the author of any of these packages, and I apologize if I have misrepresented any of the features or aims of any package. Thank you for reading, and I hope this article was of benefit to you. Feel free to contact me with any questions you may have, or leave a comment below.

About the author

Darion Cassel, United States
member since October 14, 2015
Darion has over three years of experience developing web applications in JavaScript and Python. He has worked for companies like Rackspace, CommVault, and Bristol-Myers Squibb, and for clients like Lagestee-Mulder, Inc. He has worked with many JavaScript frameworks, such as Meteor and EmberJS, and he has developed and released apps for both iOS and Android. Darion communicates well and has led both small and large teams in the past. [click to continue...]
Hiring? Meet the Top 10 Freelance Meteor Developers for Hire in December 2016

Comments

Michel H.
This article lacks experience with large scale Meteor apps. If you develop large Single Page Applications Iron Router isn't the goto solution anymore. The main reason I switched from Iron Router to Flowrouter was that Flowrouter is UI framework agnostic and uses template subscriptions. Flowrouter only runs once when url changes, where as with Iron Router I will have to strip away the reactivity to make larger apps work and that is just a not what it is supposed to do. Not a good advice. Iron router is certainly great in small projects as it saves you building your own autorun blocks. The file structure you suggest is good for applications with a lower lifetime. Because all parts are to connected and not independently packaged as they should for several teams to work on. This app structure makes is hard to develop and test new services for your app without breaking it. This structure isn't loose enough. I wrote about microservices and I pretty much think it'll change how we develop in the future. To make this work, if you develop with Meteor you need to go the smart package, hocks, and module route. https://www.linkedin.com/pulse/microservices-transform-development-michel-herszak Some more research on structuring meteor: http://stackoverflow.com/questions/10122977/what-are-the-best-practices-for-structuring-a-large-meteor-app-with-many-html-te/32597228#32597228
Decebal Dobrica
Nice article, the topic is highly controversial though and I must say reading it feels like my first encounter with Zend Php Framework. I do realize it is a stretch. You clearly put some effort into this and I can see the need for structure in this meteor. I do think you nailed some approaches, but not sure how much of it was your process and how much was the framework. As Michel suggests the architecture is not loose enough, I would recommend a study of Domain Driven Designs.
comments powered by Disqus
Subscribe
The #1 Blog for Engineers
Get the latest content first.
No spam. Just great engineering and design posts.
The #1 Blog for Engineers
Get the latest content first.
Thank you for subscribing!
You can edit your subscription preferences here.
Trending articles
Relevant technologies
About the author
Darion Cassel
JavaScript Developer
Darion has over three years of experience developing web applications in JavaScript and Python. He has worked for companies like Rackspace, CommVault, and Bristol-Myers Squibb, and for clients like Lagestee-Mulder, Inc. He has worked with many JavaScript frameworks, such as Meteor and EmberJS, and he has developed and released apps for both iOS and Android. Darion communicates well and has led both small and large teams in the past.