top of page

SNIPPETS LTD.

Clean Architecture Application Structure In NestJS Framework

  • Writer: Pavol Megela
    Pavol Megela
  • May 10, 2022
  • 3 min read

Updated: Mar 8

Clean Architecture is amazing, it keeps your code organized, flexible, and easy to maintain. But when it comes to NestJS, figuring out the perfect folder structure might seem tricky at first. Let's simplify it together!


If you haven't heard about Clean Architecture yet, try this short introduction article Understanding Clean Architecture.


I'll go over 2 folder structures, one meant for Small Applications and one for Big Applications. Let's just get right to it.


Example #1: Small NestJS App Folder Structure

Here's a recommended Clean Architecture folder structure, in my opinion based on Clean Architecture principles, suited for smaller NestJS applications:

src/
  domain/
    entities/
      user.entity.ts
    value-objects/
      email.vo.ts

  application/
    services/
      user.service.ts
    ports/
      user.repository.interface.ts

  interface-adapters/
    controllers/
      user.controller.ts
    dtos/
      create-user.dto.ts
    mappers/
      user.mapper.ts
    guards/
    pipes/
    interceptors/

  infrastructure/
    repositories/
      user.typeorm.repository.ts
    database/
      migrations/
    config/
      config.ts

  app.module.ts
  main.ts

Understanding the Folder Structure

Here's a quick breakdown of the key files and their roles:

  • Domain/  (Domain layer for business logic)

    • entities/ Represents core domain objects that have a unique identity, like user.entity.ts. Entities can change over time and have an ID that differentiates them from other instances.

    • value-objects/ Represents immutable domain concepts that don't have an identity, like email.vo.ts. If two value objects have the same data, they are considered equal. Example: an Email value object ensures that an email address is always valid before being used in an entity.

  • Application/ (Application layer for use-cases)

    • services/ Contains application logic, like user.service.ts, handling user-related actions.

    • ports/ Defines contracts (user.repository.interface.ts) to abstract dependencies.

  • Interface-Adapters/ (Interface Adapters layer for connecting logic to the outside world)

    • controllers/ Manages your HTTP requests

    • dtos/ Data Transfer Objects or DTOs for validation and formatting

    • mappers/ Converts between domain entities and persistence models or DTOs, ensuring consistency when transferring data across layers

  • Infrastructure/ ( Infrastructure for technical implementation)

    • repositories/ Here you put the actual implementation of repositories, based on the contract defined in Application/Ports/

    • database/ Handles migrations and configurations

    • config/ Manages environment settings


Here's why I think this is good for small applications:

  • Clear Boundaries - Each folder clearly shows the role of the files inside, making it easy even for newcomers to quickly understand the application.

  • Easy to Navigate - Want to change how you save users? You immediately know to look into the infrastructure/repositories folder.

  • Smooth Scaling - As your app grows, you'll naturally know where new logic should go.

  • No Over-Engineering - The structure is simple, with no complications or extra folders.


Who Should Use This Folder Structure?

This is ideal for small to medium NestJS apps like CRUD APIs, internal tools, MVPs. It keeps things simple, structured, and easy to scale.


Example #2: Vertical Slice (Feature-Based) for Big NestJS Apps

src/
  user/
    domain/
      user.entity.ts
    value-objects/
      email.vo.ts
    rules/
      user.rules.ts
    application/
      use-cases/
        create-user.service.ts
        get-user.service.ts
      ports/
        user.repository.interface.ts
    interface-adapters/
      controllers/
        user.controller.ts
      dtos/
        create-user.dto.ts
      mappers/
        user.mapper.ts
      guards/
      pipes/
      interceptors/
    infrastructure/
      repositories/
        user.typeorm.repository.ts

  order/
    domain/
      order.entity.ts
    rules/
      order.rules.ts
    application/
      use-cases/
        create-order.service.ts
      ports/
        order.repository.interface.ts
    interface-adapters/
      controllers/
        order.controller.ts
      dtos/
        create-order.dto.ts
    infrastructure/
      repositories/
        order.typeorm.repository.ts

  infrastructure/
    database/
      migrations/
    config/
      config.ts
  main.ts
  app.module.ts

Understanding the Folder Structure

I don't want to repeat myself, since most of the files/folders are explained in upper section I just focus on what I added, and thats a Rules/ folder.

  • Domain/

    • Rules/ Contains domain logic that applies business policies but doesn't fit inside entities or value objects. These are often used for decisions involving multiple entities or complex conditions.


In this structure:

  • Feature-centric organization: All code for a feature (users, orders) lives together.

  • Clear responsibilities: Inside each feature, you keep the same layers as before, ensuring good architecture.

  • Easier growth: When you add new features, you simply add another folder, making your codebase scalable and clean.


One important thing to note is that not all modules, entities, or aggregates should be in separate folders. Some of them should be grouped together depending on business requirements and how closely they are related. For example, if User and UserProfile are tightly coupled and always change together, they might belong in the same module rather than separate ones. For deeper understanding on how to decide wether the module (aggregate) should be part of existing one or not check out this article Invariants and Aggregates in Domain-Driven Design.


One Size Fit All

While the folder structures shown in this article provide a strong foundation, they are not a one-size-fits-all solution. Every project has unique requirements, and it's essential to adapt the structure based on them. Even I don’t use this exact structure everywhere.


That's it

Choosing the right folder structure in NestJS can make a huge difference in code maintainability and scalability. For small applications, organizing by layers keeps things simple and structured. As your app grows, switching to a vertical slice approach ensures that features remain modular and easy to expand.

By applying Clean Architecture principles, you ensure that your NestJS project stays readable, testable, and adaptable no matter its size. The end.


Comentarios


bottom of page