How to structure large angularJS applications

I did it all wrong in my first angularJS application. Oh I did it really wrong!

The structure of my first angular project looked something like this:

Root folder
js
controllers
Somecontroller.js
Anothercontroller.js
controller.js
directives
Firstdirective.js
SecondDirective.js
directives.js
services
AService.js
BService.js
services.js
app.js
index.html
partial1.html
partial2.html

So, all controllers were going to “controllers” folder, services into “services” folder and partials lived directly next to full html files. When we started to have more than 10 directives and 5 services the problems with this approach were really backfiring:

  • it was not easily visible what files were depending on what other files
  • refactorings were thus quite hard
  • reusing features in other code/applications was hard as the structure didn’t allow to group files of a single feature together
  • it was hard for new developers to understand the flow of the application
  • it was hard to extend the application so that it not only consisted of one (single page) application but of two applications that shared some features

Since then I tried a few different approaches and while experimenting with ngStart as a skeleton for new angular projects I’m now settled with an approach I want to use in all my next projects.

What I came up with, is an approach that groups features into angularJS modules. The main advantages is that the code structure is based on features of the application and every feature is defining its own angularJS module. The angular root module then is the glue and binds all modules together.

The structure of ngStart looks like this:

Root folder
modules
contact
contact.js
contact.html
ContactController.js
ContactService.js
about
about.js
about.html
AboutController.js
app.js
index.html

The main idea is that every folder under “modules” is by definition an angular module. The module is always declared in a javascript file that is named like the folder (“about” -> about.js) and is the name of the module too.

Each angular module can use the full list of angular features. It can have its own angular.config() phase function, can declare its own routes and can have its own list of dependencies.

modules

To refactor a certain feature you now know exactly where to begin and what files are directly dependent on each other when they implement a single feature. You can move modules around and even move them out of the your first single-page app, make another app and declare a dependency on the module to reuse your code.

In a small and trivial example the advantages of this approach are not easily visible, but as soon as a team of developers works together on an application it will grow very fast.

In enterprise applications there is usually a software architect who defines a structure which the team has to follow. Programming languages in the backend also have features to structure code in packages or modules and import it. Javascript doesn’t. And often times the software architect doesn’t care about javascript or the frontend at all. And frontend developers tend to not care about it neither as tools were missing in the past and there wasn’t really a need for it when writing jquery scripts.

But as frontend engineers or frontend developers of today, you should care! AngularJS is great. But angularJS applications will face the same problems as every other application that begins to grow. And your structure should make it easy to grow and adapt. You should care from the beginning!

14 thoughts on “How to structure large angularJS applications

  1. I like the idea of grouping the features into modules.
    I find it inconvenient, in basic structure, to jump between folders such as controllers, filters and views while working on specific module.
    But there are some cases where I need to implement the features which will be reused by other modules.
    For example, overlay directive, or some filter.
    I can think of adding new module, let’s say “common” and place all shared features (directives, filters, etc.) into it. Or maybe separating shared features based on their purpose, e.g. directives, filters, etc.
    Could you advise on how to structure some shared functionality?

    Thanks,
    Sergey.

    1. Yes, in fact, we have a module “common” in our real project where all features are in, that need to be shared across apps. Every app then only declares a requireJS dependency to that module.

      You can place the “common” module outside of your apps (in some root folder for example), as you can declare relative dependencies like so:

      define([“angular”, “../../common/common”], function(angular) {
      var app = angular.module(“myApp”, [“common”]);
      ….
      });

      (that would be the app.js in ngStart)

  2. I totally support this idea.
    Сергей, for shared functionality, say, directives and services, I have a an independent module for this and I just call it “services” or “directives”. For each of the services and directives I also have them in their independent module and then there is this “global” module which integrates them all. An example looks like this:
    /app/common/services/notification.js
    /app/common/services/session.js (or user_session.js, whatever)
    /app/common/services/services.js
    And then notification.js defines a module “services.notification” and session.js defines “services.session” and finally, services.js defines “services” and depends on “services.notification” and “services.session”
    So if at any other module I only need X or Y service I include either of them and if I need them all then I include the “services” module which already depends on the rest of the services. I do the same thing for directives.

  3. Sorry for the double post but I forgot to mention that I also go one step beyond and declare the routes in each of the modules as I like my modules to know everything about the business rules (including the routes where they should work on).
    That would be something like:
    angular.module(“posts”, [“ngRoute”]).
    config([‘$routeProvider’, function($router) {
    $router.
    when(‘/posts’, {
    controller : ‘ListCtrl’,
    templateUrl : ‘/app/posts/list.html’
    }).
    when(‘/posts/new’, {
    controller : ‘NewCtrl’,
    templateUrl : ‘/app/posts/new.html’
    });
    }]);
    and so on.
    Then when I include these modules in my main app, I don’t have to configure anything else. I just plug it in and it just works.
    This also helps a lot with the testing. It keeps everything in its place.

    Thanks for sharing and happy coding!

    1. Yes, and if you look into the ngStart project, its exactly done like you describe. Every module has its own routes, too 🙂

    1. Interesting, didn’t know the term, but after some googling, yes, it very much looks like functioal decomposition.

      Very interesting to see, that its thought of a anti-pattern in object-oriented languages or environments.

      Since angularJs applications are far from object-oriented, I don’t give much about it, but I’ll have to think more about it when we discuss the architecture of our java application next time.

Leave a Reply

Your email address will not be published. Required fields are marked *

*