Angular App Structuring Guidelines | John Papa

John Papa

Evangelist on the loose

Angular App Structuring Guidelines

...

App Structure

I find it extremely helpful to have an idea of what is important to me and my team in structuring my app instead of focusing on the actual structure.

You have to find your own path that is comfortable for you and your team. Instead of just picking a structure, it’s helpful to think about the “why”, so I’ll walk you through the guidelines I follow and my thought process on why I choose how I do. This is a follow up to my post on Angular Structure.

How you organize your structure is up to you. There are many right ways of doing this, and consistency in your project is key.

These are my guidelines. I’m sharing them as one possible path you can take but the main point of this post is to share how I think about structuring my Angular apps. There is a reason for the madness and if you take anything from this post it’s to be sure to have a set of guidelines and conventions of your own.

Thanks to my friend Ward Bell for helping review these and in many cases, contributing to the content quality over the several projects with me.

The LIFT Guidelines

The structure should follow these 4 basic guidelines. When I find my structure is not feeling comfortable, I go back and revisit these LIFT guidelines

  1. Locating our code is easy
  2. Identify code at a glance
  3. Flat structure as long as we can
  4. Try to stay DRY (Don’t Repeat Yourself) or T-DRY

Another way to check your app structure is to ask yourself …

How quickly can you open and work in all of the related files for a feature?

I find placing related files for a feature in the same drawer really helps me be more efficient.

Locating

I find this to be super important for a project. If the team cannot find the files they need to work on quickly, that needs to change. I’ve been on projects where this has been an issue and it wastes time.

Locating code needs to be untuitive, simple and fast. You may not know the file name or where its related files are, so putting them in the most intuitive locations and near each other saves a ton of time.

Identify

When I look at a file I expect to know what it contains and represents. If this means you want longer file names, then so be it. For me its more about being descriptive with file names and keeping that contents of the file to exactly 1 thing. No files with multiple controllers, multiple services, or a mixture.

There are deviations of the 1 per file rule when I have a set of very small directives or filters that are all related to each other, they are still easily idnetifiable. If not, 1 per file.

Flat

Nobody wants to search 7 levels of folders to find a file. Think about menus on web sites … anything deeper than 2 should take serious consideration. In a folder structure there is no hard and fast number rule, but when a folder has 10 files, that may be time to create subfolders (or drawers as I like to call them).

The general guidelines here is base on your comfort level. I prefer a flatter structure until I see a specific value (to help the rest of LIFT) in creating a new folder.

T-DRY

Being DRY is important, but not crucial if it sacrifices the others in LIFT, which is why I call it T-DRY. I don’t want to type session-view.html for a view because, well, it’s obviously a view. If it is not obvious or by convention, then I name it.

I don’t name my controllers with “controller” in their file name nor their registered name with Angular. I differ from others on this point, but it feels so un-DRY for me. I may have 30 controllers and continually typing “controller” everywhere (in file names and in code) and its obvious that it is a controller felt completely un-DRY.

Angular’s Registered Asset Names

Naming is not just for the files but also for the names of the registered assets. For example I prefer to name my controller files without the word “controller” in the file name such as sessions.js. This is a convention I use. Inside the file the controller is registered with Angular as sessions again. Why? A controller is requested from HTML or JavaScript by name. But when it is requested it is either requested in one of these ways:

<div ng-controller="sessions">

</div>

And in JavaScript for route configuration …

$routeProvider.when(url: '/sessions', {
        templateUrl: 'sessions.html',
        controller: 'sessions'
    });

It feels extremely obvious to me that these are referring to controllers which is why I prefer this over naming the registered controller SessionsController. Which is why I name my registered controllers like this:

angular.module('app').controller('sessions', ['$scope', sessions]);

function sessions ($scope){

}

Just for contrast, let’s take a look at what it looks like with “controller” in the name.

<div ng-controller="sessionsController">
</div>

Here the ng-controller is set to a sessionsController. Not very DRY to me. The following JavaScript also demonstrates this redundancy.

$routeProvider.when(url: '/sessions', {
        templateUrl: 'sessions.html',
        controller: 'sessionsController'
    });`

You will likely see the controllers named with a suffix of controller in many examples on the web. That’s perfectly fine and I realize I buck that trend. Again, when confronted with multiple ways you can go, I suggest you refer back to the LIFT guidelines and find what feels and works best for you.

One Size Doesn’t Fit All

One project structure may make more sense than another depending on other factors, such as size. For example, a small app that has just a handful of assets (views, controllers, services, etc) won’t warrant as many folders and organizational aspects as an app that has dozens or hundreds of assets.

Often when I start an app I begin with the flattest structure that makes sense for what I know. Let me rephrase that because it is important: I don’t guess at the structure for things I do not yet know. I design for what I do know and adapt as I go. This is important because it does not paralyze me into worrying about making the wrong choice in the structure. I start small and adjust as needed.

Assumptions

The scenarios below assume we start with a single module app and build up to a much larger app scale.

  • I prefer to have a near term view of implementation and a long term vision. In other words, start small and but keep my mind on where I am heading down the road.

  • All of my app’s code goes in a root folder named app.

  • All content is 1 feature per file. Each controller, service, module, view is in its own file. Small deviations are OK for thing like a set of small, short directives in a directive.js file

  • All 3rd party vendor scripts are stored in another root folder and not in the app folder. I didn’t write them and I don’t want them cluttering my app. I keep my dependencies in a scripts folder.

  • All content assets go in another root folder. Your naming may vary.

One option is to use a folder for each type:

css/
fonts/
images/

Another options is to use a content folder.

content/
    css/
    fonts/
    images/

Or even just a content folder for all css, images, and fonts.

App Structure Examples

Oh No! Naming Conventions

Naming conventions help provide a consistent way to find content at a glance. Consistency within the project is vital. Consistency with a team is important. Consistency across a company provides tremendous efficiency.

The naming conventions should simply help the findability and communication of code. Here is one set of naming conventions I recommend.

There are 2 names for most assets:

  • the file name
  • the registered asset name with Angular

Modules

An app with 1 module is named app.js. It is the app, so why not be super simple.

When there are multiple modules, the main module file is named app.js while other dependent modules are named after what they represent. For example, an admin module is named admin.module.js. The respective registered module names would be app and admin.

Configuration

I separate configuration for a module into its own file named after the module. A configuration file for the main app module is named config.js. A configuration for a module named admin.module.js is named admin.config.js.

Provider configuration also goes in here.

Route Configuration

I separate route configuration into its own file. Examples might be config.route.js for the main module and admin.config.route.js for the admin module. Even in smaller apps I prefer this separation from the rest of the configuration. I also like the shorter names such as admin.route.js.

Controllers

I name controllers after what they control. Examples may be registration.js, speakers.js, or speaker-detail.js. The registered assets for controllers would be Registration, Speakers, and SpeakerDetail.

By convention my controllers do not have the name “controller” in them. I find myself in controllers often (with services/factories right behind those), so I chose controllers as the place to start the convention.

Services / Factories

Services provide some service to my app for shareable features such as remote data access, data caching, local storage, and logging.

I name services after what they provide. Examples may be datacontext.service.js, storage.service.js, or logger.service.js. Their registered asset names with Angular would be datacontext, storage, and logger respectively.

Shared Features

For assets that are shared by other assets in different features, like many services often are, I place them in a folder named shared. If the asset is for a specific feature, I place it in that feature’s folder instead.

Directives

When it’s not obvious if the asset is a directive or service, I name directives with a directive suffix such as spinner.directive.js and the asset would be xxSpinner, where xx is the prefix for the directive.

If there are multiple related directives, I place them in the same file. This is one place I deviate from the 1 asset per file convention.

Layout

There are often assets that you’ll want to use for the layout of your app. For example a shell view and controller to act as the container for the app, navigation, menus, content areas, and other regions. I place these in a folder named layout.

Multiple Modules

When multiple modules are involved, I place those modules in their own folder under the app/ root.

Starting an App – Extra Small

Let’s start with a small app and see what the structure might look like. Then let’s discuss how to adjust the app structure as it grows.

Let’s assume I have an app where I have just 1 service. I’ll place it in the root. For example, it might look like this:

app/
    app.module.js
    app.config.js
    data.service.js
    sessions.html
    sessions.controller.js

I can locate my code, identify what each file represents at a glance, the structure is flat as can be, and I’m not repeating myself with redundant names. Thus the LIFT guidelines are all covered.

Adding Depth with Drawers – Small App

The need for a few more assets arises, so I have 3 small directives, a spinner service, a logging service and a local storage service too. I feel the root is starting to get cluttered so I want to organize the content by creating drawers to keep them aligned with the LIFT guidelines.

Here is where we are before refactoring.

/* 
* Locating files is difficult here.
* Although it is flat, it is too flat as
* it becomes difficult to locate files at a glance.
*/

app/
    app.module.js   // main app module
    app.config.js       // module configuration
    data.service.js 
    directives.js   // small set of directives
    localstorage.service.js
    logger.service.js   
    sessions.html   // view
    sessions.controller.js
    spinner.service.js

Here is where I start to create drawers to organize the assets.

/* 
* Adding a drawer for services brings us closer to LIFT
*/

app/
    app.module.js          // main app module
    app.config.js       // module configuration
    directives.js
    sessions.html   // view
    sessions.controller.js     // controller
    services/       
        data.service.js  
        localstorage.service.js
        logger.service.js   
        spinner.service.js

The 3 – 7 File Guideline

I feel that somewhere between 3 to 7 files is where I start to consider creating a drawer for them. Your threshold may be different, so adjust as needed.

Notice that when I have just a handful for small directives, I keep them in a directives.js file. Once this grows, I start to break those out too.

Adding More Views and Routes

Once I start adding in routing to multiple views I’ll want a view that acts as the container for the other views. I call this the shell. The shell will need a navigation view to help with routing between the content views. Of course, I’ll need some more views (ex: speakers and attendees), and all those views may warrant controllers.

/* 
* Adding just a few more features
* makes it get uncomfortable quickly.
*/

app/
    app.module.js
    app.config.js       
    attendees.html      
    attendees.controller.js
    app.routes.js    
    directives.js
    sessions.html       
    sessions.controller.js
    services/       
        data.service.js  
        localstorage.service.js
        logger.service.js   
        spinner.service.js
    shell.html          
    shell.controller.js            
    speakers.html       
    speakers.controller.js         
    topnav.html          
    topnav.controller.js           

OK, this is getting a little uncomfortable again in the root so let’s look back at LIFT. Can I locate my files? Well yes, but its not as easy as it was. The rest of LIFT is OK, but this first aspect of locating my files is super important, so it’s time to refactor and get back to LIFT. I can see that there are a few assets related to the layout, so I start there and group them in a layout drawer.

/* 
* Adding just a few more features.
*/

app/
    app.module.js
    app.config.js
    app.routes.js
    directives.js
    layout/
        shell.html      
        shell.controller.js
        topnav.html      
        topnav.controller.js       
    people/
        attendees.html
        attendees.controller.js  
        speakers.html
        speakers.controller.js
    sessions/
        sessions.html      
        sessions.controller.js
    services/       
        data.service.js  
        localstorage.service.js
        logger.service.js   
        spinner.service.js

There are also assets related to people (attendees and speakers), so I group those in a people drawer.

Adding More Related Features

The app I’m developing has 3 content views (attendees, speakers and sessions) and let’s assume that they are all a searchable listing of data. Let’s add some editing functionality by creating views for editing these features. Where do we put those?

/* 
* Adding more related features for searching 
* and editing.
*/

app/
    app.module.js
    app.config.js
    app.routes.js
    directives.js
    layout/
        shell.html      
        shell.controller.js
        topnav.html      
        topnav.controller.js       
    people/
        attendees.html
        attendees.controller.js  
        speakers.html
        speakers.controller.js
        speaker-detail.html
        speaker-detail.controller.js
    sessions/
        sessions.html      
        sessions.controller.js
        session-detail.html
        session-detail.controller.js  
    services/       
        data.service.js  
        localstorage.service.js
        logger.service.js   
        spinner.service.js

I created a drawer for sessions, moved the sessions view and controller in there, then created the new session-detail view and controller. This puts all the session related features in one place. I also added an speaker detail view and controller to the people drawer.

Note that I use the dash character - to separate words instead of camel casing, Pascal casing, or other naming conventions. Pick your favorite, this is just my style. I like dashes - to separate words, and dots . to separate major categories of features.

If you feel that the people drawer is getting a little crowded, then consider creating an attendee drawer and a speaker drawer, to split these up. It’s arguable either way and we pass our LIFT check, so just pick your favorite flavor.

The Folder Per Type Alternative

Let’s pause for a moment and consider some other options and where they would have lead us. For example, one common convention is to have a folder per type.

/* 
* Folders per type
*/

app/
    app.module.js
    app.config.js
    app.routes.js
    directives.js
    controllers/
        attendees.js            // controller
        session-detail.js       // controller
        sessions.js             // controller
        shell.js                // controller
        speakers.js             // controller
        speaker-detail.js       // controller
        topnav.js               // controller
    views/
        attendees.html          // view
        session-detail.html     // view
        sessions.html           // view
        shell.html              // view
        speakers.html           // view
        speaker-detail.html     // view
        topnav.html             // view 
    services/       
        dataservice.js  
        localstorage.js
        logger.js   
        spinner.js

For small apps (and this is still small) it’s not horrible, but it does require me to go to multiple folders when I work on a feature. You can see how this could get unwieldy quickly as you go to 5, 10 or 25+ views and controllers. Again, my preference is to follow LIFT and it immediately is not making it easy for me to locate files.

More Guidelines

When the app grows and I have multiple modules, there are some additional conventions I try to follow. Moving from a mindset of jQuery to Angular is another interesting topic, as is some common pitfalls to avoid. I’ll follow up on how I address those in a future post.

tags: angular javascript
  • Pingback: Angular App Structuring Guidelines | JavaScript...

  • Stephen Bero

    Great write article. I’ve seen a number of people complain that they can’t use this repo or that repo for production work because it isn’t ‘production’ quality. I really like the way you structure your code base for an angular project. It’s clean and straight forward.

    • johnpapa7

      @stephenbero:disqus Thanks, that is the intention

  • Robbie Rousseau

    Thanks again for a great article. For your next post regarding multiple modules, I was wondering you could elaborate if those modules could be reused in multiple ‘apps’. I’m currently working on an angular project that contains several distinct areas, of which some we want to reuse in a different app (web vs hybrid mobile app). Thanks.

    • johnpapa7

      @robbie_rousseau:disqus Yes, the modules in some cases are used by multiple apps. In others, the intention is more of a SKU type module where its made for a specific app.

  • http://trycatchfail.com/ Matt Honeycutt

    Nice job, John! I’m glad to see I’m not the only one that prefers a more “folder-per-feature” approach to organization. :)

    I’m not sure how I feel about dropping the “controller” suffix on controllers. I get your point, but it’s one of those conventions that I think I’ve ingrained so deeply that it just feels wrong to not use it….

    • johnpapa7

      @MattTCF:disqus Thanks. Feature based has worked well for me.

      The controller naming is something I have tried and it just bugs me personally :) I know I’m against the grain on that. And as with any conventions, use at your own comfort.

      The more important parts to me are using a set of guidelines that are consistent. For me, LIFT provides that.

      Thanks for the feedback.

      • Mikael Couzic

        The reason I ALWAYS use the “Ctrl” suffix is that I use services extensively. If I can, I always put my code in a service rather than a controller. For example, I would have a controller named “SessionCtrl” and a service named “sessions”

        • johnpapa7

          For me controllers tend to be nouns (things I have views for) … and services tend to be named based on what they provide (data, logging, storage, etc). That’s why I do not end up with 2 of the same.

          I use a lot of services as well. It is a great practice to make sure your controller is tight and anything reusable is moved to a service.

          The name “sessions” on your service … what service is it providing? Do you mean that it provides the data for sessions? if so, its a data service and thats cool because it can be used to provide sessions data to many controllers as services are shared.

          If the sessions service is meant to be only for the sessions controller, then its not shared. I would recommend making the services shared resources. It may just be the name that is throwing me off though.

    • johnpapa7

      Thanks. Yep, a lot of folks feel like you do about the “controller”. So yeah, I’m just different there.

  • Željko Szep

    Great article, it’s always good to see how other developers structure their code. One question: When you do an ASP.NET MVC application, where do you put your angularjs code. Do you place it under the scripts folder? To me it just feels wrong, though I have seen most people do it this way.

    • johnpapa7

      I mentioned that I keep the client logic in a folder called app. I put the vendor scripts under the scripts folder (or sometimes a folder named js)

  • http://www.AcruxSolutions.com Achal Shah

    Nice article. This thing works nice in case of small or mid projects. But in large SPA project, I prefer module vise folders. For ex.
    01_Server ( Represents all the node.js files )
    02_Client, ( shared views: index, header, footer. Css, Js, Angular app)
    03_OrderManagement
    04_Planning etc.
    After 2 folders all others are the modules of the application.
    Each module folder then sub divided in to 3 parts. i.e. 3.1_Presentation, 3.2_Application, 3.2_Services.
    Presentation part contains the views, js controllers, filters, directives etc.
    Application part contains the routes, business logic components etc.
    Service part contains the database communications, api calls etc.
    So like DDD style of way to structure the large scale project.

    • johnpapa7

      Yep, I stated at the end that I will address multiple module apps in a future post. I think I covered a enough ground in this one :)

  • Oran Dennison

    For another perspective on this, the Angular team published their own structuring guidelines 4 days ago.
    http://blog.angularjs.org/2014/02/an-angularjs-style-guide-and-best.html

    Taking the folder-per-feature idea even farther, what are your thoughts on putting server-side web service controllers in the same folder as the corresponding client-side feature code?

    • johnpapa7

      I like keeping the server side context separate. Especially since the web apis could be used by other clients or consumers.

  • http://www.thomashenson.com/ Thomas Henson

    It’s great to see another developers structuring. I have often thought it was redundant to type Controller into each file name but never had the guts to change my process.

    • johnpapa7

      Yeah. I buck the trend here, but I feel the same.

      • Maik

        Seems clearer for me, leaving the suffix. Great idea. I will try to stick to it.

    • James Barrow

      It helps a lot with ReSharper and navigating to files as well

  • MotoWilliams

    and the most important thing, the tests?

    • johnpapa7

      Tests are debatable for me. Some like unit tests right next to the controllers and services that they test. I’m OK with that since the build process can filter out what it needs. But it also makes it harder to find the code. But then again, it makes the tests ‘in your face’ . The other option is to put them in a tests folder at the root. There are other variations too.

  • Pingback: Angular App Structuring Guidelines | Programmin...

  • http://www.pokerdiy.com Rodney

    Hi John, thanks for sharing.

    How about .css files? I am splitting up my css by view (except for shared or global styles) e.g. people.css and session.css. Would this go in the People or Session folder along with the people.html and people.js files or do you keep these in a separate styling folder?

    • mc18

      I highly recommend not placing your .css in your app folders. Writing maintainable and clear CSS is extremely important as well, and binding each .css file to individual views ties your styles tightly to each view. For instance, if you have 10 views, and each of the ten views may or may not have lists, tables, alerts or anything else; if you decide you want to change the color of your alerts, you’re now forced to go into each of the pages and change the color of the alert.

      I vote for putting styles into their old folder. You would have a `lists` module, a `tables` module, a `alert` module; anytime these need to be changed, you can change them in one place.

    • johnpapa7

      I’ve seen it many ways. I tend to put my CSS in its own folder, mostly because I see the CSS as a shared resource that many views may use. It could go in the same folder as your 3rd party CSS and assets or its own folder. No right or wrong here.

  • Pingback: Articles for 2014-feb-21 | Readings for a day

  • Pingback: Angular App Structuring Guidelines | JavaScript...

  • Pingback: Angular App Structuring Guidelines | Front End ...

  • Pingback: Roller Codester | 10 Things to read (#3)

  • Pingback: Structuring Your Application Project

  • Pingback: Angular App Structuring Guidelines | MaxDev | ...

  • engineer111

    I need to ask a question about spinner , I need to add the spinner when the user types the url of partial view directly, for example localhost:4440/#/sessions, if user types this url directly without going through the home page first , this will not activate the spinner and this will not activate routeChangeStart even, so I need your help in this issue

    • http://brandonbrown.io/ Brandon Brown

      This is because Angular does not start the router unless it’s from the root of the app. This is a change you have to make to your webserver and how it interprets URLs http://stackoverflow.com/a/19745149

  • arezkiStoreFront

    // Html

    Route registration

    $routeProvider.when(url: ‘/sessions’, {
    templateUrl: ‘sessions.html’,
    controller: ‘sessions’
    });

    // Controller Registration

    angular.module(‘app’).controller(‘sessions’, ['$scope', sessions]);

    function sessions ($scope){

    }

    My question is if I change the name of my controller from sessions to something else, for example, session. I will need to change in more than one location. Is there a way to avoid this, for example declare a variable somewhere called sessionController = ‘sessions’ and then use the variable sessionController and thus I could change it’s value once rather than three or four times.

    Thx

  • Md. Shohel Rana

    need .vsix setup file for hottowel.angular.breeze

  • Adam Buczynski

    Question, what’s your convention when a controller and service have the same name? For example, you have a Cart controller, which you would call Cart and not CartCtrl or CartController, and then a Cart service to hold the actual cart items, which you’d also call Cart. How do you distinguish them? And do you put both files in a /cart drawer? Do you give them both suffixes?

    • johnpapa7

      really up to you. i think the cart service might be more about what you do to a cart. so naming it cart service would be fine. Or you can name your controllers with a suffix, it you prefer. Some folks I work with do, it really depends on the team.

      • Adam Buczynski

        Thanks, in the end I’ve opted to keep the main module files without suffix, and apply suffixes to all other file types (e.g. controllers, services, directives), keeping all closely relevant files together in one folder. This works well, as everything is now clear and properly organised.

%d bloggers like this: