Code Organization in Large AngularJS and JavaScript Applications — Cliff Meyers

17
Menu CLIFF MEYERS 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 ocial starting point for AngularJS apps. The "app" directory contains the following structure: css/ img/ js/ app.js controllers.js directives.js lters.js services.js lib/ partials/ The JavaScript directory has one le for every type of object we write. This is much like organizing your clothes into dierent piles on the oor. You have a pile of socks, underwear, shirts, pants, etc.

description

Strategies for code organization for angular

Transcript of Code Organization in Large AngularJS and JavaScript Applications — Cliff Meyers

  • Menu

    CLIFF MEYERS

    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.

    http://cliffmeyers.com/http://cliffmeyers.com/blog/2013/4/21/code-organization-angularjs-javascripthttps://github.com/angular/angular-seed

  • 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

  • view raw

    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:

    UserModule.js hosted with by GitHub

    123456789

    10111213

    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]);

    12

    var userModule = angular.module('userModule',[]);

    https://gist.github.com/cliffmeyers/3d1db5373313938c3b87/raw/UserModule.jshttps://gist.github.com/cliffmeyers/3d1db5373313938c3b87#file-usermodule-jshttps://github.com/

  • 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.

    3456789

    10111213

    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]);

    https://gist.github.com/cliffmeyers/3d1db5373313938c3b87/raw/UserModule.jshttps://gist.github.com/cliffmeyers/3d1db5373313938c3b87#file-usermodule-jshttps://github.com/

  • Newer Older

    Comments (34) Subscribe via e-mail

    Great post, I wonder if you could add some more gists to show how you define your controllers or

    services. Just for all of us that dont know yet all the possible ways to declare a service/controller

    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.

    ! "

    100 Likes

    $ Share

    $ Share

    Oldest First

    Preview Post Comment

    raul Arabaolaza A year ago

    http://cliffmeyers.com/blog/2013/8/27/mocking-server-dependencies-in-javascript-and-angularjs-applicationshttp://cliffmeyers.com/blog/2013/3/11/integration-angularjs

  • and add it to a module

    I think angular-app is a good example of the style: https://github.com/angular-app/angular-app

    Take a look at ng-boilerplate http://joshdmiller.github.io/ng-boilerplate/#/home by Josh David Miller

    a active member of Angular community.

    Completely agree! I've been exploring this kind of organization as well for my Backbone app (I use

    Browserify). I also have a common or core folder with things shared by all modules.

    One of the challenges I still haven't quite solved is the "assets" bit. I'm trying to keep them separated

    in each module (vs a global "assets" folder). I have quite a bit of shared styles in the common folder,

    notably Sass or LESS variables to define colors, type, etc. That means the styles in each module will

    have to remain in Sass or LESS until the whole application is built.

    I'm also exploring making it really easy to work on one piece (module) at a time, and to do so, being

    able to boot the app with only "common" and that particular module (let's say "cart"), and maybe

    "user" (for me it's part of "common"). Right now I'm doing that at the "debug" build step, where I

    have a custom Grunt.js task that allows me to specify a particular module to only build the app with

    that module and it's "common/core" dependencies. If I don't, it builds the whole app. Have you

    considered that? I guess I'm also trying to make it so I could, if I wanted, put a module in its own

    NPM or Bower package.

    Thanks for the post!

    Cheers,

    Nicolas

    Phaedryx A year ago

    hcentelles A year ago

    Nicolas Hery A year ago

  • You've got "return new UserModel" in your userModel factory. I'm curious to see where UserModel

    is defined, and what it looks like.

    I usually keep my logic in my controllers, which is probably not the best way of doing it, and would

    like to mimic what you've got going. Would be nice to get insight into what's behind that UserModel

    object.

    I actually use a combination of Sock Drawer and Modularity. I keep files seprated by type but name

    the files by modules.

    root

    --imgs

    ----idv.TOC

    -------image1.png

    -------image2.png

    --css

    ----idv.TOC.css

    ----SiteFeaturesAddressSearch.css

    ----SiteFeaturesCustomSearches.css

    --js

    ----idv.TOC.js

    ----SiteFeaturesAddressSearch.js

    ----SiteFeaturesCustomSearches.js

    This works really well for me because when in debug mode each file for css and js are loaded

    separately. Also I can make sure css are loaded at the top of page and js files are at the bottom.

    When the site is set to release mode my minification and merging code can find the files easily and

    stream them as only 2 files.

    Aaron Smith A year ago

    Donny V A year ago

  • Thanks for this post. I have been working with backbone for a year or so now but am seriously

    thinking it might be time to make the leap to Angular (or even Meteor) but was a little put off by the

    suggested structure from the angular-seed project.

    the current structure I have migrated to for my backbone/marionette projects (w/require.js) is as

    follows:

    /mod1/

    - mod1.controller.js ( similar to the manifest file above)

    - mod1.models.js

    - mod1.views.js

    - mod1.templates.html

    /mod2/

    - mod2.controller.js

    - mod2.models.js

    - mod2.views.js

    - mod2.templates.html

    with a global 'EventBus' used to facilitate communication between models and the application

    'controller'.

    So far it's proving to be a pretty solid structure especially in combination with requirejs but it still

    doesn't make up for some of the core problems with backbone in general, hence the consideration

    of Angular as a next step.

    so thanks again for posting about an alternative organization structure that makes a little more

    sense to me.

    A question I have though is around pub/sub in Angular. I see you mention it in point #4

    (emit/broadcast/on) above but I haven't seen it in any examples or tutorials yet. are there any good

    examples out there you can point to?

    Sean

    Sean Brookes A year ago

  • Spreading related things into multiple directories isn't really modularity IMO, pros and cons of each

    approach I suppose but I definitely found these rails-style approches really annoying in the past,

    certainly not what I would call modular.

    Nice article. I've always tried to structure code after the domain instead of the techniques used.

    What the code does is always more important than HOW it does it.

    @raul @aaron @sean I'll have a follow-up post soon which will show the tiered architecture we're

    using on a large enterprise app plus a few other enhancements I plan to use on my next app.

    @nicolas The bootstrapping of a single module is a great way to accelerate development. We

    actually did some of that back in the day with Flex applications. If you have nice clean modules and

    easily mockable/pluggable dependencies then that bootstrapping process becomes a lot easier. We

    aren't currently using this technique on my current project but I'd like to get there. Thanks for

    sharing!

    I would prefer to have folder for directives and filters as well, with each directive/filters in their own

    file.

    TJ Holowaychuk A year ago

    Thomas Haukland A year ago 1 like

    Cliff Meyers A year ago 1 like

    Cliff Meyers A year ago

    Suman Paul A year ago

  • great tips to organize web apps better, thanks for sharing

    I agree too, just seems logical to group this way. Just made sure that my own little boilerplate for

    Angular starts off like this https://github.com/mattstyles/yeoman-angular-express-plus

    This is really nice, and I am interested creating a RequireJS example of what you have done, if

    nobody else has already done so.

    Nice article. I'm all for the second organization by function.

    I feel strongly that the rails-style controllers/views/models separation is a mistake. It took the Rails

    Community 5 - 7 years before they stopped writing poorly structured applications due to the implicit

    lack of modularity and abstraction that encouraged. It was funny watching Rails go from the skinny-

    model-era to the skinny-controller-era and now to the skinny-model and skinny-controller, lets use

    services and design patterns era.

    IMHO the concept of a component or package MUST be a top-level abstraction in any sane software

    structuring approach. And a directory fills this definition just fine.

    I also feel that AngularJS apps should be written to enable a "zip and ship" philosophy. i.e. you can

    take your app/ directory, zip its contents, and unzip into /var/www on an Apache, nginx, or Node.js

    server and be off and running.

    If we follow that strategy we can hopefully hasten the death of PHP.

    JavaScriptBank A year ago

    Matt Styles A year ago

    Joshua Gough A year ago

    Nick Van Weerdenburg A year ago

    http://www.javascriptbank.com/

  • @nick how does frontend dev related to PHP? :/

    @chris Thanks for the article, I'm always struggling to decide on the "perfect" folder structure, your

    article helped clear up some things I didn't think about.

    Could you explain what should be in service, model and controller? thank in adv

    btw, great post

    The fonts and colors on this page make it very hard to read!

    Interesting. We take a slightly different approach, in our boilerplate http://brandid.github.io/parse-

    angular-demo/

    We don't want to load all the controller files on first load. Could you please throw some light on lazt

    loading of controller files only when required. ?

    Curious if you've ever seen, or recommend, adding filters to your individual modular objects. I

    definitely can see using a common filters.js, but should you put all of your filters in there, or should

    you move your filters specifically for the users view to UserFilters.js?

    Jason Yang A year ago

    Chok Wee Ching A year ago

    Nick Perkins A year ago

    Arush A year ago

    praveen vedanth A year ago

    fyjs A year ago

    http://fuckyeahjavascript.tumblr.com/

  • I absolutely loved this post. I've set up a structure like this, and someone pointed me to this article

    showing me why I did it. Thank you.

    I do, however, have a single question for you.

    At the end of "Modularity" you said: "This would also be a reasonable place to add some loader

    directives for RequireJS or Browserify."

    Would you mind explaining this a bit more? What's a "loader directive"? Because I am, in fact, using

    browserify.

    hi

    my structure is below :

    for example user section

    .

    .

    ---user Folder

    ------ Controller Folder

    ----------- LoginController.js

    ----------- RegistrationController.js

    ------ Model Folder

    ----------- UserModel.js

    ------ Service Folder

    ----------- UserService.js

    .

    .

    Do you accept this structure?

    Wesley Overdijk A year ago

    Naser Tahery A year ago

  • Great example! Coming from PHP development, it looks to me more or less like Symfony2 Bundle

    structure idea.

    Cheers

    I was happy to stumble upon your article and see that you organized your files in almost the same

    way I did. The one exception is that I consider models to be common. For example you show a

    folder for Cart, Product and User, each having their own models, but Cart would use its own model,

    plus the Product model and the User model, which in my mind makes them common.

    Food for thought...

    Nice post! I'm new to angularJS and It would be nice if you provide a project with this strucure. I'm

    very confused on how to "connect" separeted controllers in my app. Thanks!

    Hey Cliff,

    I had a look at your repo on github and find you need to declare extra namespaces to store class

    definition so later you can instantiate those inside manifest.

    Do you think in the end, manifest file actually cause more trouble than benefit?

    max A year ago

    Darryl A year ago

    Joao Grassi A year ago

    Jon A year ago

    http://www.labanalytix.com/

  • Thanks

    Great post. I think your clothing analogy works well and flows with the topic.

    Have been doing this for a few years. The same idea but with some .NET specifics:

    http://www.ndepend.com/Res/NDependWhiteBook_Namespace.pdf

    Great post. Thanks

    Really good post for a newbie like me. Its a good kickstart point for my new app

    Wonderful article to have a good idea about best approach of working with ng.

    Thanks a lot.

    Nazmul Hasan

    anony 7 months ago

    Cody 6 months ago

    Roman Boiko 3 months ago

    Kaushik 3 months ago

    Shinu Suresh 2 months ago

    Nazmul Hasan Sarkar 2 months ago

    http://www.codyromano.com/

  • Twitter

    Linkedin

    Google

    Facebook

    Github

    I'm a Solutions Architect for Universal Mind, a digital solutions agency specializing in applications that deliver a

    unified multiscreen customer experience.

    AngularJS AOP Books Eclipse Flash Flex Git Hibernate HTML5Java JavaScript jQuery Localization Mac MustacheOOP Sencha Touch Spring Subversion Testing Tools

    On The Twitter

    RT @StoneBrewingCo: BIG THANKS to all who vied for #StoneEast. We now know our East Coast brewery site.

    CHECK IT http://t.co/lFYrVY2eTa #beer #craftbeer #RVA

    2 months ago

    iOS 8s predictive QuickType keyboard found to suggest parts of your passwords http://t.co/HxTQvul90p

    2 months ago

    RT @trochette: Friday was my last day @universalmind. 2 years working on amazing projects, traveling and

    learning. I'll miss you guys!!

    2 months ago

    Follow Follow @cliffmeyers@cliffmeyers

    https://twitter.com/cliffmeyershttp://www.linkedin.com/in/cliffmeyershttps://plus.google.com/116522540335417503947http://www.facebook.com/cliffmeyershttps://github.com/cliffmeyershttp://www.universalmind.com/http://cliffmeyers.com/blog?category=AngularJShttp://cliffmeyers.com/blog?category=AOPhttp://cliffmeyers.com/blog?category=Bookshttp://cliffmeyers.com/blog?category=Eclipsehttp://cliffmeyers.com/blog?category=Flashhttp://cliffmeyers.com/blog?category=Flexhttp://cliffmeyers.com/blog?category=Githttp://cliffmeyers.com/blog?category=Hibernatehttp://cliffmeyers.com/blog?category=HTML5http://cliffmeyers.com/blog?category=Javahttp://cliffmeyers.com/blog?category=JavaScripthttp://cliffmeyers.com/blog?category=jQueryhttp://cliffmeyers.com/blog?category=Localizationhttp://cliffmeyers.com/blog?category=Machttp://cliffmeyers.com/blog?category=Mustachehttp://cliffmeyers.com/blog?category=OOPhttp://cliffmeyers.com/blog?category=Sencha+Touchhttp://cliffmeyers.com/blog?category=Springhttp://cliffmeyers.com/blog?category=Subversionhttp://cliffmeyers.com/blog?category=Testinghttp://cliffmeyers.com/blog?category=Toolshttps://twitter.com/intent/follow?original_referer=http%3A%2F%2Fcliffmeyers.com%2Fblog%2F2013%2F4%2F21%2Fcode-organization-angularjs-javascript&region=follow_link&screen_name=cliffmeyers&tw_p=followbuttonhttps://twitter.com/StoneBrewingCohttps://twitter.com/#!/search?q=%23StoneEasthttp://t.co/lFYrVY2eTahttps://twitter.com/#!/search?q=%23beerhttps://twitter.com/#!/search?q=%23craftbeerhttps://twitter.com/#!/search?q=%23RVAhttp://twitter.com/cliffmeyers/status/520398091686772736http://t.co/HxTQvul90phttp://twitter.com/cliffmeyers/status/516810495178059776https://twitter.com/trochettehttps://twitter.com/universalmindhttp://twitter.com/cliffmeyers/status/516015968166760448