NestJS and Project Structure - What to Do?

smolinari

Scott Molinari

Posted on September 3, 2023

NestJS and Project Structure - What to Do?

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.

The Onion/ Clean -> Nest Architecture

You hopefully know about Onion Architecture, or Clean Architecture or you have a basic understanding of what Separation of Concerns is all about.

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? Borrowed from https://herbertograca.com/2017/11/16/explicit-architecture-01-ddd-hexagonal-onion-clean-cqrs-how-i-put-it-all-together/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.

Two Domains

As just mentioned, you have two main Domains to juggle as an app developer. Those two domains are the

  • "Developer Domain" - knowledge about how to make an app work in the framework and language in use, in our case Nest and TypeScript/JavaScript

and 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.

Domains, Features and Modules

As we noted earlier, as a beginner with Nest, you probably know your business domain much better than you know the development domain. 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.

New Nest app folder structure

app.controller.ts and 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 description
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..

Blog management:

  • Draft a blog
  • Publish a blog
  • Comment on a blog

And, what about user management?

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?

Auth Management:

  • Login a User
  • Logout a User
  • Register a User
  • Set permissions for a user

That is simple enough, right?

Creating the Onion's Layers

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


Enter fullscreen mode Exit fullscreen mode

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?

Image description

Image description

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 user and 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 blog folder.

Change into the blog folder and run the following commands:



nest generate module drafting
nest generate module publishing
nest generate module commenting


Enter fullscreen mode Exit fullscreen mode

You end up with the below image and again, the modules are automatically registered in blog.module.ts
Image description

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


Enter fullscreen mode Exit fullscreen mode

You now have this:

Image description

And again, the services are auto-registered into the auth module. Nice!

Image description

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


Enter fullscreen mode Exit fullscreen mode

Note the --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 auth.module.ts.

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


Enter fullscreen mode Exit fullscreen mode

And our guard is created. Note though, guards aren't automatically registered in your module.

Image description

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:

  1. You can basically take any module and reuse it somewhere else (with some modification). Code reuse for the win!

  2. 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.

  3. 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/draft folder 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.

  4. 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.

Hmm....Something is Missing!

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?"

Great question!

How about a common domain... uh...ehem...module?

Change directory up to the root and run:



nest generate module common


Enter fullscreen mode Exit fullscreen mode

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


Enter fullscreen mode Exit fullscreen mode

There you go!

Image description

Now you can tackle your problems logically.

Conclusion

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 user and 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 and if need be, break it out into its own library for use in any other of your apps.Awesome, right? 🙂

Additional Note 2: One of the things I see people often attempt, is to try and fit "DDD" into the file structure of a Nest project. Nest is flexible enough to allow for this, however, you end up with two problems.

  1. Actually fixing a problem requires sifting through those different layers of the file system you've determined to be "proper" for fitting your app to "DDD". Whereas, if you just keep the DDD within each module, as Nest prescribes, you just need to go to that one module to fix the problem.

  2. The bigger issue, you've broken up Nest's module system and thus the ability to reuse code/ modules as libraries. Maybe not a big deal for those just starting, but it will be when you realize you could take a lot of code and reuse it in new or other Nest projects.

To me, and this is my personal opinion, DDD should be a guide to how you think about your application, not necessarily how you design your file system/ project structure. When you come across a problem in the real world, either it being to enhance or to fix the application, nobody should need to rethink about the locality of code according to DDD to fix it. That is just going to waste a ton of time. Use Nest's "way of modularization". That is the standard you should work with. Keep DDD in your mind, when dealing with the overall problem and keep it away from the code structure. It just isn't necessary.

Additional Note II: I just ran into this article today. I think it helps make my points above.

https://medium.com/@stevebishop_89684/clean-architecture-is-not-a-project-structure-b158c9c4163f

💖 💪 🙅 🚩
smolinari
Scott Molinari

Posted on September 3, 2023

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related