Monday, January 4, 2016

Best practices for organizing folder structures for large and scalable angular js applications


Conclusion:
Organise based on the functional areas. Eg. for e-commerce shopping app its better to use following structure

  • cart/
    • CartModel.js
    • CartService.js
  • common/
    • directives.js
    • filters.js
  • product/
    • search/
      • SearchResultsController.js
      • SearchResultsModel.js
    • ProductDetailController.js
    • ProductModel.js
    • ProductService.js
  • user/
    • LoginController.js
    • RegistrationController.js
    • UserModel.js
    • UserService.js

For more info read following two articles from John papa and cliff maeyer

Code Organization in Large AngularJS and JavaScript Applications

Many developers struggle with how to organize an application's code base once it grows in size. I've seen this recently in AngularJS and JavaScript applications but historically it's been a problem across all technologies including many Java and Flex apps I've worked on in the past.
The general trend is an obsession with organizing things by type. It bears a striking resemblance to the way people organize their clothing.

Piles on the Floor

Let's take a look at angular-seed, the official starting point for AngularJS apps. The "app" directory contains the following structure:
  • css/
  • img/
  • js/
    • app.js
    • controllers.js
    • directives.js
    • filters.js
    • services.js
  • lib/
  • partials/
The JavaScript directory has one file for every type of object we write. This is much like organizing your clothes into different piles on the floor. You have a pile of socks, underwear, shirts, pants, etc. You know your black wool socks are in that pile in the corner but it's going to take a while to dig them out.
This is a mess. People shouldn't live like this and developers shouldn't code like this. Once you get beyond a half-dozen or so controllers or services these files become unwieldy: objects you're looking for are hard to find, file changesets in source control become opaque, etc.

The Sock Drawer

The next logical pass at organizing JavaScript involves creating a directory for some of the archetypes and splitting objects into their own files. To continue the clothing metaphor, we've now invested in a nice mohaghony dresser and plan to put socks in one drawer, underwear in another, and neatly fold our pants and shirts in still others.
Let's imagine we're building a simple e-commerce site with a login flow, product catalog and shopping cart UI's. We've also defined new archetypes for Models (business logic and state) and Services (proxies to HTTP/JSON endpoints) rather than lumping them into Angular's single "service" archetype. Our JavaScript directory can now look like this:
  • controllers/
    • LoginController.js
    • RegistrationController.js
    • ProductDetailController.js
    • SearchResultsController.js
  • directives.js
  • filters.js
  • models/
    • CartModel.js
    • ProductModel.js
    • SearchResultsModel.js
    • UserModel.js
  • services/
    • CartService.js
    • UserService.js
    • ProductService.js
Nice! Objects can now be located easily by browsing the file tree or using IDE shortcuts, changesets in source control now clearly indicate what was modified, etc. This is a major improvement but still suffers from some limitations.
Imagine you're at the office and realize you need a few outfits dry-cleaned for a business trip tomorrow morning. You call home and ask your significant other to take your black charcoal and blue pinstripe suits to the cleaners. And don't forget the grey shirt with the black paisley tie and the white shirt with the solid yellow tie. Imagine that your significant other is completely unfamiliar with the your dresser and wardrobe. As they sift through your tie drawer they see three yellow ties. Which one to pick?
Wouldn't it be nice if your clothing was organized by outfit? While there are practical constraints like cost and space that make this difficult with clothing in the real world, something similar can be done with code at zero cost.

Modularity

Hopefully the trite metaphors haven't been too tedious but here's the recap:
  • Your significant other is the new developer on the team who's been asked to fix a bug on one of the many screens in your app.
  • The developer sifts through the directory structure and sees all the controllers, models and services neatly organized. Unfortunately it tells him/her nothing about which objects are related or have dependencies on one another.
  • If at some point the developer wants to reuse some of the code, they need to collect files from a bunch of different folders and will invariably forget code from another folder somewhere else.
Believe it or not, you rarely have a need to reuse all of the controllers from the e-commerce app in the new reporting app you're building. You may however have a need to reuse some of the authentication logic. Wouldn't it be nice if that was all in one place? Let's reorganize the app based on functional areas:
  • cart/
    • CartModel.js
    • CartService.js
  • common/
    • directives.js
    • filters.js
  • product/
    • search/
      • SearchResultsController.js
      • SearchResultsModel.js
    • ProductDetailController.js
    • ProductModel.js
    • ProductService.js
  • user/
    • LoginController.js
    • RegistrationController.js
    • UserModel.js
    • UserService.js
Any random developer can now open the top-level folder and immediately gain insight into what the application does. Objects in the same folder have a relationship and some will have dependencies on others. Understanding how the login and registration process work is as easy as browsing the files in that folder. Primitive reuse via copy/paste can at least be accomplished by copying the folder into another project.
With AngularJS we can take this a step further and create a module of this related code:
var userModule = angular.module('userModule',[]);
userModule.factory('userService', ['$http', function($http) {
return new UserService($http);
}]);
userModule.factory('userModel', ['userService', function(userService) {
return new UserModel(userService);
}]);
userModule.controller('loginController', ['$scope', 'userModel', LoginController]);
userModule.controller('registrationController', ['$scope', 'userModel', RegistrationController]);
view rawUserModule.js hosted with ❤ by GitHub
If we then place UserModule.js into the user folder it becomes a "manifest" of the objects used in that module. This would also be a reasonable place to add some loader directives for RequireJS or Browserify.

Tips for Common Code

Every application has common code that is used by many modules. We just need a place for it which can be a folder named "common" or "shared" or whatever you like. In really big applications there tends to be a lot of overlap of functionality and cross-cutting concerns. This can be made manageable through a few techniques:
  1. If your module's objects require direct access to several "common" objects, write one or more Facades for them. This can help reduce the number of collaborators for each object since having too many collaborators is typically a code smell.
  2. If your "common" module becomes large subdivide it into submodules that address a particular functional area or concern. Ensure your application modules use only the "common" modules they need. This is a variant of the "Interface segregation principle" from SOLID.
  3. Add utility methods onto $rootScope so they can be used by child scopes. This can help prevent having to wire the same dependency (such as "PermissionsModel") into every controller in the application. Note that this should be done sparingly to avoid cluttering up the global scope and making dependencies non-obvious.
  4. Use events to decouple two components that don't require an explicit reference to one another. AngularJS makes this possible via the $emit, $broadcast and $on methods on the Scope object. A controller can fire an event to perform some action and then receive a notification that the action completed.

Quick Note on Assets and Tests

I think there's more room for flexibility with respect to organizing HTML, CSS and images. Placing them in an "assets" subfolder of the module probably strikes the best balance between encapsulating the module's asset dependencies and not cluttering things up too much. However I think a separate top-level folder for this content which contains a folder structure that mirrors the app's package structure is reasonable too. I think it works well for tests as well.

Ref: http://www.johnpapa.net/angular-growth-structure/
Your Angular app is growing and you want your structure to adapt with it. Let's explore one way you can approach this.
As your app grows it becomes even more important to structure it in a way that makes it easy to manage and maintain it. I recently posted a common technique I use when I create a new app to help structure it. A simple structure by type is easy out of the gate, as the app generally starts with a single set of features. This usually starts as the first module you write. Now fast forward and imagine you are working on an app that has a handful of modules, with dozens of views and controllers. How do you organize your code now?

Options for Managing Growth

I like the type organization (folders for controllers and views, for example), but as an app grows, instead of putting that at the root and putting all views and controllers in their own folders, sometimes it makes more sense to organize them 1 level deeper into feature areas. For a similar approach, see Cliff Meyer's great post. We tend to look for the one right answer, but often there are many good options. You have to choose what feels right for you.
So how might this look? There are a lot of ways we could do this, but let's focus on two options side by side. NgStructureCompare

Sort By Type

On the left we have the app organized by type. Not too bad for smaller apps, but even here you can start to see it gets more difficult to find what you are looking for. When I want to find a specific view and its controller, they are in different folders. It can be good to start here if you are not sure how else to organize the code as it is quite easy to shift to the technique on the right: structure by feature.

Sort By Feature

On the left the project is organized by feature. All of the layout views and controllers go in the layout folder, the admin content goes in theadmin folder, and the services that are used by all of the areas go in the services folder. The idea here is that when you are looking for the code that makes a feature work, it is located in one place. Services are a bit different as they "service" many features. I like this once my app starts to take shape as it becomes a lot easier to manage for me.

Structuring for Modules

This structure by feature can be extended fairly easily once you start to gather multiple modules. When i see common functionality that can be extracted and re-used, I like to break that out into its own module. In the structure by feature notice that I have a common folder. In there I have another module named common that contains logging, progress bars, and other common features. Sometimes I break this out such that the modules are the first folder under the app folder. But in this case I just had 2 modules: common and the main app.
As the app grows even further, you can have many modules. That's when I like to have the modules in their own folders under app. Specifically when the main app has very separate feature areas like point of sale and inventory, for example. Those could be their own modules. Then I'd also have some other common modules. See where this is heading? Separate silos of features that are easily separated (think Separation of Concerns).

Your Choice

Ultimately how you organize your code is entirely up to you and your team. Don't feel married to your choice, you can adapt when it makes sense. The key is in keeping good separation and making it easy to find your code.
If you are interested in SPA, HTML5, Angular, BreezeJS or JavaScript patterns then you will love my upcoming course at Pluralsight, due out in October 2013. Or if you prefer Knockout and Durandal check out my courses on Pluralsight today.

No comments:

Post a Comment