Enterprise Angular
Enterprise Angular
Manfred Steyer
This book is for sale at http://leanpub.com/enterprise-angular
This is a Leanpub book. Leanpub empowers authors and publishers with the Lean Publishing
process. Lean Publishing is the act of publishing an in-progress ebook using lightweight tools and
many iterations to get reader feedback, pivot until you have the right book and build traction once
you do.
Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1
Case Studies . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1
Help to Improve this Book! . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1
Thanks . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2
Literature . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42
Case Studies
To show all the concepts in action, this book uses several case studies. You can find them in my
GitHub account:
Thanks
I want to thank several people who helped me with this work:
• The great people at Nrwl.io⁵ who provide the open source tool Nx⁶ which is also used in the
case studies here and described in the following chapters.
• Thomas Burleson⁷ who did a great job on describing the idea of facades. The chapter about
Tactical Design uses it. Also, Thomas contributed to this chapter.
⁵https://nrwl.io/
⁶https://nx.dev/angular
⁷https://twitter.com/thomasburleson?lang=de
Strategic Domain Driven Design
Monorepos allow huge enterprise applications to be subdivided into small and maintainable libraries.
This is, however, only one side of the coin: First, we need to define criteria for slicing our application
into individual parts. Also, we must establish rules for the communication between them.
In this chapter, I present a methodology I’m using for subdividing big software systems into
individual parts: It’s called Strategic Design and it’s part of the domain driven design⁸ (DDD)
approach. Also, I show how to implement its ideas with an Nx⁹-based monorepo.
To recognize domains, it is worth taking a look at the processes in the system. For example, an e-
Procurement system that handles the procurement of office supplies could support the following
two processes:
It is noticeable that the process steps Approve Order, Request Budget and Approve Budget primarily
revolve around organizational hierarchies and the available budget. In addition, managers are
primarily involved here. By contrast, the process step is primarily about employees and products.
Of course, it can be argued that products are omnipresent in an e-Procurement system. However,
a closer look reveals that the word product denotes different things in some of the process steps
shown here. For example, while a product is very detailed when it is selected in the catalog, the
approval process only needs to remember a few key data:
Strategic Domain Driven Design 5
In the sense of the ubiquitous language that prevails within each domain, a distinction must be
made between these two forms of a product. This leads to the creation of different models that are
as concrete as possible and therefore meaningful.
At the same time, this approach prevents the creation of a single model that attempts to describe
the entire world. Such models are often confusing and ambiguous. In addition, they have too many
interdependencies that make decoupling and subdividing impossible.
At a logical level, the individual views of the product may still be related. If this is expressed by the
same id on both sides, this works without technical dependencies.
Thus, each model is valid only within a certain scope. DDD also calls this the bounded context¹².
Ideally, each domain has its own bound context. As the next section shows, however, this goal can
not always be achieved when integrating third-party systems.
If we proceed with this kind of analysis we might come up with the following domains:
¹²https://martinfowler.com/bliki/BoundedContext.html
Strategic Domain Driven Design 6
If you like the shown process oriented approach of identifying different domains alongside the
vocabulary (entities) and groups of domain experts, you might love Event Storming¹³. This is a
workshop format where several domain experts analyze business domains together.
Context-Mapping
Although the individual domains are as self-contained as possible, they still have to interact from
time to time. In the example considered here, the ordering domain for sending orders could access
both the catalog domain and a connected ERP system:
The way these domains interact with each other is determined by a context map. In principle,
Ordering and Booking could share the common model elements. In this case, however, care must be
taken to ensure that one modification does not entail inconsistencies on the other.
One domain could easily use the other. In this case, however, the question arises as to who in this
interaction is entitled to how much power. Can the consumer impose certain changes on the provider
¹³https://www.eventstorming.com
Strategic Domain Driven Design 7
and insist on backward compatibility? Or does the consumer have to be satisfied with what it gets
from the provider?
In the case under consideration, Catalog offers an API to prevent changes in the domain from forcibly
affecting consumers. Since ordering has little impact on the ERP system, it uses an anti-corruption
layer (ACR) for access. If something changes in the ERP system, it only has to update it. In addition,
Strategic Design defines further strategies for the relationship between consumers and providers.
An existing system like the shown ERP system does normally not follow the idea of the bounded
context. Rather, it contains several logical and intermingled sub-domains.
Another possible strategy I want to stress out here is Separate Ways, which means that a specific
aspect like calculating the VAT is separately implemented in several domains:
At first sight this seems to be awkward, especially because it leads to code redundancies and
hence breaks the DRY principle (don’t repeat yourself). Nevertheless, in some situations it comes
in handy because it prevents a dependency to a shared library. While preventing redundant code is
an important goal preventing dependencies is as well vital. The reason is that each dependency also
defines a contract and contracts are hard to change. Hence, it’s a good idea to evaluate whether an
additional dependency is worth it.
As above mentioned, each domain should have its own bounded context. The example shown here
contains an exception: If we have to respect an existing system like the ERP system it might contain
Conclusion
Strategic Design is about identifying self-contained (sub-)domains. In each domain we find an
ubiquitous language and concepts that only make sense within the domain’s bounded context. A
context map shows how those domains interact with each other.
In the next chapter we’ll see we can implement those domains with an Angular using an Nx¹⁴-based
monorepo.
¹⁴https://nx.dev/
Implementing Strategic Design with
Nx Monorepos
In the previous chapter, I’ve presented the idea of Strategic Design which allows to subdivide a
software system into several self-contained (sub-)domains. In this part, I show how to implement
those domains with Angular and an Nx¹⁵-based monorepo.
If you want to have a look into the underlying case study¹⁶, you find the source code here¹⁷
For this, I’m following some recommendations the Nx team recently wrote down in their free e-
book about Monorepo Patterns¹⁸. Before this was available, I’ve used similar strategies but in order
to help establishing a common vocabulary and common conventions in the community, I seek to
use this official way.
Implementation with Nx
For the implementation of the defined architecture, a workspace based on Nx [Nx] is used. This is
an extension for the Angular CLI, which among other things helps to break down a solution into
different applications and libraries. Of course this is just one of several possible approaches. As an
alternative, one could, for example, implement each domain as a completely separate solution. This
would be called a micro-app approach.
The solution shown here puts all applications into one apps folder, and all the reusable libraries are
grouped by the respective domain name in the libs folder:
¹⁵https://nx.dev/
¹⁶https://github.com/manfredsteyer/strategic-design
¹⁷https://github.com/manfredsteyer/strategic-design
¹⁸https://go.nrwl.io/angular-enterprise-monorepo-patterns-new-book
Implementing Strategic Design with Nx Monorepos 9
Because such a workspace consists of several applications and libraries that are managed in a
common source code repository, there is also talk of a monorepo. This pattern is used extensively by
Google and Facebook, among others, and has been the standard case for the development of .NET
solutions in the Microsoft world for about 20 years.
It allows the sharing of source code between the project participants in a particularly simple way and
also prevents version conflicts by having only one central node_modules folder with dependencies.
This ensures that e.g. each library uses the same Angular version.
To create a new Nx based Angular CLI project – a so called workspace – you can just use the
following command:
1 cd e-proc
2 ng generate app ui
3 ng generate lib feature-request-product
Please note the separation between smart and dumb components here. Smart components within
feature libraries are use case specific. An example is a component which allows to search for
products.
On contrary, dumb components don’t know the current use case at all. They just receive data via
inputs, display it in a specific way and emit events. Such presentational components “just” help to
implement use cases and hence they can be reused across them. A example is a date time picker,
which does not know at all which use case it supports. Hence, it can be used within all use cases
dealing with dates.
In addition to these categories, I’m also using the following categories:
• shell: For an application that contains multiple domains, a shell provides the entry point for a
domain
• api: Provides functionalities exposed for other domains
• domain: Domain logic like calculating additional expanses (not used here), validations or
facades for use cases and state management. I’ll come back to this idea in the next chapter.
To keep the overview, the categories are used as a prefix for the individual library folders. Thus,
libraries of the same category are presented next to each other in a sorted overview.
This is a vital aspect of good software design as it allows to split them into a public and a private
part. The public one is exposed for other libraries. Hence, here we have to take care about breaking
changes as they would affect other parts of the system.
However, the private part can be changed quite flexible as long as you make sure the public part
stays the same.
Implementing Strategic Design with Nx Monorepos 11
If we just concentrate on the Catalog domain in our case study, the result looks as follows:
To define such restrictions, Nx allows us to assign tags to each library. Based on this tags, we can
define linting rules.
Tagging Libraries
The tags for our libraries are defined in the file nx.json, which is generated by Nx:
1 "projects": {
2 "ui": {
3 "tags": ["scope:app"]
4 },
5 "ui-e2e": {
6 "tags": ["scope:e2e"]
7 },
8 "catalog-shell": {
9 "tags": ["scope:catalog", "type:shell"]
10 },
11 "catalog-feature-request-product": {
12 "tags": ["scope:catalog", "type:feature"]
13 },
14 "catalog-feature-browse-products": {
15 "tags": ["scope:catalog", "type:feature"]
16 },
17 "catalog-api": {
18 "tags": ["scope:catalog", "type:api", "name:catalog-api"]
19 },
20 "catalog-data-access": {
21 "tags": ["scope:catalog", "type:data-access"]
22 },
23 "shared-util-auth": {
24 "tags": ["scope:shared", "type:util"]
25 }
26 }
Alternatively, these tags can also be specified when setting up the applications and libraries.
According to a suggestion from the mentioned e-book about Monorepo Patterns²¹, the domains are
named with the prefix scope and The library types are prefixed with kind. Prefixes of this type are
only intended to increase readability and can be freely assigned.
²¹https://go.nrwl.io/angular-enterprise-monorepo-patterns-new-book
Implementing Strategic Design with Nx Monorepos 13
1 "nx-enforce-module-boundaries": [
2 true,
3 {
4 "allow": [],
5 "depConstraints": [
6 { "sourceTag": "scope:app",
7 "onlyDependOnLibsWithTags": ["type:shell"] },
8 { "sourceTag": "scope:catalog",
9 "onlyDependOnLibsWithTags": ["scope:catalog", "scope:shared"] },
10 { "sourceTag": "scope:shared",
11 "onlyDependOnLibsWithTags": ["scope:shared"] },
12 { "sourceTag": "scope:booking",
13 "onlyDependOnLibsWithTags":
14 ["scope:booking", "scope:shared", "name:catalog-api"] },
15
16 { "sourceTag": "type:shell",
17 "onlyDependOnLibsWithTags": ["type:feature", "type:util"] },
18 { "sourceTag": "type:feature",
19 "onlyDependOnLibsWithTags": ["type:data-access", "type:util"] },
20 { "sourceTag": "type:api",
21 "onlyDependOnLibsWithTags": ["type:data-access", "type:util"] },
22 { "sourceTag": "type:util",
23 "onlyDependOnLibsWithTags": ["type:util"] }
24 ]
25 }
26 ]
To test against these rules, just call ng lint on the command line:
Implementing Strategic Design with Nx Monorepos 14
Development environments such as WebStorm / IntelliJ or Visual Studio Code show such violations
while typing. In the latter case, a corresponding plugin must be installed.
Hint: Consider using Git Hooks, e. g. by leveraging Husky²², which ensures that only code
not violating your linting rules can be pushed to the repository.
Conclusion
Strategic Design provides a proven way to break an application into self-contained domains. These
domains are characterized by their own specialized vocabulary, which must be used rigorously by
all stakeholders.
The CLI extension Nx provides a very charming way to implement these domains with different
domain-grouped libraries. To restrict access by other domains and to reduce dependencies, it allows
setting access restrictions to individual libraries.
This helps to ensure a loosely coupled system which is easier to maintain as a sole change only
affects a minimum of other parts of the system.
²²https://github.com/typicode/husky
Tactical Domain-Driven Design with
Angular und Nx
The previous chapters showed how to use the ideas of strategic design with Angular and Nx. This
chapter builds upon the outlined ideas and describes further steps to respect tactical design too.
The case study used in this chapter is about a travel web application which consists of the following
sub-domains:
While using layers is quite a traditional way of organizing an domain, there are also
alternatives like hexagonal architectures or clean architecture.
For those aspects that are to be shared and used across domains, an additional shared swimlane is
used. Shared libraries can be quite useful. Consider - for example - shared libraries for authentication
or logging.
Note: the shared swimlane corresponds to the Shared Kernel proposed by DDD and also
includes technical libraries to share.
As discussed in the previous chapter, access constrains define which libraries can use/depend upon
other libraries. Typically, each layer is only allowed to communicate with underlying layers. Cross-
domain access is allowed only with the shared area. The benefit of using these restrictions can result
in loose coupling and thus increased maintainability.
To prevent too much logic to be put into the shared area, the approach presented here
also uses APIs that publish building blocks for other domains. This corresponds to the
idea of Open Services in DDD.
Tactical Domain-Driven Design with Angular und Nx 17
Regarding the shared part, one can see the following two characteristics:
• As the grayed-out blocks indicate, most util libraries are in the shared area, especially as
aspects such as authentication or logging are used across systems.
• The same applies to general UI libraries that ensure a system-wide look and feel.
Notice that domain-specific feature libraries, however, are not in the shared area. Feature-related
code should be placed within its own domain.
While developers may elect to share feature code (between domains), this practice can lead to shared
responsibilities, more coordination effort, and breaking changes. Hence, it should only be shared
sparingly.
Code Organization
Based on Nrwl.io’s Enterprise MonoRepository Patterns²⁵, I distinguish between five categories of
layers or libraries:
This complete architectural matrix is initially overwhelming. But after a brief review, almost all
developers I’ve worked with agreed that the code organization facilitates code reuse and future
features.
Of course, these layers can now also be packaged in their own libraries. For the sake of simplicity,
it is also possible to store them in a single library, which is subdivided accordingly. This decision
makes sense if these layers are usually used together and only need to be exchanged for unit tests.
Implementations in a Monorepos
Once the components of our architecture have been determined, the question arises of how they
can be implemented in the world of Angular. A very common approach also used by Google itself
is using a monorepo. It is a code repository that contains all the libraries of a software system.
While a project created with the Angular CLI can nowadays be used as a monorepo, the popular tool
Nx²⁶ offers some additional possibilities which are especially valuable for large enterprise solutions.
²⁶https://nx.dev/
Tactical Domain-Driven Design with Angular und Nx 19
These include the previously discussed ways to introduce access restrictions between libraries²⁷. This
prevents each library from accessing each other, resulting in a highly coupled overall system.
To create a library in a monorepo, one instruction is enough:
As you just use ng generate library instead of ng generate module there is not more
effort for you. However, you get a cleaner structure, improved maintainability, and less
coupling.
The switch directory provided by Nx specifies an optional subdirectory where the libraries are to
be put. This way, they can be grouped by domain:
The names of the libraries also reflect the layers. If a layer has several libraries, it makes sense to
use these names as a prefix. This results in names such as feature-search or feature-edit.
In order to isolate the actual domain model, the example shown here divides the domain library into
the three further layers mentioned:
²⁷https://www.softwarearchitekt.at/aktuelles/sustainable-angular-architectures-2/
Tactical Domain-Driven Design with Angular und Nx 20
As usual in OO-land, these entities use information hiding to ensure that their state remains
consistent. You implement this with private fields and public methods that operate on them.
These entities not only encapsulate data, but also business rules. At least the method setStatus indi-
cates this circumstance. Only for cases where business rules can not be meaningfully accommodated
in an entity, DDD defines so-called domain services.
Entities that only represent data structures are frowned upon in DDD. The community
calls them devaluing bloodless (anemic)²⁸.
With functional programming, the previously considered entity model would therefore be separated
into a data part and a logic part. Domain-Driven Design Distilled³¹ which is one of the standard
works for DDD and primarily relies on OOP, also admits that this rule change is necessary in the
world of FP:
Here the entities also use public properties. This practice is quite common in FP; the excessive use
of getters and setters, which only delegate to private properties, is often ridiculed!
Much more interesting, however, is the question of how the functional world avoids inconsistent
states. The answer is amazingly simple: data structures are preferably immutable. The keyword
readonly in the example shown emphasizes this.
A part of the program that wants to change such objects has to clone it, and if other parts of the
program have first validated an object for their own purposes, they can assume that it remains valid.
Facades
Facades³³ (aka Applications Services) are used to represent the domain logic in an use case specific
way. The provide several advantages:
• Encapsulating complexity
• Taking care about state management
• Simplified APIs
Independent of DDD, this idea has been very popular in the world of Angular for some time.
For our example, we could create the following Facade:
Note the use of RxJS and observables in the facade. This means that the facade can auto-deliver
updated flight information when conditions change. Another advantage of Facades is the ability
to transparently introduce Redux and @ngrx/store later when needed. This can be done without
affecting any of the external application components.
For the consumer of the Facade it is not relevant whether it manages the state by it self
or by delegating to a state management library.
Stateless Facades
While it is a good practice to make server-side services stateless, often this goal is not performant
for services in web/client-tier.
A web SPA has state and that’s what makes it user-friendly!
To avoid UX issues, Angular applications do not want to reload all the information from the server
over and over again. Hence, the Facade outlined holds the loaded flights (within the observable
discussed before).
Domain Events
Besides performance improvements, using Observables provide a further advantage. Observables
allow further decoupling, since the sender and the receiver do not have to know each other directly.
This also perfectly fits to DDD, where the use of domain events are now part of the architecture.
If something interesting happens in one part of the application, it sends a domain event and other
application parts can react to it.
In the shown example, a domain event could indicate that a passenger is now BOARDED. If this is
interesting for other parts of the system they can execute specific logics.
For Angular developers familiar with Redux or Ngrx: Domain events can be represented
as dispatched actions!
Tactical Domain-Driven Design with Angular und Nx 25
Conclusion
Modern single page applications (SPAs) are often more than just recipients of data transfer objects
(DTOs). They often contain significant domain logic which adds complexity. Ideas from DDD help
developers to manage and scale with the resulting complexity.
Due to the object-functional nature of TypeScript and prevailing customs, a few rule changes are
necessary. For instance, we usually use immutables and separate data structures from the logics
operating on them.
The implementation outlined here bases upon the following ideas:
• The use of monorepos with multiple libraries grouped by domains helps building the basic
structure.
• Access restrictions between libraries prevent coupling between domains.
• Facades prepare the domain model for individual use cases and take care of maintaining the
state.
• If needed, Redux can be used behind the facade without the rest of the application noticing.
If you want to see all these topics in action, checkout our Angular Architecture work-
shop³⁴.
³⁴https://www.softwarearchitekt.at/schulungen/advanced-angular-enterprise-anwendungen-und-architektur/
From Domains to Micro Frontends
Let’s assume, you’ve identified the sub-domains for your system. The next question is, how to
implement them.
One option is to implement them within a big application – aka a deployment monolith – and the
second one is about providing a separate application for each domain.
Such applications are nowadays called micro frontends.
Deployment Monoliths
A deployment monolith is a big integrated solution containing different domains:
This approach supports a consistent UI and leads to optimized bundles as everything is compiled
together.
On the other side, a team responsible for one sub-domain needs to coordinate with other teams
responsible for other sub-domains. They have to agree on an overall architecture, the leading
framework, and an update policy for dependencies. Interestingly, you might also see this as an
advantage.
Also, it is tempting to just reuse parts of other domains which may lead to higher coupling and
– sooner or later – to breaking changes. To prevent this situation, you can go with free tools like
Nrwl’s Nx³⁵. For instance, Nx allows to define access restrictions between the parts of your mono
repo to enforce your envisioned architecture and loosely coupling.
³⁵https://nx.dev/angular
From Domains to Micro Frontends 27
.
Also, as we are having several tiny systems now, it decreases the complexity.
If you seek even more isolation between your sub-domains and teams responsible for them, you
could decide to put each sub-domain into a (mono) repository of its own:
Now, you have something people are calling micro frontends nowadays. This allows the individual
teams to be as autarkik as possible: They can choose for their own architectural style, their own
technology stack and they can even decide by themselves when to update to newer versions of the
From Domains to Micro Frontends 28
frameworks used. This also means they can use “the best technology” for the requirements given
within the current sub-domain.
To possibility to use their own framework and architecture comes in handy for applications which
are developed in long term. If, for instance, a new framework shows up in five years, we can just
use it for implementing the next domain.
However, this does not come without costs: Now you have to deal with shipping your shared libraries
via npm and this includes versioning which can lead to version conflicts.
This approach seems to fit quite well for product suites like Google or Office 365:
From Domains to Micro Frontends 29
Here, each domain is a self contained application. This worked quite well, because we don’t need
much interactions between the domains. If we needed to share data, we can use the backend. Using
this strategy, Word 365 can use an Excel 365 sheet for a series letter.
This approach has several advantages:
• It is easy
• It uses SPA frameworks as intended
• We get optimized bundles per domain
In the shown screenshot, the shell loads the micro frontend with the red border into its working
area. Technically, it just loads the micro frontends bundles on demand. After this, the shell creates
an element for the micro frontend’s root element:
Instead of bootstrapping several SPAs we could also use iframes. While we all know the huge
disadvantages of iframes as well as strategies to deal with most of them, they provide two useful
features here:
1) Isolation: An micro frontend in one iframe cannot influence or hack another microfrontend in
another iframe. Hence, they come inhandy for plugin systems or when integrating applications from
other vendors. 2) They also allow to integrate legacy systems.
A library that compensates most of the disadvantages of iframes for intranet applications can be
found here³⁶.
³⁶https://www.npmjs.com/package/@microfrontend/common
From Domains to Micro Frontends 31
• The user gets an integrated solution which consists of different micro frontends.
• We don’t loose state when navigating between domains.
• If we don’t use specific tricks (outlined in the next chapter), each micro frontend comes with
its own copy of Angular and the other frameworks. This increases the bundle sizes.
• We have to implement some infrastructure code for loading micro frontends and switching
between them.
• Also here, we have to make sure to get a common look and feel (we need a common design
system).
Finding a Solution
Deciding between a deployment monolith and different approaches for micro frontends is quite
difficult because each option comes with its very own advantages and disadvantages.
To provide some guidance I’ve created the following decision tree which also sums up the ideas
outlined in this chapter:
As the implementation of a deployment monolith and the hyperlink approach is obvious, the next
chapter discusses how to implement a shell.
From Domains to Micro Frontends 32
Conclusion
There are several ways for implementing micro frontends and all of them come with their very own
advantages and disadvantages. Also, using a consistent and optimized deployment monolith can
also be the right choice.
At the end of the day, it’s about knowing your architectural goals and about evaluating them against
the positive and negative consequences of architectural candidates available for your needs.
6 Steps to your Angular-based Micro
Frontend Shell
As discussed in the last chapter, there are several approaches for implementing SPA-based micro
frontends.
In this chapter, I show how to implement one of them in 6 steps: A shell loading micro frontends
on demand. Other than in my chapter about micro frontends and web components, I don’t use web
components for the macro architecture. Instead, I just go with ordinary SPAs which are loaded and
bootstrapped on demand. For the micro architecture I still use web components.
While this decision simplifies the implementation, we can still isolate different applications using
shadow DOM as Angular also supports this standard associated with web components for traditional
Angular components since its first day.
The case study loads a simple client-a and a simple client-b into the shell. Also, the former one
shares a widget with the latter one:
6 Steps to your Angular-based Micro Frontend Shell 34
The source code³⁷ for this can be found in my GitHub account here³⁸.
³⁷https://github.com/manfredsteyer/angular-microfrontend
³⁸https://github.com/manfredsteyer/angular-microfrontend
6 Steps to your Angular-based Micro Frontend Shell 35
1 @NgModule({
2 imports: [
3 ReactiveFormsModule,
4 BrowserModule,
5 RouterModule.forRoot([
6 { path: 'client-a/page1', component: Page1Component },
7 { path: 'client-a/page2', component: Page2Component },
8 { path: '**', component: EmptyComponent}
9 ], { useHash: true })
10 ],
11 [...]
12 })
13 export class AppModule {
14 [...]
15 }
³⁹https://martinfowler.com/chapters/microservices.html#ComponentizationViaServices
⁴⁰https://nx.dev/angular
6 Steps to your Angular-based Micro Frontend Shell 36
1. Just put everything into one bundle, so that this global array is not needed
2. Rename the global array
Here, I use solution 1) because a micro frontend is by definition small and just having one bundle
makes loading it on demand easier. Also, as we will see later, we can share libraries like RxJS or
Angular itself between them.
To tweak the CLI to produce one bundle, I’m using my free tool ngx-build-plus⁴³ which provides a
--single-bundle switch:
1 ng add ngx-build-plus
2 ng build --prod --single-bundle
1 frontend['visible'] = false;
1. It does not matter in which order the micro frontends are loaded. When they are loaded, they
can grab the current parameters from the url
2. It allows deep linking
3. It’s like the web is supposed to work
4. It’s easy to implement
Setting an url parameter with the Angular router is just a matter of calling one method:
1 this.router.navigate(['.'], {
2 queryParamsHandling: 'merge', queryParams: { id: 17 }});
The option merge makes sure that the existing url parameters are not lost. If there is already a
parameter id, the router will overwrite it.
Also, listening for changes within url parameters is also something the Angular router can help with:
6 Steps to your Angular-based Micro Frontend Shell 38
1 route.queryParams.subscribe(params => {
2 console.debug('params', params);
3 });
1. If you wrap your micro frontends into web components, you could use their properties and
events to communicate with the shell.
2. The shell could put a “message bus” into the global namespace: typescript (window as
any).messageBus = new BehaviorSubject<MicroFrontendEvent>(null);
Both, the shell and the Microfrontends could now subscribe to this message bus and listen for
specific events they are interested into. Also, both can emit events.
3. Using custom Events provided by the browser: “‘typescript // Sender const customer = { id: 17,
… }; window.raiseEvent(new CustomEvent(‘CustomerSelected’, {details: customer}))
// Receiver window.addEventListener(‘CustomerSelected’, (e) ⇒ { … }) “‘
To use webpack externals together with the Angular CLI you can leverage ngx-build-plus⁴⁴ which
even comes with a schematic introducing the needed changes into your application.
As mentioned above, you can install it with ng add:
1 ng add ngx-build-plus
1 ng g ngx-build-plus:externals
Please remember that within an monorepo you have to provide the name of the project in question:
⁴⁴https://www.npmjs.com/package/ngx-build-plus
6 Steps to your Angular-based Micro Frontend Shell 40
This also introduces an npm script build:<project-name>:externals. For the default project there
is a script build:externals too.
If you look into the index.html after running this script, you see that Angular is directly loaded:
To optimize this, one could also put those parts of Angular into one bundle.
Also, if you have a look into the generated webpack.externals.js, you find a section mapping
package names to global variables:
This, for instance, makes the produced bundle to reference the global variable ng.core when it needs
@angular/core. Hence, @angular/core does not need to be part of the bundle anymore.
Please note that this is not the default operation mode for Angular and hence it comes with some
risks.
6 Steps to your Angular-based Micro Frontend Shell 41
Conclusion
With the right wrinkles, implementing a shell for micro elements is not that difficult. However,
this is only one way for implementing micro frontends and – as all – it comes with its very own
advantages and disadvantages. Hence, before implementing it, make sure it fits your architectural
goals.
Literature
• Evans, Domain-Driven Design: Tackling Complexity in the Heart of Software⁴⁵
• Wlaschin, Domain Modeling Made Functional⁴⁶
• Ghosh, Functional and Reactive Domain Modeling⁴⁷
• Nrwl, Monorepo-style Angular development⁴⁸
• Jackson, Micro Frontends⁴⁹
• Burleson, Push-based Architectures using RxJS + Facades⁵⁰
• Burleson, NgRx + Facades: Better State Management⁵¹
• Steyer, Web Components with Angular Elements (article series, 5 parts)⁵²
⁴⁵https://www.amazon.com/dp/0321125215
⁴⁶https://pragprog.com/book/swdddf/domain-modeling-made-functional
⁴⁷https://www.amazon.com/dp/1617292249
⁴⁸https://go.nrwl.io/angular-enterprise-monorepo-patterns-new-book
⁴⁹https://martinfowler.com/articles/micro-frontends.html
⁵⁰https://medium.com/@thomasburlesonIA/push-based-architectures-with-rxjs-81b327d7c32d
⁵¹https://medium.com/@thomasburlesonIA/ngrx-facades-better-state-management-82a04b9a1e39
⁵²https://www.softwarearchitekt.at/aktuelles/angular-elements-part-i/
About the Author
Manfred Steyer
Manfred Steyer is a trainer, consultant, and programming architect with focus on Angular.
For his community work, Google recognizes him as a Google Developer Expert (GDE). Also, Manfred
is a Trusted Collaborator in the Angular team. In this role he implemented differential loading for
the Angular CLI.
Manfred wrote several books, e. g. for O’Reilly, as well as several articles, e. g. for the German Java
Magazine, windows.developer, and Heise.
He regularly speaks at conferences and blogs about Angular.
Before, he was in charge of a project team in the area of web-based business applications for
many years. Also, he taught several topics regarding software engineering at a university of applied
sciences.
Manfred has earned a Diploma in IT- and IT-Marketing as well as a Master’s degree in Computer
Science by conducting part-time and distance studies parallel to full-time employments.
You can follow him on Twitter (https://twitter.com/ManfredSteyer) and Facebook (https://www.facebook.com/manfr
and find his blog here (http://www.softwarearchitekt.at).
Trainings and Consultancy
If you and your team need help regarding Angular, we are happy to help with our on-site workshops
and consultancy.
Please find our offers here⁵³.
⁵³https://www.softwarearchitekt.at/angular-schulung/