Clean 🧼 Architecture’d REST Api using NestJS as 3rd Party 🥳

In this post I present an implementation of clean architecture’d REST Api using NestJS. This post does not intends to teach about clean architecture, and for that you will need to follow the references at the bottom of this page.

The stack for that project is:

  • PostgresSQL
  • TypeOrm
  • NodeJS
  • NestJS, a framework for building efficient, scalable Node.js server-side applications.
  • Nrwl – monorepo
  • Typescript

For the codebase I used the following project structure, projects & library packages:

  • Generate apps
    • api – NestJs
    • client – Angular
  • Generate libraries
    • business-logic, Typescript Library
    • entities, Typescript Library
    • repository, Typescript Library
    • inteface-adapters, Typescript Library
    • ui, Angular Library

AccountController

Let’s have look at the main process of activating the API using clean architecture

Translating the code we have:

  • a NestJs route for GET /accounts included by creating a new NestJS controller named ‘accounts.controller.ts’
  • an ‘Interactor’ which is initialized with matching validators & repository for the upcoming input.
  • The DB connection from NestJS is injected into the repository
  • The Express Request is injected into NestView
  • a Presenter to generate the view model and wrap it as output
  • a Nest View to handle the actual HTTP Response of the view model with the help of the NestJs framework
  • a controller to initiate and orchestrate the request/response pipe, which we’ll be shown soon.

Notes:

  • Using clean architecture we have full control over the details of the application. Validations, repositories, connections, views, even the NestJS framework is isolated and can be easily plugged-out, plugged-in & replaced.
  • GetAll request have no input parameters therefore validation is not required for that API, we will see later GetById which will be validated using UUID validator

AccountsLogicController

  • That piece of code is the main process of the request/response flow.
  • The Interactor handles the request – validation, processing & persistency and generate the result for presenation.
  • The presenter handle the response from the interactor and prepare it for view
  • The view is the driver that talks to the NestJS/Express Response which delivers the output to the HTTP.

Notes:

  • NetsJS’s “ApplicationController” name collided with clean architecture name of the same object so I named it AccountsLoginController. How would you call it?

GetAllAccountsInteractor

The interactor (AKA usecase) implements IRequestHandler which dictates that a handler function is required. The interactor is responsible for request validation till response message is ready:

  • input validation
  • use case flow
  • working with the repositories
  • creating the response message which package both the validationResult and the data for the ViewModel

IRequestHandler

Interactor’s common interface, requires to implement a handle function with IRequest parameter as its input & IResponse parameter as its output

GetAllAccountsResponsePresenter

The Presenter implements IPresenter which dictates that a handler function is required. The presenter is responsible for the response validation till response message is ready

  • conditional handling of presentation due to success or failure of use case
  • prepare view model for each case, success or failure

Notes:

  • GetAllAccountsOutput is a wrapper for error & success for the View to be able to differentiate if the feedback to the action should be displayed in the positive flow (success behaviour) or negative flow (failure behaviour).

NestView

NestView is the view driver over NestJS, That is actually one of the amazing things about clean code architecture and it how it introduce the framework as plug-in into the system and how it decouples NestJS from the Business Logic.
Actually we could say that replacing the framework has become very easy and that’s correct, 1) create a new View for the new framework, 2) change the routes 3) change the db connection and the framework is replaced (of course you should also configure the new framework).

The View only needs the “Response” object from the NestJS(/Express) framework and the view model and it renders the JSON as HTTP Response

RepositoryFactory

Helper function to initialise & return desired repositories from the data layer. the existing functions create an In-Memory Account Repository & TypeORM over PostgresSQL Account Repository.

AccountRepository

Account Repository implements IRepository act as a common interface for all repositories, and act as mechanism for changing the dependency of code over database, and make the database plug-in to the code. In the example TypeORM over PostgreSQL is the current plugged device but the example also show a InMemory Account Repository which can act as repository mock for testing.

AccountEntity

AccountEntity is a db schema file for TypeOrm which describe how the storage will be structured. It’s location is the interface-adapters or the data layer if such further separation is desired.

GetAllAccountsRequestMessageValidator

Responsible for the input parameters validation, implements IValidator interface to enable the validator plug in functionality into the architecture.

IValidator

IValidator generic interface for all validator passed as request message validators for the interactor.

GetAllAccountsValidationResult

Validation Result model implements IValidationResult interface, is the output model of the validation test over the input parameters. it can be valid or invalid with errors.

IValidationResult

common interface for validation result models

GetAllAccountsRequestMessage

Request Message represent the input passed into the interactor, implements IRequestMessage implementing a generic interface which follows the interactor input interface.

GetAllAccountsResponseMessage

Response Message represent the output passed out from the interactor and into the presenter, implements IResponseMessage implementing a generic interface which follows the interactor output interface.

IPresenter

Presenters common interface for integrating with the interactor. handle is the only function presenters required to implement, response message (from the interactor output) as input and outputs a view model

Account

Data object used for the business logic of the application, all data coming from storage or input tot he system are converted to that standard system interface

GetAllAccountsOutput

Simple inheritance of ViewOutputImplementation with passing GetAllAccountsViewModel as the view model type reusing the implementation code.

ViewOutputImplementation

View Output Model implementation of result passing the view model or the error to the presenter

IViewOutput

Generic interface for all View Output

IView

Generic interface for all view drivers, having render function and viewModel placeholder to render the proper view according to the view device

IRequestMessage

Request message interface provides a common interface for input parameters of the API request going into the interactor

IResponseMessage

Response message interface provides a common interface for output going out of the interactor and to the presenter

Final Notes

In this post I walked thru an implementation of clean architecture of a REST API using NestJS, TypeORM, Postgres as pluggable frameworks.
Clean Architecture is considered an enterprise application architecture. Having that experience with clean architecture shows that the amount of boilerplate code added to the codebase is 10x and more than a straight forward monolithic code.

Questions can rise against using it like, why should we care if the DB or NestJS is pluggable. In favour we can quickly see how easy it is to write unit test to the business logic.

So thoughts like, “is that the proper way to architect a system from day one”, or “should such architecture be evolved through many previous steps”. But I can imagine startups investing their time to bring some architecture to their codebase and the gain in productivity occurs as the code keeps remaining consistent, decoupled, and the real advantage when the development remains adaptive to future changes, keeping themselves capable of switching from one framework/storage/3rd party api/solutions to another without any substantial effort when the tsunami hits.

References

The Clean Code Blog – Clean Architecture – https://blog.cleancoder.com/uncle-bob/2012/08/13/the-clean-architecture.html

Robert C Martin – Clean Architecture – https://www.youtube.com/watch?v=Nltqi7ODZTM

The Book

a Clean Architecture in .Net – https://medium.com/@stephanhoekstra/clean-architecture-in-net-8eed6c224c50

Nrwl – monorepo – https://nx.dev/angular/tutorial/01-create-application

NestJs – https://docs.nestjs.com/

Generating a Typescript Library – ng generate @nestjs/schematics:library mynestlib

a minimal demo that shows how to use multiple backends and share modules between them. https://github.com/creadicted/nx-modular-nest-backend

Building Full-Stack Applications https://nx.dev/angular/fundamentals/build-full-stack-applications