Project Structure for Modern Web Applications

Ramandeep Singh
6 min readJun 19, 2018

--

Creating a modern yet maintainable web application is no mean feat. A lot of time and effort is spent on the requirements gathering, application design, coding practices, testing, continuous integration and continuous deployment/delivery etc. However, one of the really important aspects that often gets missed out is the application directory structure.

Application structure is a really important part of an application’s design. An application should be structured keeping in mind the following:

  • Loose coupling: Modules/components should be loosely coupled and compartmentalized. It should be easy to locate files and start working on any component immediately.
  • Consistency: No matter what directory structure we decide to follow, it needs to be consistent throughout the application.
  • Folders-by-feature: Folders/directories should be named and organized per feature. This makes it very easy for developers to find the components they need to work on.

Keeping the above in mind, I decided to delve further on the structure of my Angular web application. After struggling with the application structure and restructuring the project multiple times, i finally came up with the following structure. While the following is aimed at an Angular web application, similar structure can be utilized for any kind of application.

A sample Angular application structure

The structure below tries to follow the patterns and practices defined in the Angular style guide.

Top-level: Angular CLI automatically generates the following top-level directory structure:

Descriptions of the above directories is as under:

  • app: application code, further details can be found below.
  • assets: static files that need to be copied to the deployment (web server) as it is e.g. images, language translation files etc.
  • environments: environment specific files (e.g. development, test/QA. staging, production etc. These are specially useful for cloud deployments.

App directory: Application code

  • core: all singleton services and any code that needs to be shared throughout the application.
  • modules: all major application modules.
  • shared: shared components, directives, pipes etc. that needs to be re-used and referenced by the components declared in other feature modules.

More details about each of the above directories is given below.

Core module: singleton services and more

Here we can add any code that needs to be available to the entire application. The core module should be injected only once in the app module and a module import guard should be added to avoid duplicate injection. The core module can include exception handler service, logging service, HTTP and logging interceptors, application level services, utility classes etc.

Shared modules: directives, pipes, styles, UI components, unit tests etc.

This module can include shared code that can be used by other modules in the application. Any shared directives, pipes, styles, reusable UI components (dumb components), unit tests can be added here. The difference between the core module and shared module is that shared module is injected in on-demand basis i.e. only the modules that need the shared module components need to inject it.

Modules: Application broken down into sub-modules

This directory will house our main application code. We will divide our application into sub-modules or sub-domains (if we consider the Domain Driven Design model). Each module should be independent of each other and the only way to communicate among the modules should be through a well defined mechanism or protocol.

As an example consider the following modules in case of an application that monitors IoT devices across the globe:

Our application modules

A brief description of the modules:

  • authentication: responsible for user login/logout. It could be an in-house authentication provider or an external provider using OAuth 2.0 (e.g. Azure Active Directory). We should be able to reuse this module in any other Angular application that needs authentication.
  • main-menu: this houses our application wide menu.
  • monitoring: all the code to monitor our IoT devices.
  • reporting: code to show dashboard and generate reports for our IoT devices.

Folders by feature

As per the Angular style guide, folders/directories should be named and organized per feature. Keeping that in mind, we can now define our modules’ sub-directories:

main-menu module directory structure

Here we have 2 components called menu-bar and title-bar in our module. This way we can cleanly see the features available in our modules and have separate component directories for each feature.

shared directory: We need a shared directory inside some modules as it’s entirely possible that some features or components within a module will share some part of code e.g. entities, models, services, state etc. For example:

Shared libraries

Angular CLI v6 includes in-built support for creating shared libraries. So, if you have any piece of code that you feel is reusable and can be used independently, you can have it as a library. Refer https://github.com/angular/angular-cli/wiki/stories-create-library for more details on this. You can even publish your libraries to npm repository so that people can use it.

As an example, i have created a library called ngx-powerbi for my application and this library can be used by any Angular application. By default, Angular CLI creates the library in the projects directory which is created at the same level as the src directory so it’s lies parallel to the application code.

Overall project structure

Here’s the final project structure i have now:

As you can see, at the top level we have the core, modules and shared directories and each sub-module conveniently lies within it’s own sub-directory under the modules directory. This makes it very easy to find stuff to work on quickly. If someone wants to work on the main-menu module, he/she can navigate directly to that directory. Within the main-menu module, they can then navigate to the feature directory (remember the folders by feature methodology they we followed above?) that they want to work on.

Module communication and state management

As mentioned above, we need to keep the modules loosely coupled and define a clear protocol for communication among the modules. There are multiple ways to achieve this. For our application, we are using @ngrx/store to do state management of the application and create a communication pipeline among the modules. With proper state management, modules don’t need to know about each other and can use the state management store for sharing data and events with each other.

For this, we create a state-management sub-directory in our shared directory as shown above and then we can use any state management solution we like (e.g. @ngrx/store, NGXS etc.). You can search the internet for more information on state-management in Angular.

Summary

As we saw, application directory structure is a very important aspect of designing a maintainable and reusable software application. It is extremely important to agree on a structure at an early stage of the project otherwise the time spent on refactoring the code will be monumental. In the end, whatever application structure we choose, it should be consistent throughout the application.

Feel free to provide any feedback on this article through the comments section. Thanks!

--

--