- pnpm - The Best Package Manager.
- Nest.js - The Best Node.js Framework.
- MikroORM - The Best TypeScript ORM for Node.js.
(yes, this is strongly opinionated 😉 but it's OK if you don't like it, you can do you own cooking here...)
$ pnpm install
$ pnpm start:dev
$ pnpm test
Have a look at the E2E test as they help clarify the architecture:
$ pnpm test:e2e
The goal of this project is to demonstrate how to use the Onion (or similar) Architecture with the Nest.js framework. To read more about my explanations on this architecture, head over to my blog post.
This example was bootstrapped from the official Nest.js Advanced Course courses but will deviate over time toward a more "real-life" example. Still, to dive deeper and get more hands-on experience just go there and buy their courses, they are excellent, and they explore many other concepts and architectures like CQRS, Event Sourcing, etc., that are not covered at all in this repository. 💡
Onion, Hexagonal, and Clean Architecture are commonly referred to as "Ports and Adapters Architecture", "Layered Architecture with Dependency Inversion", or "Concentric Architecture". While mostly similar, they differ in subtle ways. For the sake of naming, I have used Onion Architecture most of the time as I feel like that specification is easier to grasp.
Design your code to interact with an abstract definition of behavior (an interface) rather than a specific implementation.
- Dependencies always point inward (we visualize these architectures as a circle[^1] with the domain at the center)
- Inner layers know nothing about outer layers
- Business/Domain logic is isolated from external concerns[^2]
- High-level modules don't depend on low-level modules
- Both depend on abstractions
- Abstractions don't depend on details
- Clear boundaries between different responsibilities
- Core business/domain logic at the center
- Infrastructure and UI concerns at the outer layers
I can't answer that question for you. I think a good approach is to start with a standard layered architecture, which at some point might lead to some bloating in services, or some headache like a tight coupling between the controllers and services, and the services and the repositories. At that point, you might want to come back here and start applying some abstractions, for example you might want to:
- Integrate UseCases instead of calling your services directly.
- Create QueryObjects instead of calling your repositories for everything.
- Make sure the Services and UseCases are not aware of the data-access details by abstracting the Repositories, EntityManager, or any ORM related classes from them.
- Ensure Services don't receive anything from the HTTP layer and don't return HttpExceptions.
- Ensure Services are unaware of the HTTP Authentication mechanisms (JWT, etc.). Often, to test ownership, developers have the tendency to pass the Authentication context to the Services. This creates a strong coupling.
- Etc... many more things to think about, but if you apply the core principles, they should become obvious.
There are 4 mains "folders":
- application
- domain
- infrastructure
- presenter
This is the application layer.
Basically, only POJO, containing the Business Logic for the creation, and modification of entities.
To simplify, this is the data-access layer. Any interaction with a third party, or external system should be abstracted here.
This layer manage the input/output of different presenter. A presenter is a user interface. For example, a HTTP interface, or a CLI tool.
[^1] Yes, Hexagonal Architecture uses a hexagon, but the principle is identical. The hexagon actually made it confusing.
[^2] Basically, they are just POJO, not even linked to Nest.js.