best practices for angular project structure

A few months ago, the AngularJS team published a document outlining a new best-practices project structure. There's also a (huge) Github issue discussing its pros and cons in finer detail, and it was mentioned in the generator-angular 2014 Q1 roadmap issue.

The old recommended structure, which is what yo angular generates, groups files together by function: controllers are with other controllers, directives are with other directives, etc. In some cases, they're all even in the same file.

app/
    css/
        app.css
        bootstrap.css
    scripts/
        app.js
        controllers/
            main.js
            sidemenu.js
        services/
            backend.js
        directives/
            datepicker.js
    templates/
        datepicker.html
        main.html
    index.html
test/
    spec/
       controllers/
           mainSpec.js
       services/
           backendSpec.js
       directives/
           datepickerSpec.js

That was easy for me to grasp initially and due to the strong tooling built around that structure, it was easy to add new directives and providers because the tools knew how to navigate the folder structure and set up the boilerplate. But, I really like the new proposed structure due to some shortcomings of the old one:

  • It doesn't scale very well. In a large project, the controllers, directives, and services folder will each have many files, and the majority of them will be unrelated to each other.

  • It's hard to find things if you don't know where to look, or if you've just forgotten where you put it. Given a front end component that in which you found a bug, it's not immediately evident where you would look to find and edit that functionality.

  • When editing a file, it's not immediately evident from the filename what kind of file you're looking at, as the 'type-of-thing' classification is done in the folder name.

The new structure is grouped by components and might look like

app/
    app.js
    index.html
    components/
        datepicker/
            datepicker-directive.js
            datepicker-directive_test.js
            datepicker.html
        backend/
            backend-service.js
            backend-service_test.js
    main/
        main-controller.js
        main-controller_test.js
        main.html

The components are grouped by functionality and house services, filters, directives, and related files (templates, test files, etc). Meanwhile, the sub sections like main would only have controllers, css, and templates. I really enjoy using the new layout and it makes it super easy to switch between the test and the implementation, since they're right next to each other.

When I converted my project to this structure, I had to account for the new file structure in the Gruntfile and in the karma configuration. Both of the config files have a useful **/*.js glob option that recursively includes all child files that match, but the bower_components folder also gets picked up, and then I end up linting someone else's files.

My current project only has one sub-section, editor, so the end of the karma.conf.js files section looks like:

 // our scripts
 'app/app.js',
 'app/editor/**/*.js',
 'app/components/**/*.js'

The gruntfile was a bit more of a pain, since the js files are referenced in more places. But, the watch:livereload task after the conversion looked like:

livereload: {
    options: {
        livereload: '<%= connect.options.livereload %>'
    },
    files: [
        '<%= yeoman.app %>/{components,editor}/**/*.{js,html,css}',
        '<%= yeoman.app %>/index.html',
    ]
}

It's a bit unwieldy - the a/{b,c}/e construct matches a/b/e and a/c/e, and the **/* does a recursive descent into folders, so that one line catches everything in components and in my only subsection, and skips the bower_components folder.