Understanding NestJS’s Business Logic Organization
If you come from a traditional MVC architecture, NestJS may seem foreign. Sure, the idea of a separation of concerns between a view (UI) layer, model (database) layer, and controller (business logic) exists. However, with NestJS we are introduced to new flows for structuring our files and business logic. Instead of a controller as a full-service router and business logic handler, NestJS uses a pattern of a controller for routing requests, a service for handling business logic, and a repository for manipulating the database. Let’s discuss how to organize your business logic in NestJS.
A New Way to Handle Requests
NestJS is a configuration-based back-end framework. It is written in JavaScript but shines when using TypeScript. It differs from the Wild West approaches of basic NodeJS with Express, and the strict convention-based approach of Ruby on Rails. NestJS provides patterns or recipes to allow for flexible design while still forming best practices for developing applications.
The setup of a NestJS project is centered on the orchestration of modules. All logic is wrapped in modules that either provide functionality or consume it. As with any other software, we have an entry point, and then a web of dependencies:
NestJS can get complex as we share services and repositories across modules to handle user requests. It’s better to start simple with an isolated module containing a controller, service, and repository. Let’s discuss how a database-altering request is routed through a NestJS project.
What does a Controller do in NestJS?
It all starts with the controller. In a classic MVC project, the controller does a lot of the heavy lifting for the project. The view is a reflection of data returned from the server. The model gives data structure and stores some business logic related to records, and the controller orchestrates everything in between.
Controllers have some of the same functionality in NestJS but have a lot less responsibility. A controller functions simply as a traffic director, seeing what needs to go where and telling it how to get there. Sure, you may add more responsibilities like pipes for request validation, or DTOs (data transfer objects) to shape and validate incoming data, but controllers do not handle the business logic.
In NestJS, a controller’s job is to accept a request, perform some validation, and route the request and data to the correct service. This is still a lot of responsibility, but less than other frameworks may require.
What does a Service do in NestJS?
Once we know where the request and optional data are going, we shape it as our business logic dictates. This is where our service files come in. A service in NestJS is the logic that creates, shapes, or updates data based on our request from our controller. Maybe we are creating a new order for someone who bought a pair of shoes. Or, we are removing a user’s outdated payment method.
Whatever it may be, it is in the service that we perform the operations on our application data to allow us to persist the new state. While a service shapes the data, it is not responsible for talking to the database to persist this new state. To persist the data we need the repository.
What does a Repository do in NestJS?
The repository handles requests to a database. Once a user’s request has been routed to the correct business logic (by the controller) and the appropriate operations have been performed on the data (by the service), we are ready to persist the updated application state.
The repository consists of CRUD operations and could be seen as a similar tool to an API client. Methods on the repository expect to read or write specific data records to the database. That is it. This differs from other approaches with ORMs where the calls to a database would be woven into the service. While having extra files may seem like extra work, having separate repository files allows for sharing database operations between services or apps, modularizing your code for extension.
Final Thoughts on NestJS
NestJS is a powerful back-end framework that expedites the process of building APIs. There is a lot to learn coming from a Ruby on Rails or Java background. Luckily, the docs are extensive and easy to follow. I highly recommend reviewing them more to ensure you write, clean and conventional code.