Hexagonal Architecture with Angular
Tags: architecture, angular, javascript, webdev
Published: 2022-08-10 20:22:00

# Summary

1. [Introduction](#1-introduction)
2. [A complete, working example](#2-a-complete-working-example)
3. [Implementation choices](#3-implementation-choices)
4. [Hexagonal architecture benefits in Angular](#4-hexagonal-architecture-benefits-in-angular)
5. [When to use Hexagonal architecture in Angular](#5-when-to-use-hexagonal-architecture-in-angular)
6. [Conclusion](#6-conclusion)
 

# 1 - Introduction

For a long time, Model View Controller seemed to  be the favorite architecture of software developers. It was used in back-end, as well as in front-end code. But with the growing interest of the community for Domain-Driven Design, this architecture has been challenged by its cousin, the "hexagonal" (or "ports and adapters") architecture.

As MVC, hexagonal architecture uses the separation principle, but also more abstraction, and domain code is central to its architecture. 
_If you want more information about hexagonal architecture, here is [a complete article](https://alistair.cockburn.us/hexagonal-architecture/), written by its designer, Alister Cockburn._

Currently hexagonal architecture is mostly used in back-end code, and there are poor resources about it in front-end code, especially for Angular.

**How to adapt hexagonal architecture to Angular ? Would it be beneficial ? If you are also interested in these questions, you should read this article.**

# 2 - A complete, working example

All the following explanations will be based on [an exemple app I developed, and made available on Github](https://github.com/aurelien-alet/angular-toh-hexagonal). This app is based on [Angular's tour of heroes](https://angular.io/tutorial). If you launch the app, the displayed interface is the same as in the Angular tutorial, but there are great differences in the code structure. As a reminder, the principle of this small app is to display a list of heroes and to manage (create, delete, modify) them. The [angular-in-memory-web-api module](https://www.npmjs.com/package/angular-in-memory-web-api) is used to simulate external API calls.

This is an overview of this example's architecture:

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/k2lweiasthqx79srjlb4.png)

And the associated code organization: 

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/gs1mjrt114vs1yhnk2ys.png)

## Domain

In hexagonal architecture, the entire domain related code is isolated. The tour of heroes app has the following purposes: display a list of heroes, display details on a specific hero, and display logs of the actions made by the user. The domain related classes are central to the architecture: `HeroesDisplayer`, `HeoresDetailDisplayer` and `MessagesDisplayer`.

## Ports

As you can imagine, domain related code is not lonely in our heroes app. There is also a user interface related code, corresponding to Angular components, and external APIs calls, corresponding to Angular services. In every hexagonal architecture, domain related code doesn't interact directly with all of this code. Instead, objects called ports are used, and they are implemented by interface classes. This weakens the coupling between the elements of our architecture.

In our heroes app, `HeroesDisplayer` and `HeoresDetailDisplayer` need to interact with an external service, which stores heroes related interactions. For this purpose, they will expose an `IManageHeroes` port. For each of our domain classes, we want to keep track of every user's interactions. That is why they also have an `IManageMessages` port.

The users realize actual actions in our app through display interfaces. These interfaces can be divided in several categories, according to their purpose. To ensure a faithful comparison with the Angular tour of heroes app, we should have interfaces displaying heroes (a list of heroes and a dashboard), an interface displaying hero details, and an interface displaying messages. Therefore, the related ports should be respectively `IDisplayHeroes`, `IDisplayHeroDetail` and `IDisplayMessages`.

## Adapters

Now that our ports are defined, we have to plug adapters on them. One of the benefits of hexagonal architecture is the ease when switching between adapters. For example, the adapter plugged to `IManageHeroes` could be an adapter calling a REST API, and we could replace it easily by an adapter using a GraphQL API. In our case, we want our app to be identical to Google tour of heroes app. So we implement an angular service, `HeroAdapterService`, calling an in-memory web API, and another, `MessageAdapterService`, storing messages locally.

The adapters for the three other ports are user interface related adapters. In our app, they will be implemented by Angular components. As you can see, the `IDisplayHeroes` port is implemented by three adapters. Details will be available in the following.
 
_As explained above, there is an asymmetry in our adapters, due to their nature. The architecture diagram represents it in the following way: architecture left-side adapters are designed for users interactions, whereas right-side adapters are designed for external services interactions._

# 3 - Implementation choices

Because hexagonal architecture was designed for back-end applications, some arrangements have been made in code implementation. These choices are explained in the following part. 

## Angular related objects in domain code

A good practice in hexagonal architecture is to keep domain related code independent of any framework, in order to ensure it is functional for any kind of adapter. But in our code, the domain is highly dependent on Angular and rxjs objects. In fact, we can assume that we won't use several typescript or JavaScript frameworks, in order to keep interface coherence. Also, the angular dependency injection system is very useful to achieve the inversion of control principle. However, it should be possible to use JavaScript promises instead of rxjs observables, but we would have to write a lot of _boilerplate_ code in our classes.

## Observable return type in left-side ports

Since the logic behind the code is handled in the domain, one could wonder why returning Observable objects in `IDisplayHeroDetail`, `IDisplayHeroes` and `IDisplayMessages` ports. Indeed, each object returned by the services is handled inside the domain code using `pipe` and `tap` methods. For example, the hero detail save result returned by `HeroAdapterService` is managed directly in the `HeroDetailDisplayer`: 

#### **`hero-detail-displayer.ts`**
```typescript
askHeroNameChange(newHeroName: string): Observable<void> {
    [...]
    const updatedHero = {id: this.hero.id, name: newHeroName};
    return this._heroesManager.updateHero(updatedHero).pipe(
        tap(_ => this._messagesManager.add(`updated hero id=${this.hero ? this.hero.id : 0}`)),
        catchError(this._errorHandler.handleError<any>(`updateHero id=${this.hero.id}`, this.hero)),
        map(hero => {if(this.hero){this.hero.name = hero.name}})
    );
}
```

Still, returning an empty observable from the `askHeroNameChange` method is interesting if we aim at enabling the interface adapters to know when the data is loaded. For example, when the hero detail changes are effective, we can go back to the previous page: 

#### **`hero-detail.component.ts`**
```typescript
changeName(newName: string): void {
    this.heroDetailDisplayer.askHeroNameChange(newName).pipe(
        finalize(() => this.goBack())
    ).subscribe();
}
```

The drawback of this implementation choice is the need of subscribing to each domain function call inside left-side adapters:

#### **`heroes.component.ts`**
```typescript
this.heroesDisplayer.askHeroesList().subscribe();
```

## HeroesDisplayer class instantiated twice

In our app, dependency injection is handled in **`app.module.ts`**. We use injection tokens to make domain classes accessible inside Angular components. For instance the injection of IDisplayHeroDetail into the HeroDetail component is done this way:

#### **`app.module.ts`**
```typescript
import HeroDetailDisplayer from '../domain/hero-detail-displayer';

providers: [
    [...]
    {provide: 'IDisplayHeroDetail', useClass: HeroDetailDisplayer},
    [...]
}
```
_Sets HeroesDetailDisplayer instance as a IDisplayHeroDetail implementation_

#### **`hero-detail.component.ts`**
```typescript
import IDisplayHeroDetail from 'src/app/domain/ports/i-display-hero-detail';

export class HeroDetailComponent implements OnInit {
    constructor(
        @Inject('IDisplayHeroDetail') public heroDetailDisplayer: IDisplayHeroDetail,
        [...]
    ) {}
}
```
_Injects HeroDetailDisplayer inside HeroDetailComponent_

However, there is a subtlety somewhere in the code: two different injection tokens are generated for the HeroesDisplayer class. Moreover, `HeroesComponent` and `DashboardComponent` share the same injection token, whereas `HeroSearchComponent` component uses another token.

#### **`app.module.ts`**
```typescript
import HeroesDisplayer from '../domain/heroes-displayer';

providers: [
    // Used in HeroesComponent and in DashboardComponent 
    {provide: 'IDisplayHeroes', useClass: HeroesDisplayer},
    // Used in HeroSearchComponent
    {provide: 'IDisplayHeroesSearch', useClass: HeroesDisplayer},
]
```

This is because `HeroesComponent` and `DashboardComponent` can share the same instance of `HeroesDisplayer`: they display the same list of heroes. On the other hand, if `HeroSearchComponent` had this same instance, each search would affect the displayed heroes, since the `heroes` attribute is modified by the `askHeroesFiltered` method in `HeroesDisplayer`. Sharing the same token for the three components would change our app's behavior:

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/djmk2bl9j93bava88cme.gif)

# 4 - Hexagonal architecture benefits in Angular

The main essence of hexagonal architecture consists in having interchangeable adapters allowing our app to be driven equally by a human, a system, or by tests. In our app, we are highly tied to the Angular framework, which means we do not make a whole benefit from this architecture asset. However, I found some promising insights from experiencing it in front-end code.

## Decoupled presentational layer, core layer and external services calls

Domain code, corresponding to our core layer, is clearly separated from the interface adapter, namely the presentational layer, by ports. Thanks to these same ports, the risk of adding unwanted code into external service calls is reduced. All the core logic is handled into domain classes.

#### **`heroes.component.ts`**
```typescript
constructor(
    @Inject('IDisplayHeroes') public heroesDisplayer: IDisplayHeroes
) { }
```
_Imports domain class, corresponding to code layer_

#### **`heroes.component.html`**
```html
<li *ngFor="let hero of heroesDisplayer.heroes">
    [...]
</li>
```
_Uses heroes information handled by domain code inside view, corresponding to presentational layer_

## Code factorization

If you look at the original tour of heroes application, the main purpose of the `HeroesComponent`, of the `HeroSearchComponent` and of the `DashboardComponent` are very close. They all display the list of heroes, but the possible interactions differ depending on components. Therefore the related core code, mapping services returns to displayed information, should be factorized. In our code, the domain related code for the three components is factorized: we take advantage of hexagonal port re-usability.

## Testing

Sometimes, Angular tests can be very painful, even more if core code is mixed with presentational code inside components. This code grows as your application evolves. Keeping our display components, our domain code, and our services isolated from each other make the tests more straightforward. You can easily mock other layers, and focus on testing the current class.

#### **`hero-detail.component.spec.ts`**
```typescript
beforeEach(async () => {
    spyIDisplayHeroDetail = jasmine.createSpyObj(
      'IDisplayHeroDetail', 
      ['askHeroDetail', 'askHeroNameChange'],
      {hero: {name: '', id: 0}}
    );
    spyIDisplayHeroDetail.askHeroDetail.and.returnValue(of());
    spyIDisplayHeroDetail.askHeroNameChange.and.returnValue(of());
    [...]
}
```
_Hero details display tests: domain class and methods can be mocked easily_

# 5 - When to use Hexagonal architecture in Angular

Even if we can't totally compare it with back-end code, hexagonal architecture can have some great benefits in some front-end applications. Some use cases seem particularly adapted to it.

## Profile based apps

As we isolated the presentational layer, apps where the same logic is used inside different interfaces, like profile based apps, are good candidates for our architecture. The [admin-panel](https://github.com/aurelien-alet/angular-toh-hexagonal/tree/admin-panel) branch gives an example of what the app would look like if we added an admin panel interface. This interface, designed for admin users, allows them to do every administrative action inside a single view: adding, changing, deleting or searching for heroes. Only the `AdminPanelComponent` is added to the heroes app, no changes inside domain code or services, showing their reusable attribute.

_To launch the administrator interface, run `npm run start:admin` on `admin-panel` branch._

## Apps calling multiple external services

Angular hexagonal architecture is also adapted if you have to contact several external services serving the same purpose. Once again, domain code re-usability simplifies. Let's say we want to call an online service instead of our in-memory heroes service: [the superhero api](https://akabab.github.io/superhero-api/) by Yoann Cribier. Adding a `SuperheroApiAdapterService` is the only thing required, as you can see in the [superhero-api](https://github.com/aurelien-alet/angular-toh-hexagonal/tree/superhero-api) branch.

_To make the app communicate with superhero-api, run `npm run start:superhero-api` on the `superhero-api` branch. Observation: in our example, heroes modifying and deletion aren't implemented by the online service._

# 6 - Conclusion

This tiny app shows it's possible to adapt hexagonal architecture to an Angular app. Some problems, which haven't been raised by the tour of heroes tutorial app, can be solved using it. 

Thank you for reading !
I hope you enjoyed this article, and I'm very interested in any feedback of this architecture adoption.   

I also would like to thank Leila for correcting this article.