Angular Best Practice: Directory Structure

The most important thing is of course to find something that works for you. The other thing is that, as its creator Miško puts it, once you structure it in a particular way, and other people structure it the same way, it makes it easier for you to leap from one project to another.

So there’re few conventional best practises, take your pick:

1. Divided by Type

This is easy to grasp. When the app is not too large, you could just directly go with:

   |— controllers/
   |— directories/
   |— animations/
   |— filters/
   | …

The pitfall is of course, when the app grows increasingly large it gets messier, harder to maintain, and it’s a struggle finding the exact module you need to work on. For the current project I’m working on, I had initially structured it like this, until it grew from a cute tiny bunny into a hug-ful of fur. That’s when I decided that something needed to be done with it. I went with:

2. Divided by Purpose


Basically, under the main app/, directories are divided by functionality:

   |— common/
   |— my-dashboard/
   |— view-report/
   |— build-report/
   | …

And because it’s such a complex system that each functional page are sorted out and structured in much the same way:

   |— controllers/
   |— directives/
   |— animations/
   | …
   |— module.js
   |— module.require.js

Note that I have a module.js file as well as module.require.js file. The former one serves the purpose of creating DI module for my Angular app. Since in Angular a module needs to be declared before you could make controllers and directories its components, in module.js of my-dashboard/ we have:

define(['angular'], function (angular) {
   return angular.module('graph.myDashboard', []);

The string “graph.myDashboard” is the name we’re going to inject into the main app module, and this module.js is the file I’ll be needing in all the controllers under my-dashboard/, for instance the structure of my-dashboard/controllers/ is basically:

define(['angular'], function(){
   angular.module('graph.myDashboard').controller('MyDashboardCtrl', function(){…});

Notice that I used requirejs together with angular, therefore I need to require all the files needed somewhere. This is what the file mydashboard/module.require.js is for:

  // controllers
  // directives
], function () {});

Which solved my problem.


Briefly summarised, the divide-by-type type is for smaller projects, while divided-by-purpose is for chubbier ones. The most important thing is still, as I said, finding the one that works best for you.