So, you're just getting going with Nest, ey? You might not yet completely understand why you want to use Nest or why it is one of the most popular NodeJS frameworks on the planet. But, you are saying for sure, "if this many people are using it, it must be good", right?
Well, this article will get you a few steps closer to that better understanding of why you've chosen Nest.
And, some other knowledge you should have is a rough idea of what Domain Driven Design is. Especially what a "domain" is (and it's not about the names of websites).
Check out this image.
Puh! Overwhelming, right? Image borrowed from herbertograca.com
Don't get too caught up in this image, but yeah, this is practically everything you have to juggle to make sure a full stack application will work (in any language, with any framework).
And, Nest is only a part of it. The "core" part to be exact.
Since Nest is the core part of your system, it also covers some of the "layers" of the architecture for your stack, namely:
- the communication layer via controllers for REST/ resolvers for GraphQL or Gateways for Websockets (and a lot more for microservices)
- the database abstraction layer with ORM repositories or ODM models or Prisma clients
- the business logic layer with services
- the access control layer with guards
- the data validation and transformation layer with pipes
- a CQRS/ Bus system
- presentation layer too, if you go for MVC architecture
..and a good bit more.
The point being, your Nest application can do a whole lot of things, and wondering how to put all of those things in their right places AND also somehow model the overall business domain problems the application needs to help solve, is a daunting task. At the very beginning, very often the question arises - How do I structure my Nest application?
This question arises usually because you are just learning Nest. And yet, for sure, you have a fairly clear idea about what your app should do.
..and that is a really good thing, because we can take advantage of that to build the Nest application!
But for now, just keep in mind that Nest will build the final "onion" or "clean" architecture for you. It's one of the reasons why Nest has its module system (and why you need to add all this new verbosity to your app) and also why Nest is so popular. It's kind of a paradox, in a way.
As just mentioned, you have two main Domains to juggle as an app developer. Those two domains are the
- "Business Domain" - knowledge about what the app should do to solve your business problems
When you write code with Nest, you have to fit these two domains together, when in fact, they really don't fit together in any way at all. Again, the paradox....
So, it's at this point the questions arise...
Do I organize my code to the domain of creating the code and the framework and somewhere inside, I'll be able to put in the business logic?
- or -
Do I try to model my business domain and figure out how to fit in the development domain into that?
The latter should be the one you choose and Nest's module system simplifies it for you.
As we noted earlier, as a beginner with Nest, you probably know your business domain much better than you know the development domain, particularly working with Nest. So, in that sense, why not start with what you know best?
Let's move forward with a new Nest app.
If you haven't already, install the Nest global CLI and create a new project too.
app.service.ts are just examples to give you a running app. You can delete them. Keep
app.module.ts though. That is your "root" module.
Now, let's take your business domain and work with it right now. Let's say you want to build a blog application like Wordpress, but it will be the next best thing. It will be even bigger and better than Wordpress! Haha! We all want world domination, right!
IMAGE CREDIT: DOOMSTEADDINER.ORG
Ok. Ok. Maybe not. But for sure, you do want your business domain problems solved. So, either way, you definitely know what you want. For our example, it's a blog application.
Ask yourself, what are the overall "features" you might want to give your blog application? We'll start relatively simple of course. How about..
- Draft a blog
- Publish a blog
- Comment on a blog
And, what about user management?
- Create a User
- Remove a User
- Edit a User
- Show users
And lastly, what about getting the users into the app with the right permissions?
- Login a User
- Logout a User
- Register a User
- Set permissions for a user
That is simple enough, right?
Now you can use the Nest CLI's commands to build your app.
Let's create the high level "management" domains. Inside your project folder, run these commands:
nest generate module blog nest generate module user nest generate module auth
Notice three things have happened.
- You have folders with the names of these domains
- You have modules TS files with those names
- (the kicker) the modules are already imported into your
app.modules.ts. How cool is that?
Now could be a good time to think even farther into the future. You could continue with the module creation process for the management features, but it might be overkill depending on what must actually be done in terms of code to make the features a reality.
You think the
auth modules will be fairly straight forward, so you don't continue with adding modules there. But with with
blog, hmmm.... you aren't so certain. It could get hairy and more complex, especially the commenting part. So, you carry on inside the
Change into the
blog folder and run the following commands:
nest generate module drafting nest generate module publishing nest generate module commenting
Let's move on to the
auth module.. ehem...domain, um...management. (See how this DDD stuff is getting in here?)
Ok. We only need the three processes. Login, Logout and Register. Theoretically, we only see these being three methods, but the methods might get somewhat large. So, we decide to make singular classes for each process. Change into the
auth directory and run the following command:
nest generate service login nest generate service logout nest generate service register
You now have this:
And again, the services are auto-registered into the
auth module. Nice!
Hopefully by now, you are catching on.
But, we need more things to make the
auth feature work. How about a controller to get the calls routed to the service? Controllers should be small, so you know you can fit them all in one class.
nest generate controller auth --flat
--flat argument. It tells Nest not to create a folder named
auth with the controller in it. It puts the controller file in the current directory. And once more, the controller is automatically registered in
Ok. We have our controller, what now?
We've learned about guards, and thus know we'll also need a guard of some sort to protect certain other paths in our future API. Let's create that.
nest generate guard auth --flat
And our guard is created. Note though, guards aren't automatically registered in your module.
We can continue on with even more creation of Nest providers, like adding the ORM files (repositories, entities), creating classes for DTOs, pipes for validation and a whole bunch of other stuff, but you are more than likely getting the idea by now.
The important part to note with this exercise is, we are putting all the layers of the architecture in a module (a mini-onion). When compiling, Nest aggregates all of the modules to create the finished application (the big final kick-ass onion).
There are some very nice advantages to this module system:
You can basically take any module and reuse it somewhere else (with some modification). Code reuse for the win!
You get a powerful dependency injection system. We didn't even start on that subject, but such a system is hugely important for enterprise grade code bases and at some point for your sanity.
When you have an issue with a feature, you can quickly find the code you need to work on. For instance, let's say you have a problem with validation of a blog input. You go to the
/blog/draftfolder and there you'll have either the DTO you need defining the validation rules or the pipe that works the validation. All together. No searching through tons of files to find the right pieces of the puzzle.
Lastly, Nest's modularization pushes you more into the direction of a Service Oriented kind of thinking. That, in turn, will help you make the jump to microservices easier. Remember, we wanted world domination! Right? Haha!
Joking aside. Basically, you could say, Nest tries to get out of your way as a dev, by making you follow a process of modularization, where Nest is sort of secondary. It is a means to an end, but your domain problem is the first class citizen (or rather it should be). Your business problems at hand should determine how you build the app. Nest just adds the neat tools to make it happen.
You might be saying, "wait a sec! What about those things in an application, which don't belong to any business domain, but only to the developer domain? Things like connecting to a database -or- generating the API via Swagger -or- configuration?"
How about a
common domain... uh...ehem...module?
Change directory up to the root and run:
nest generate module common
Let's also create some services for the things mentioned above. Change back down to the
common folder and run:
nest generate module database nest generate module swagger nest generate module config
There you go!
Now you can tackle your problems logically.
I hope this got you to a better understanding of why Nest's module system is so potent for your work as a developer. There are a good number of other features in Nest that make it a pleasure to work with. Yes, it seems verbose at the beginning. But, now you know why. And once you use Nest more and more, you'll wonder how you did your backend applications without it before.
Enjoy Nest and have fun!
Additional Note: After helping someone in the Nest Discord server once more about project structure, I realized one more advantage to Nest's modules. In our blog example, we had higher level
auth modules. In effect though, they could be considered "things I would always need in any app". I mentioned code reuse, and if you know these modules would always be added to any app together, you might want to end up with a "core" module holding such other modules like "auth", "authz" and "user". The cool things being (and the advantage) you can refactor this fairly easily. Create a "Core" module and move the folders and register them in the module as imports. Done. 🙂