AH

Create an Angular clean architecture solution

2021 September 26th
Development
#Angular #cleanArchitecture

I am amassed how developer split and architect their projects multi repo, mono repo solutions but when ever I see clean architecture I froze and spend more time to think about it and try to analyses the structure and how to separate modules and component concerns.

In this post I’ll try to cover the basics to have a starting clean Angular solution, this article will certainly have a follow up or modification in the future, I will keep it updated.

Prerequisites

This is an Angular project so make sure to have node installed (best advice is to use nvm):

  • Node installed and ready to be used
  • Angular CLI installed npm i -g @angular/cli@latest

Scaffolding the empty solution

First we need to tell angular to create a new workspace with only structure folders and without creating the application for it

ng new clean-architecture --create-application false --strict

  • --create-application false is to tell angular to not generate
  • --strict is to strict type checking to catch error ahead of time

after initializing our workspace change directory to it cd clean-architecture

Scaffolding our application

Angular provide a very nice schematics to scaffold our application, all we need to do is to generate application schematics:

ng g application clean-architecture-app --prefix cat --style scss --routing

  • application are a schematic to generate a basic angular app definition in the projects folder
  • --prefix cat is to change the application prefix from app (the default prefix) to cat, optional (just to mention that such an option exists)
  • --style scss is flag to tell angular we want to use .scss files to style our components
  • --routing to add app-routing to our generated app definition

The Clean Architecture

Again angular is providing us with a the necessary schematics to scaffold our modules, lets start by creating our core module

ng g m core this module will be load at the start with the AppModule

ng g m features/home --route home --module app.module.ts this is our first lazy loaded feature module we named it HomeModule. we will continue on adding our feature modules in the features folder

ng g m features/back-office --route back-office --module app.module.ts this is our second lazy loaded feature module BackOfficeModule, these two feature are just for demonstration and can be anything depending on your need for sure.

ng g m shared this module ShredModule will contain mainly things that are shared across our features.

now lets adjust our dependencies and our imports.

First things first lets start by adding two route paths to our routing

const routes: Routes = [
  { path: '', redirectTo: 'home', pathMatch: 'full'},
  { path: 'home', loadChildren: () => import('./features/home/home.module').then(m => m.HomeModule) },
  { path: 'back-office', loadChildren: () => import('./features/back-office/back-office.module').then(m => m.BackOfficeModule) },
  { path: '**', redirectTo: 'home'}
];

now lets do some imports and remove redundant once:

lets start with the AppModule:

import { NgModule } from '@angular/core';

import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { CoreModule } from './core/core.module';

@NgModule({
  declarations: [AppComponent],
  imports: [
    AppRoutingModule,
    CoreModule],
  providers: [],
  bootstrap: [AppComponent],
})
export class AppModule {}

notice we almost deleted everything from there and kept only the AppRoutingModule and added the CoreModule which will contain the basic imports like the angular common and all required modules to start the app.

lets take a look at the CoreModule then:

import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { RouterModule } from '@angular/router';

@NgModule({
  declarations: [],
  imports: [BrowserModule, BrowserAnimationsModule, RouterModule],
})
export class CoreModule {}

here we imported the two main modules for BrowserModule (which also contain the CommonModule) and BrowserAnimationsModule (this is more optional but thought we will need it anyway later), the RouterModule is there because this module will also hold our app layout therefore it will hold our navigation and routing. we will add more to this module a bit later when we will add the library that we will use in our project to help us to better present our controls, views and styles.

for he shared module this will get the most changes as we build our app as most of the feature shares a lot of modules between them and all those modules will be added here lets take a look at it before it gets more bigger:

import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { RouterModule } from '@angular/router';

@NgModule({
  declarations: [],
  imports: [CommonModule, RouterModule],
  exports: [CommonModule, RouterModule],
})
export class SharedModule {}

for now it only hold the CommonModule and the RouterModule but the interesting part here is that we also added them to the exports to make them visible for the host module later.

lets review one of the feature module example the HomeModule now:

import { NgModule } from '@angular/core';
import { SharedModule } from '../../shared/shared.module';

import { HomeRoutingModule } from './home-routing.module';
import { HomeComponent } from './home.component';

@NgModule({
  declarations: [HomeComponent],
  imports: [SharedModule, HomeRoutingModule],
})
export class HomeModule {}

for now it is clean no feature specific import as it has only the HomeRoutingModule which is specific to this module that will contain the module sub-routing if ever needed, and like all the other feature module it imports the SharedModule, and for now only declares the HomeComponent.

We can say now that we have achieved our clean and straight forward architecture (thought every opinionated architecture has its drawback). now we will continue on adding some useful library and add them to our structured modules.

Adding A UI library

When adding a UI library make sure to import its module in the shared library as mainly all the application layers will benefit from it and no need to load it each time.

« Go back to the site
© 2022 Ahmed HABBACHI. All rights reserved.