Using Angular 2+ in a Microservice environment

Summary

A microservices back end doesn't require a microservices front end. You probably want to organise your angular application by module unless you have an exceptionally large application that would be better as multiple smaller independent applications. In which case, Angular may not be the best framework for you.


You shouldn't choose to architect the frond end application in a certain way just because the server side application is architected like that. It's perfectly feasible to use microservices on the back end with a single Angular application on the front end. But if you want to divide it up and it makes sense to do so, you should first look at using multiple modules.

You are already using modules (these are added to the imports of your app.module file) such as HttpClientModule so it's not a big step to create your own. A module would contain components relating to a part of the application such as a particular feature or a set of UI components.

Modules don't immediately provide any technical benefits but from a developer perspective they help organise large codebases. You can however opt to lazy-load these to speed up the initial load time for the application, and to pre-load the lazy loaded modules so the application will load the other modules after loading the initial page. Initially, you would probably want to keep all the modules in a single codebase which makes managing the code easier. As the application grows you can split the modules into separate repositories if you ever get to the point where that seems like a good idea. They can then be installed with your package manager (NPM) and imported as you would import other third party modules, but you still have one app.

Using this approach you still have the benefits of a Single Page Application but it's more modular. At the extreme end, you could split it out into completely different applications served from different server routes. Which option to choose depends on your requirements but the following may help.

Single Module

  • One application, one codebase, one module.
  • Use this option for small simple applications with limited features.

Multiple Modules

  • One application but multiple modules.
  • Code is more organised making it more developer friendly
  • Modules can be independently loaded decreasing the initial load time.
  • Use this option for larger applications or where there are at least two distinct sections to the app.

Multiple Applications

  • Multiple applications, each could be built with one or more modules.
  • Modules can be shared between applications as external dependencies.
  • Use this if parts of the application are completely separate.
  • You will need to incorporate aspects of traditional multi-page applications here and use the server for (some of) the routing. You will have additional non-angular complexities; how will one app share data with another?
  • If you don't know if you need this, you probably don't.

99% of Angular projects should be single applications organised as multiple modules. Given that your question is Angular specific, I've created a simple app with routing, and then split it out over multiple lazy loaded modules so you can compare.

Here's the standard single module app.

Here's a fork of it with exactly the same functionality but split into modules.

I've used feature modules, plus a shared module.

Feature Modules

All the components, directives etc. relating to a feature are contained in the feature module. You can see that the AppModule is much leaner in the multi-module version.

Each module should have its own router (if the feature has routes). All reference to the lazy loaded modules should be removed from AppModule in order to prevent Webpack from including them in the main bundle which would cause them to be loaded immediately. Then, a string representation of the path to the modules can be used to point to the module:

const appRoutes: Routes = [
  { path: '', component: HomeComponent },
  { path: 'places', loadChildren: './places/places.module#PlacesModule' },
  { path: 'products', loadChildren: './products/products.module#ProductsModule' }
]

There are different preloading strategies to determine how lazy loaded modules are loaded. PreloadAllModules will load all lazy loaded modules after loading the initial module.

imports: [
  RouterModule.forRoot(appRoutes, { preloadingStrategy: PreloadAllModules })
],

Shared Modules

Shared modules are used to make components directives etc. available to multiple modules. They need to export any of the declarations that will be used by other modules, as well as the CommonModule:

exports: [
  CommonModule,
  LegalComponent
]

Services

Services can also be provided into individual modules. When provided to lazy loaded modules or shared modules used in lazy loaded modules, you will get a different instance of the service. So to keep the example simple, I've left the services in the AppModule providers array.