Skip to content

ericknilson/modelo_nestjs_clean_arch

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

7 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

NestJS Clean Architecture

📋 Sobre o Projeto

Este projeto é uma API REST desenvolvida com NestJS aplicando os princípios da Clean Architecture (Arquitetura Limpa). O projeto demonstra como implementar uma arquitetura robusta, testável e escalável para aplicações Node.js, incorporando práticas de Domain-Driven Design (DDD) e SOLID.

🎯 Objetivos

  • Demonstrar a implementação da Clean Architecture em NestJS
  • Aplicar conceitos de Domain-Driven Design (DDD)
  • Criar uma estrutura de código testável e manutenível
  • Implementar autenticação JWT
  • Usar Prisma como ORM
  • Configurar testes automatizados (unitários, integração e e2e)

🏗️ Arquitetura

O projeto segue a estrutura da Clean Architecture, dividida em camadas bem definidas:

┌─────────────────────────────────────────────────────────────┐
│                    Interface Layer                          │
│              (Controllers, DTOs, Presenters)               │
├─────────────────────────────────────────────────────────────┤
│                   Application Layer                         │
│               (UseCases, Services, DTOs)                   │
├─────────────────────────────────────────────────────────────┤
│                     Domain Layer                            │
│           (Entities, Repositories, Validators)             │
├─────────────────────────────────────────────────────────────┤
│                 Infrastructure Layer                        │
│         (Database, External Services, Guards)              │
└─────────────────────────────────────────────────────────────┘

Princípios Aplicados

  1. Dependency Inversion: As camadas internas não dependem das externas
  2. Single Responsibility: Cada classe tem uma única responsabilidade
  3. Open/Closed: Aberto para extensão, fechado para modificação
  4. Interface Segregation: Interfaces específicas para cada necessidade
  5. Liskov Substitution: Subtipos devem ser substituíveis por seus tipos base

📁 Estrutura do Projeto

src/
├── main.ts                     # Ponto de entrada da aplicação
├── app.module.ts              # Módulo principal
├── global-config.ts           # Configurações globais
│
├── shared/                    # Código compartilhado
│   ├── application/           # Camada de aplicação compartilhada
│   │   ├── dtos/             # Data Transfer Objects
│   │   ├── errors/           # Erros customizados da aplicação
│   │   ├── providers/        # Interfaces de provedores
│   │   └── usecases/         # Interface base para casos de uso
│   │
│   ├── domain/               # Camada de domínio compartilhada
│   │   ├── entities/         # Entidades base
│   │   ├── errors/           # Erros de domínio
│   │   ├── repositories/     # Contratos e implementações base
│   │   └── validators/       # Validadores de domínio
│   │
│   └── infrastructure/       # Camada de infraestrutura compartilhada
│       ├── database/         # Configuração do banco de dados
│       ├── env-config/       # Configuração de ambiente
│       ├── exception-filters/ # Filtros de exceção
│       ├── interceptors/     # Interceptadores
│       └── presenters/       # Apresentadores de dados
│
├── users/                    # Módulo de usuários
│   ├── application/          # Casos de uso dos usuários
│   │   ├── dtos/            # DTOs específicos de usuários
│   │   └── usecases/        # Casos de uso (signup, signin, etc.)
│   │
│   ├── domain/              # Domínio dos usuários
│   │   ├── entities/        # Entidade User
│   │   ├── repositories/    # Contrato do repositório de usuários
│   │   └── validators/      # Validadores de usuário
│   │
│   └── infrastructure/      # Infraestrutura dos usuários
│       ├── users.controller.ts  # Controlador REST
│       ├── users.module.ts     # Módulo NestJS
│       ├── database/           # Implementação do repositório
│       ├── dtos/              # DTOs da API
│       └── presenters/        # Apresentadores de resposta
│
└── auth/                     # Módulo de autenticação
    └── infrastructure/       # Implementação de autenticação
        ├── auth.guard.ts     # Guard JWT
        ├── auth.module.ts    # Módulo de autenticação
        └── auth.service.ts   # Serviço de autenticação

🔧 Tecnologias Utilizadas

Core

  • Node.js 18+
  • NestJS 9 - Framework web
  • TypeScript - Linguagem de programação
  • Fastify - Servidor HTTP (mais performático que Express)

Banco de Dados

  • Prisma - ORM moderno para TypeScript
  • PostgreSQL - Banco de dados relacional

Autenticação e Segurança

  • JWT (JSON Web Tokens) - Autenticação stateless
  • bcryptjs - Hash de senhas

Validação e Transformação

  • class-validator - Validação de DTOs
  • class-transformer - Transformação de objetos

Testes

  • Jest - Framework de testes
  • Supertest - Testes de integração HTTP

Documentação

  • Swagger/OpenAPI - Documentação automática da API

DevOps

  • Docker - Containerização
  • Docker Compose - Orquestração de containers

🚀 Como Executar

Pré-requisitos

  • Node.js 18+
  • Docker e Docker Compose
  • npm ou yarn

Instalação

  1. Clone o repositório
git clone <repository-url>
cd modelo_nestjs_clean_arch
  1. Instale as dependências
npm install
  1. Configure as variáveis de ambiente
# Crie os arquivos de ambiente
cp .env.example .env.development
cp .env.example .env.test
cp .env.example .env
  1. Inicie o banco de dados
docker-compose up -d
  1. Execute as migrações
npx prisma migrate dev
  1. Inicie a aplicação
npm run start:dev

A aplicação estará disponível em http://localhost:3000

📚 Scripts Disponíveis

Desenvolvimento

npm run start:dev    # Inicia em modo desenvolvimento
npm run start:debug  # Inicia em modo debug
npm run build        # Gera build de produção
npm run start:prod   # Inicia em modo produção

Testes

npm run test         # Executa todos os testes
npm run test:unit    # Executa testes unitários
npm run test:int     # Executa testes de integração
npm run test:e2e     # Executa testes end-to-end
npm run test:cov     # Executa testes com coverage
npm run test:watch   # Executa testes em modo watch

Qualidade de Código

npm run lint         # Executa ESLint
npm run format       # Formata código com Prettier

🏛️ Detalhamento das Camadas

1. Domain Layer (Camada de Domínio)

A camada mais interna, contendo as regras de negócio puras.

Entities (Entidades)

// src/shared/domain/entities/entity.ts
export abstract class Entity<Props = any> {
  public readonly _id: string
  public readonly props: Props

  constructor(props: Props, id?: string) {
    this.props = props
    this._id = id || randomUUID()
  }
  // ...
}

User Entity

// src/users/domain/entities/user.entity.ts
export class UserEntity extends Entity<UserProps> {
  constructor(public readonly props: UserProps, id?: string) {
    UserEntity.validate(props)
    super(props, id)
    this.props.createdAt = this.props.createdAt ?? new Date()
  }

  update(value: string): void {
    UserEntity.validate({ ...this.props, name: value })
    this.name = value
  }
  // ...
}

Repository Contracts

// src/users/domain/repositories/user.repository.ts
export namespace UserRepository {
  export interface Repository
    extends SearchableRepositoryContract<
      UserEntity,
      UserFilter,
      SearchParams,
      SearchResult
    > {
    findByEmail(email: string): Promise<UserEntity>
    emailExists(email: string): Promise<void>
  }
}

2. Application Layer (Camada de Aplicação)

Contém os casos de uso e regras de aplicação.

Use Cases

// src/users/application/usecases/signup.usecase.ts
export namespace SignupUseCase {
  export type Input = {
    name: string
    email: string
    password: string
  }

  export type Output = UserOutput

  export class UseCase implements DefaultUseCase<Input, Output> {
    constructor(
      private userRepository: UserRepository.Repository,
      private hashProvider: HashProvider,
    ) {}

    async execute(input: Input): Promise<Output> {
      // Validações
      // Regras de negócio
      // Persistência
    }
  }
}

3. Infrastructure Layer (Camada de Infraestrutura)

Implementa os detalhes técnicos e integrações externas.

Controllers

// src/users/infrastructure/users.controller.ts
@ApiTags('users')
@Controller('users')
export class UsersController {
  @Inject(SignupUseCase.UseCase)
  private signupUseCase: SignupUseCase.UseCase

  @Post()
  async create(@Body() signupDto: SignupDto) {
    const output = await this.signupUseCase.execute(signupDto)
    return UsersController.userToResponse(output)
  }
  // ...
}

🔐 Autenticação

O sistema utiliza JWT (JSON Web Tokens) para autenticação:

Login

POST /users/login
{
  "email": "[email protected]",
  "password": "password123"
}

Rotas Protegidas

@UseGuards(AuthGuard)
@ApiBearerAuth()
@Get('me')
async profile() {
  // Rota protegida
}

🧪 Estratégia de Testes

Configuração de Testes

O projeto possui configurações separadas para diferentes tipos de teste:

  • jest.unit.config.ts - Testes unitários
  • jest.int.config.ts - Testes de integração
  • jest.e2e.config.ts - Testes end-to-end

Tipos de Teste

1. Testes Unitários

Testam unidades isoladas (entities, use cases, services):

describe('UserEntity', () => {
  it('should create a user with valid data', () => {
    const userData = {
      name: 'John Doe',
      email: '[email protected]',
      password: 'password123'
    }

    const user = new UserEntity(userData)

    expect(user.name).toBe('John Doe')
    expect(user.email).toBe('[email protected]')
  })
})

2. Testes de Integração

Testam a integração entre camadas:

describe('SignupUseCase', () => {
  it('should create a new user', async () => {
    const repository = new InMemoryUserRepository()
    const hashProvider = new BcryptjsHashProvider()
    const useCase = new SignupUseCase.UseCase(repository, hashProvider)

    const result = await useCase.execute({
      name: 'John Doe',
      email: '[email protected]',
      password: 'password123'
    })

    expect(result.name).toBe('John Doe')
  })
})

3. Testes E2E

Testam fluxos completos da API:

describe('/users (e2e)', () => {
  it('should create a user', async () => {
    return request(app.getHttpServer())
      .post('/users')
      .send({
        name: 'John Doe',
        email: '[email protected]',
        password: 'password123'
      })
      .expect(201)
  })
})

📊 Monitoramento e Logs

Interceptors

// Wrapper para padronizar responses
@UseInterceptors(WrapperDataInterceptor)

Exception Filters

// Tratamento centralizado de erros
@UseFilters(ConflictErrorFilter)
@UseFilters(NotFoundErrorFilter)

🔄 Fluxo de Desenvolvimento

Implementando um Novo Módulo

  1. Criar a Entidade de Domínio
// domain/entities/product.entity.ts
export class ProductEntity extends Entity<ProductProps> {
  // Implementar regras de negócio
}
  1. Definir o Contrato do Repositório
// domain/repositories/product.repository.ts
export namespace ProductRepository {
  export interface Repository {
    // Definir métodos necessários
  }
}
  1. Implementar os Casos de Uso
// application/usecases/create-product.usecase.ts
export namespace CreateProductUseCase {
  export class UseCase {
    // Implementar lógica do caso de uso
  }
}
  1. Criar o Controlador
// infrastructure/product.controller.ts
@Controller('products')
export class ProductController {
  // Implementar endpoints
}
  1. Implementar o Repositório
// infrastructure/database/prisma/product-prisma.repository.ts
export class ProductPrismaRepository implements ProductRepository.Repository {
  // Implementar persistência
}
  1. Configurar o Módulo
// infrastructure/product.module.ts
@Module({
  providers: [
    // Configurar providers
  ],
  controllers: [ProductController]
})
export class ProductModule {}

🌐 API Endpoints

Usuários

Método Endpoint Descrição Auth
POST /users Criar usuário
POST /users/login Login
GET /users Listar usuários
GET /users/:id Buscar usuário
PUT /users/:id Atualizar usuário
PATCH /users/:id Atualizar senha
DELETE /users/:id Deletar usuário

Documentação

  • Swagger UI: http://localhost:3000/api
  • Documentação automática de todos os endpoints
  • Esquemas de request/response
  • Autenticação JWT integrada

🐳 Docker

Desenvolvimento

# Iniciar apenas o banco
docker-compose up -d

# Build da aplicação
docker build -t nestjs-clean-arch .

Produção

# Dockerfile multi-stage para otimização
FROM node:18-alpine AS builder
# ... build steps

FROM node:18-alpine AS production
# ... production setup

📈 Boas Práticas Implementadas

1. SOLID Principles

  • S: Cada classe tem uma responsabilidade específica
  • O: Código aberto para extensão, fechado para modificação
  • L: Subtipos são substituíveis pelos tipos base
  • I: Interfaces segregadas e específicas
  • D: Dependência de abstrações, não de implementações

2. Clean Architecture

  • Separação clara de responsabilidades
  • Dependências apontam para dentro
  • Regras de negócio isoladas
  • Facilita testes e manutenção

3. DDD (Domain-Driven Design)

  • Entities com comportamento
  • Value Objects para conceitos sem identidade
  • Repositories como contratos
  • Services para operações complexas

4. Error Handling

  • Erros customizados por camada
  • Exception filters centralizados
  • Responses padronizados

5. Validation

  • DTOs com class-validator
  • Validação na entrada da aplicação
  • Validação de domínio nas entities

🔍 Troubleshooting

Problemas Comuns

  1. Erro de Conexão com Banco
# Verificar se o container está rodando
docker-compose ps

# Recriar o container
docker-compose down && docker-compose up -d
  1. Erro de Migração
# Reset do banco (desenvolvimento)
npx prisma migrate reset

# Aplicar migrações
npx prisma migrate dev
  1. Erro de Dependências
# Limpar cache e reinstalar
rm -rf node_modules package-lock.json
npm install

🤝 Contribuindo

  1. Fork o projeto
  2. Crie uma branch para sua feature (git checkout -b feature/AmazingFeature)
  3. Commit suas mudanças (git commit -m 'Add some AmazingFeature')
  4. Push para a branch (git push origin feature/AmazingFeature)
  5. Abra um Pull Request

📄 Licença

Este projeto está sob a licença UNLICENSED - veja o arquivo LICENSE para detalhes.

👥 Autores

🙏 Agradecimentos

  • NestJS Team
  • Clean Architecture Community
  • Prisma Team
  • Contribuidores Open Source

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages