📜 ⬆️ ⬇️

Code organization in large AngularJS and JavaScript applications

From the translator: I think that articles on the architecture of the application and organization of the code are most important at the initial stage, since, unlike everything else, it is very difficult to change the basis of the application. [ Original article ]

Many developers are struggling to organize the application code base as soon as it grows in size. Recently, it has been observed in both the angular and the javascript applications, but historically this problem is inherent in any technology, including Java and many flex applications with which it has worked in the past.

The general trend is obsession with organizing entities by type. It is strikingly similar to how people sort their clothes.
')

Piles on the floor


Let's take a look at the Angulyar template project, the official starting point for creating your application. The app directory has the following structure:


The javascript directory contains one file for each type of object being described. Very similar to the organization of clothing in piles on the floor. You have heaps of socks, underwear, shirts, pants, etc. You know that your black woolen socks are in the corner of the pile, but it takes time to dig them out.

This is a mess. People should not live as well as developers should not support such code. As soon as the number of your controllers and services exceeds a dozen, the file structure becomes too cumbersome: objects will become harder to find, the file of changes in the control source codes becomes opaque, etc.

Sock drawer


The next logical passage in the Javascript organization involves creating a directory for some entities and splitting objects into separate files. Continuing the clothing metaphor, take a mahogany chest of drawers and put socks in one drawer, underwear in another, and gently spread the pants and shirts on the rest.

Let's imagine that we are doing a simple e-commerce site with a login form, a product catalog and a cart interface. We also defined new entities for models (business logic and state) and services (proxies for HTTP / JSON terminals), rather than dumping them into one heap of “services”. Our javascript directory will look like this:


Fine! Objects are now laid out so that it is easy to browse the tree of files or navigate through it using hot keys; in the list of changes it is now easy to indicate which changes have been made, etc. This is a major improvement, but there are still some limitations.

Imagine that you are in the office, and understand that you need to take a few outfits that you will need for a business trip tomorrow morning to dry cleaning. You call home and ask your soul mate to take the black and blue striped suit to dry cleaning. And do not forget, a gray shirt with a tie with a black ornament and a white shirt with a dull yellow tie. Imagine that your other half is completely unfamiliar with what is happening in your dresser and closet. Opening the box with ties, she sees three yellow ties. Which one to choose?

Wouldn't it be bad if your clothes were immediately assembled into an outfit? Although in practice there are restrictions such as cost and space occupied, which make it difficult to do this with clothes in the real world, but something similar can be done with the code absolutely free.

Modularity


Let's hope that commonplace metaphors weren't too tedious, and here’s a summary:


Believe it or not, it rarely becomes necessary to reuse all controllers from an e-commerce application in a new reporting application that you do. However, it may be necessary to reuse part of the authentication logic. Wouldn't it be nice if it all lay in one place? Let's reorganize the application based on functional areas:


Now, any third-party developer can open top-level folders and immediately understand what the application does. Objects in one folder relate to each other and some of them depend on others. Understanding how the authorization and registration process works is as easy as viewing the files in this folder. A primitive reuse procedure using copy / paste can be done at least by copying a folder to another project.

In Angulyar we can take this step as a basis and create a module with the appropriate 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]); 

If you then put UserModule.js in a user folder, it becomes an “explicit” object used in this module. It would also be wise to add some directives for RequireJS or Browserify.

General Code Tips


Each application has a common code used in many modules. You just need a place for it, which can be a folder with the name “common” or “shared” or whatever. In really large applications, there is a tendency to overlapping and intersecting functionality. There are several methods to manage all of this:

  1. If the objects in your module require direct access to several “common” objects, create one or more façades for them. This will help reduce the number of freeloaders for each object, since the presence of too many freeloaders, as a rule, shows that the code is rotten.
  2. If your “shared” folder becomes a large module, divide it into sub-modules for solving specific functional tasks or problems. Make sure that the application modules use only those “common” modules they need. This is the " Interface Separation Principle " from SOLID.
  3. Add utility methods in $ rootScope so that they can be used in child areas. This will relieve the implementation of the same dependency (for example, "PermissionsModel") in each controller in the application. Note that this needs to be done thoughtfully in order to avoid cluttering up the global area and not make the dependencies not obvious.
  4. Use events to separate components that do not require each other’s explicit reference. In Angulyar, this is done using the $ emit , $ broadcast and $ on methods in the object scope. The controller can generate an event to perform certain actions, and then receive a notification that the action has been completed.

A quick note on resources and tests


I think that you can more flexibly approach the organization of HTML, CSS and images. Placing them in the “assets” folder in the module folder, which is likely to improve the balance between the resources and the modules dependent on them and does not make the structure too cumbersome. Also, I think it would be wise to divide top-level folders into folders for content containing a folder structure reflecting the structure of the application package. I think this is just as good for testing.

Source: https://habr.com/ru/post/180837/


All Articles