Building Onion Architecture With NestJS and Nx
Your code should have layers, like an onion
--
Some time ago I read a brilliant article, “Tactical Domain-Driven Design with Angular and Monorepos?” by Manfred Steyer. I was delighted with the content I found there, and as a big fan, I would like to translate this knowledge to NestJS ground.
Despite Angular and Nest being comparable frameworks, the environments they run on are very different. This article addresses this difference by explaining how to apply the aforementioned concepts in a server-side setting.
Before reading the rest of the article, I advise you to familiarize yourself with Manfred’s article. Additionally, skimming through other articles in the “Domain-Driven Design with Angular” series is highly beneficial.
In my projects, I like to use onion architecture, and I will rely on it in this article. For a better understanding of onion architecture, you can check out this article.
You can find an example Nest project implementing all these concepts on GitHub.
Concepts That Can Be Easily Adapted
Implementation with Nx
Nx is an extension for the Angular CLI that provides strong support for developing Nest and full-stack applications. It provides us with a lot of schematics that make our work faster and less boring. This tool is especially useful in architectures where dependency inversion is an important rule. We can define a set of rules on how our libraries can depend on each other and even display the final version of the dependency graph.

An additional advantage for me is the unification of the CLI. I like to compare it to what Docker gave us. In many repositories, we can find a friendly command which will serve us exactly what we want, without installing new dependencies in our system. Of course, we can add all scripts to our package.json
but I think improving our architect in angular.json
and executing its commands, like ng serve api
and ng test api
, is much prettier.
You can read more about developing with Nx in Manfred’s other article “Strategic Design — Implementation.”
Facade
The facade from Angular is similar to the application service in onion architecture. That part largely depends on the programmer’s preferences since the only difference is the name of the class.
I like to use facade in this situation since this gives a unification inside full-stack projects and the facade pattern describes concisely what application services do.
Concepts That Need Changes
Code organization
Nrwl’s Enterprise MonoRepository Patterns splits an application into six categories of layers: feature, UI, API, domain, util, and application.
Those layers cannot and also should not be moved to the server side.
On the back end, we have mature architectures like clean architecture, onion architecture, and many others which were tested by many companies in a lot of projects. They are independent of technology and framework which tries to implement them so we can easily use them with Nest.
Categories of libraries
In contrast to Manfred and Nrwl, I distinguish these categories of libraries taken from the onion architecture:

- UI: a place for components designed to handle communication with a user by a specific channel; also provides the domain to the application (don’t confuse it with the UI on the front end)
- infrastructure: implements adapters of the application ports
- application services: the place for an application service/facade and, optionally, commands and queries
- domain services: repository interfaces and domain logic involving several entities
- domain: contains the domain models (entities, aggregates…)
In addition, I’m also using the following categories:
- API: exports building blocks from the current subdomain for others
- kernel: a place for global services independent of a domain, included once for an app (exception handler, global middleware…)
- shell: an entry point for the domain, responsible for compositions ports with adapters
- util: includes general utility functions
After applying this set of libraries to the folders and grouping them by a platform and a domain, we can end up with a structure like this:

Dependency restriction
On the server side, user interface means something different than “dumb components.” It is a specific set of components designed to handle communication with a user by a specific channel. (I prefer to use a UI type of library to describe that concept and come up with a different name for the UI libraries on the front-end side).
Since channels cannot be combined in the same application (for example, we cannot run a CLI command on an up and running REST API) UI libraries have to be imported directly by an application module. Then in our UI libraries, we can finally import the shell library which provides the domain for our UI.
Following the dependency inversion from the onion architecture, we will end with dependency restrictions similar to this:
Update
After practicing this approach for two years, I want to share with you two of the most important things I have learned.
First, defining so many library types is unnecessary in most cases. The user interface, application services, and domain are all it takes to get you started. Around this observation, I prepared a plugin for Nx to generate libraries for the entire domain.
Second, how important is the strategical part of the Domain Driven Design, that is not mentioned in this article. In a lot of projects I saw that for many, a domain is an entity. They try to represent their database in code. Long story really short, try to build the domain around the problem it solves, not around the data structure.
Conclusion
On the first page of the Nest’s documentation, we find:
“While plenty of superb libraries, helpers, and tools exist for Node (and server-side JavaScript), none of them effectively solve the main problem of — Architecture.
“Nest provides an out-of-the-box application architecture which allows developers and teams to create highly testable, scalable, loosely coupled, and easily maintainable applications.”
Following that sentence, we should not only start to use that framework and its API to develop controllers and its services, but also wrap it in the best architectural practices that are well known and tested in many projects (like the onion architecture) and start using tools like Nx to make our work simpler, faster, and more productive.
I will be grateful for some opinions about this article. Thanks for reading!