As informações apresentadas nesta página são complementares às diretrizes de desenvolvimento de software do Ministério da Saúde.
Este documento tem como objetivo apresentar uma proposta de arquitetura modular para padronização de projetos em NestJS. A proposta de arquitetura aqui apresentada tem como objetivo garantir a qualidade e a consistência dos projetos desenvolvidos com NestJS, através da criação de módulos independentes que podem ser facilmente adicionados ou removidos de um projeto.
A arquitetura proposta tem como base a separação de responsabilidades em módulos independentes. Cada módulo é responsável por uma área específica do sistema e pode ser facilmente adicionado ou removido de um projeto, sem afetar o restante do sistema. A seguir, são apresentados os módulos e suas responsabilidades:
A camada de apresentação é responsável por receber as requisições e enviar as respostas. Neste módulo, é possível utilizar o NestJS para criar controllers e rotas para as APIs.
A camada de serviço é responsável por implementar as regras de negócio. Neste módulo, é possível utilizar os providers do NestJS para injetar dependências e utilizar serviços externos.
O módulo de persistência é responsável por realizar a comunicação com o banco de dados. Neste módulo, é possível utilizar bibliotecas como por exemplo TypeORM para criar entidades e repositórios.
A injeção de dependências é um padrão de projeto que pode ser utilizado para criar módulos independentes. Neste padrão, cada módulo pode depender de outros módulos e ser dependente de outros módulos, sem que haja acoplamento entre eles.
Os microserviços são um padrão de projeto que podem ser utilizados para criar módulos independentes que podem ser executados em máquinas diferentes. Neste padrão, cada módulo é responsável por uma funcionalidade específica e se comunica com os demais módulos através de uma interface definida.
src/
├── main.ts
├── app.module.ts
├── shared/ (módulo com funcionalidades comuns a diversos módulos)
│ ├── shared.module.ts
│ ├── controllers/ (camada de apresentação - controllers)
│ ├── services/ (camada de serviço - providers)
│ ├── repositories/ (repositórios de persistência - providers)
│ ├── models/
│ ├── schemas/ (schemas do MongoDB)
│ ├── entities/ (entidades do TypeORM)
│ ├── dtos/ (data transfer objects)
│ ├── enums/
│ └── interfaces/ (interfaces de comunicação com outros módulos)
├── modules/
│ ├── user/ (módulo de usuário)
│ │ ├── presentation.module.ts
│ │ ├── controllers/
│ │ ├── services/
│ │ ├── repositories/
│ │ ├── models/
│ │ ├── schemas/
│ │ ├── entities/
│ │ ├── dtos/
│ │ ├── enums/
│ │ └── interfaces/
│ ├── product/ (módulo de produto)
│ │ ├── presentation.module.ts
│ │ ├── controllers/
│ │ ├── services/
│ │ ├── repositories/
│ │ ├── models/
│ │ ├── schemas/
│ │ ├── entities/
│ │ ├── dtos/
│ │ ├── enums/
│ │ └── interfaces/
│ ├── category/ (módulo de categoria)
│ │ ├── presentation.module.ts
│ │ ├── controllers/
│ │ ├── services/
│ │ ├── repositories/
│ │ ├── models/
│ │ ├── schemas/
│ │ ├── entities/
│ │ ├── dtos/
│ │ ├── enums/
│ │ └── interfaces/
│ └── ... (outros módulos)
└── ...
Para garantir que a aplicação seja segura e protegida contra ataques maliciosos. Aqui estão algumas boas práticas de segurança que devem ser seguidas:
Utilizar protocolos como HTTPS, criptografia e autenticação para proteger a aplicação e os dados transmitidos.
Validar todas as entradas de dados para prevenir ataques de injeção de código malicioso ou exploração de vulnerabilidades.
Armazenar todas as credenciais e senhas de maneira segura e criptografada e utilizar ferramentas de gestão de senhas para facilitar a gestão das mesmas.
Usar cookies seguros e gerenciar a expiração das sessões de usuários para evitar ataques de sessão.
Utilizar bibliotecas confiáveis e manter todas as bibliotecas atualizadas para evitar vulnerabilidades conhecidas.
Implementar boas práticas de autenticação, como o uso de senhas fortes e autenticação multifator para garantir que apenas usuários autorizados possam acessar a aplicação.
Implementar medidas de proteção de dados, como criptografia de dados armazenados, backups regulares e restrição de acesso a dados confidenciais.
Realizar testes regulares de segurança para identificar e corrigir vulnerabilidades na aplicação.
As convenções e padrões são importantes em um projeto por diversos motivos:
Legibilidade e manutenção do código: quando todos os desenvolvedores seguem as mesmas convenções e padrões, o código se torna mais fácil de ser lido, compreendido e mantido. Isso é especialmente importante quando há vários desenvolvedores trabalhando em um mesmo projeto.
Redução de erros: seguir as convenções e padrões reduz a probabilidade de erros no código, pois o desenvolvedor sabe exatamente onde encontrar as informações necessárias e como implementar uma funcionalidade de forma consistente.
Aumento da produtividade: seguir convenções e padrões permite que os desenvolvedores se concentrem na lógica do negócio, em vez de gastar tempo em questões triviais de formatação e estruturação do código.
Facilita a integração de novos desenvolvedores: quando um novo desenvolvedor se junta ao projeto, ele pode se adaptar mais facilmente ao código existente, pois segue as mesmas convenções e padrões que os outros membros da equipe.
Melhora a qualidade do código: seguir convenções e padrões ajuda a garantir que o código esteja bem organizado, fácil de entender e seguro.
Ajuda na manutenção e evolução do projeto: a aplicação de convenções e padrões facilita a manutenção do projeto no longo prazo, bem como a evolução do código para atender a novos requisitos e funcionalidades.
Em resumo, as convenções e padrões são importantes em um projeto porque tornam o código mais legível, consistente, fácil de manter e evoluir. Além disso, ajudam a melhorar a qualidade do código e a reduzir a probabilidade de erros.
Nomes de classes devem ser escritos em PascalCase. Por exemplo: UserController, AuthService, OrderService.
Nomes de métodos e variáveis devem ser escritos em camelCase. Por exemplo: getUserById(), createOrder().
Os nomes de propriedades devem ser escritos em camelCase. Por exemplo: firstName, age, address.
Para serviços, é recomendado que o nome termine com o sufixo Service. Por exemplo: UserService, OrderService.
Para controladores, é recomendado que o nome termine com o sufixo Controller. Por exemplo: UserController, OrderController.
Para repositórios, é recomendado que o nome termine com o sufixo Repository. Por exemplo: UserRepository, OrderRepository.
Os nomes dos arquivos devem ser escritos em kebab-case (separados por traço). Por exemplo: user.service.ts, order.controller.ts, user-profile.repository.ts.
Os nomes dos arquivos devem refletir o nome da classe contida no arquivo. Por exemplo, a classe UserService deve estar definida no arquivo user.service.ts.
organização de código é fundamental para criar projetos NestJS que sejam fáceis de manter e escaláveis. Algumas práticas recomendadas para organizar o código incluem:
Separar as responsabilidades em módulos para facilitar a manutenção e escalabilidade do projeto.
Utilizar a injeção de dependência para gerenciar as dependências entre os módulos e reduzir o acoplamento.
Separar a lógica de negócios da aplicação da lógica de entrada e saída, utilizando as classes service e controller, respectivamente.
Separar a lógica de acesso aos dados da aplicação em classes repository.
Utilizar as classes DTO para definir o formato de entrada e saída dos dados em uma solicitação HTTP.
Utilizar classes entity para definir os modelos de dados utilizados pela aplicação.
Utilizar a biblioteca class-validator para validar as entradas recebidas em uma solicitação HTTP.
Utilizar a biblioteca TypeORM ou Mongoose para implementar a lógica de acesso aos dados da aplicação.
Utilizar a biblioteca Passport para implementar a autenticação e autorização na aplicação.
Utilizar as classes guard para implementar regras de autorização e proteção de rotas.
Além dessas práticas, é recomendado utilizar uma ferramenta de linting, como o ESLint, para manter um código limpo e consistente em todo o projeto. Também é importante seguir as boas práticas do JavaScript, como evitar o uso de variáveis globais, evitar o aninhamento excessivo de callbacks e utilizar constantes para valores fixos.
A controller deve ser projetada de forma a fornecer uma interface consistente e organizada para a lógica de negócios da aplicação. A controller é responsável por gerenciar as solicitações de entrada e saída da aplicação e deve ser implementada de forma a ser escalável, fácil de entender e manter.
Aqui estão alguns princípios e padrões que devcem ser seguidos para criar uma controller eficiente:
Cada controller deve ter uma única responsabilidade, gerenciando apenas um conjunto específico de solicitações. Isso torna a controller mais fácil de entender e manter.
As rotas e os métodos da controller devem seguir as convenções de nomenclatura e do HTTP. Isso torna a controller fácil de entender e utilizar para outros desenvolvedores.
A controller deve utilizar a injeção de dependência para obter serviços e outros recursos necessários para processar solicitações. Isso torna a controller mais modular e mais fácil de testar.
A controller deve tratar adequadamente os erros e retornar mensagens de erro significativas para o cliente. Isso torna a aplicação mais resiliente e mais fácil de depurar.
A controller deve ser uma camada fina que delega a lógica de negócios para outros serviços. Isso mantém a controller focada em sua responsabilidade principal e torna a aplicação mais escalável e fácil de manter.
A controller deve validar adequadamente as entradas de solicitação antes de processá-las. Isso ajuda a prevenir erros e torna a aplicação mais segura.
A controller deve seguir as boas práticas de segurança recomendadas e pelo protocolo HTTP. Isso torna a aplicação mais segura e ajuda a prevenir vulnerabilidades de segurança.
Em resumo, a controller deve ser focada, modular e seguir as convenções e boas práticas recomendadas e pelo protocolo HTTP. Isso torna a controller mais fácil de entender e manter, além de tornar a aplicação mais escalável, segura e resiliente.
Toda entrada de dados deve ser validada usando o pacote class-validator. Isso ajuda a garantir que os dados de entrada sejam válidos e seguros.
Uma service deve ser projetada de forma a implementar a lógica de negócios da aplicação de maneira organizada, escalável e fácil de manter. A service é responsável por lidar com a complexidade da aplicação e deve ser projetada para ser modular, fácil de testar e separada da camada de apresentação da aplicação.
Aqui estão alguns princípios e padrões que devem ser seguidos para criar uma service eficiente:
Cada service deve ter uma única responsabilidade, implementando apenas uma única funcionalidade ou conjunto de funcionalidades específicas. Isso torna a service mais fácil de entender e manter.
A service deve utilizar a injeção de dependência para obter recursos e outros serviços necessários para implementar sua funcionalidade. Isso torna a service modular e mais fácil de testar.
As classes, métodos e variáveis na service devem seguir as convenções de nomenclatura. Isso torna a service fácil de entender e utilizar para outros desenvolvedores.
A service deve tratar adequadamente os erros e retornar mensagens de erro significativas para a controller ou camada de apresentação. Isso torna a aplicação mais resiliente e mais fácil de depurar.
A service deve seguir as boas práticas de segurança recomendadas e pelo protocolo HTTP. Isso torna a aplicação mais segura e ajuda a prevenir vulnerabilidades de segurança.
A service deve ser separada da camada de apresentação da aplicação, evitando assim a mistura de responsabilidades. A controller deve delegar a lógica de negócios para a service, mantendo assim uma estrutura organizada e escalável.
A service deve ser uma camada fina que delega a lógica mais complexa para outros serviços. Isso torna a service focada em sua responsabilidade principal e torna a aplicação mais escalável e fácil de manter.
A service deve ser fácil de testar, utilizando mocks e outros recursos para simular o comportamento de outros serviços ou recursos necessários para implementar a funcionalidade.
O repository deve ser projetado de forma a gerenciar o acesso aos dados do aplicativo, tornando a camada de serviço mais modular e focada nas regras de negócios. O repository é responsável por implementar a lógica de acesso aos dados da aplicação e deve ser projetado para ser escalável, eficiente e fácil de manter.
Aqui estão alguns princípios e padrões que devem ser seguidos para criar um repository eficiente:
O repository deve ser separado da lógica de negócios da aplicação, evitando assim a mistura de responsabilidades. A camada de serviço deve delegar o acesso aos dados para o repository, mantendo assim uma estrutura organizada e escalável.
O repository deve ser projetado de forma a fornecer uma abstração do acesso aos dados da aplicação. Isso torna o repository mais modular e independente da tecnologia utilizada para armazenar os dados.
O repository deve utilizar a injeção de dependência para obter recursos e outros serviços necessários para implementar sua funcionalidade. Isso torna o repository modular e mais fácil de testar.
As classes, métodos e variáveis no repository devem seguir as convenções de nomenclatura. Isso torna o repository fácil de entender e utilizar para outros desenvolvedores.
O repository deve ser projetado para ser eficiente em termos de desempenho, minimizando o número de consultas necessárias para recuperar e armazenar dados.
O repository deve tratar adequadamente os erros e retornar mensagens de erro significativas para a camada de serviço. Isso torna a aplicação mais resiliente e mais fácil de depurar.
O repository deve seguir as boas práticas de segurança recomendadas pelo NestJS e pelo protocolo HTTP. Isso torna a aplicação mais segura e ajuda a prevenir vulnerabilidades de segurança.
O repository deve ser fácil de testar, utilizando mocks e outros recursos para simular o comportamento do banco de dados ou outro recurso utilizado para armazenar os dados.
A biblioteca @nestjs/typeorm é um módulo oficial do NestJS que facilita a integração com o TypeORM, uma biblioteca ORM (Object-Relational Mapping) para Node.js. Com o @nestjs/typeorm, é possível criar entidades, repositórios e serviços de banco de dados de forma mais simples e rápida.
O TypeORM é uma das bibliotecas mais populares para gerenciamento de bancos de dados no Node.js. Ele suporta vários bancos de dados, como MySQL, PostgreSQL, SQLite, Oracle, entre outros. Ele permite que o desenvolvedor defina as entidades usando classes, o que torna a escrita e leitura de código mais fácil e intuitiva.
Com o @nestjs/typeorm, é possível configurar as conexões com o banco de dados, definir as entidades, criar repositórios e serviços de forma mais fácil. Para isso, o módulo fornece decorators, como o @Entity(), para definir entidades, o @Repository(), para criar repositórios, e o @InjectRepository(), para injetar repositórios nos serviços.
Além disso, o @nestjs/typeorm fornece um conjunto de recursos adicionais para facilitar o gerenciamento de transações, migrações de banco de dados e outras funcionalidades avançadas. Por exemplo, é possível utilizar o TypeORMModule.forRoot(), para configurar a conexão com o banco de dados, e o TypeORMModule.forFeature(), para fornecer entidades e repositórios específicos para um módulo.
"dependencies":
"devDependencies":
Click aqui para ver a documentação
A biblioteca @nestjs/mongoose é um módulo oficial do NestJS que facilita a integração com o MongoDB utilizando o Mongoose, um ODM (Object-Document Mapping) para Node.js. Com o @nestjs/mongoose, é possível criar esquemas, modelos e serviços de banco de dados de forma mais simples e rápida.
O MongoDB é um banco de dados NoSQL orientado a documentos, e o Mongoose é uma biblioteca amplamente utilizada para trabalhar com o MongoDB no Node.js. Ele permite que o desenvolvedor defina esquemas usando classes, o que torna a escrita e leitura de código mais fácil e intuitiva.
Com o @nestjs/mongoose, é possível configurar as conexões com o banco de dados, definir os esquemas, criar modelos e serviços de forma mais fácil. Para isso, o módulo fornece decorators, como o @Schema(), para definir esquemas, o @InjectModel(), para injetar modelos nos serviços, e o @Prop(), para definir as propriedades de um esquema.
Além disso, o @nestjs/mongoose fornece um conjunto de recursos adicionais para facilitar o gerenciamento de transações, migrações de banco de dados e outras funcionalidades avançadas. Por exemplo, é possível utilizar o MongooseModule.forRoot(), para configurar a conexão com o banco de dados, e o MongooseModule.forFeature(), para fornecer esquemas e modelos específicos para um módulo.
Click aqui para ver a documentação
A biblioteca @nestjs/schedule é um módulo oficial do NestJS que facilita a criação e execução de tarefas agendadas (jobs) na aplicação. Com o @nestjs/schedule, é possível criar jobs que executam tarefas em intervalos de tempo específicos ou em horários definidos.
O @nestjs/schedule é baseado na biblioteca node-schedule e fornece uma interface fácil de usar para a criação de tarefas agendadas. Para criar um job, basta criar uma classe que implementa a interface CronJob e definir o horário ou intervalo de tempo em que o job deve ser executado.
Click aqui para ver a documentação
A biblioteca @nestjs/swagger é um módulo oficial do NestJS que facilita a documentação da API utilizando o Swagger. Com o @nestjs/swagger, é possível gerar a documentação da API de forma automática, com base nos decorators adicionados aos endpoints.
O Swagger é uma ferramenta para documentação de APIs, que permite que os desenvolvedores documentem e testem suas APIs de forma fácil e padronizada. Ele permite que os desenvolvedores definam endpoints, parâmetros e respostas, além de permitir testes de chamadas API diretamente na documentação.
O @nestjs/swagger fornece decorators para definir informações sobre a API, endpoints, parâmetros e respostas. Por exemplo, o decorator @ApiTags() é usado para definir as tags da API, e o decorator @ApiOperation() é usado para definir a descrição de uma operação.
Além disso, o @nestjs/swagger fornece um conjunto de recursos adicionais para personalização da documentação, como a definição de esquemas de resposta e a personalização de cabeçalhos e parâmetros.
Click aqui para ver a documentação
A biblioteca @nestjs/websockets é um módulo oficial do NestJS que facilita a criação de conexões em tempo real (real-time) em uma aplicação web utilizando WebSockets. Com o @nestjs/websockets, é possível criar sockets que permitem a comunicação bidirecional entre o servidor e o cliente, sem a necessidade de fazer requisições constantes ao servidor.
O WebSockets é um protocolo de rede que permite a comunicação em tempo real entre cliente e servidor, permitindo a troca de mensagens instantâneas sem a necessidade de requisições constantes. Com o @nestjs/websockets, é possível criar sockets WebSocket com facilidade e rapidez.
Click aqui para ver a documentação
O NestJS Config é uma biblioteca que facilita a leitura e o gerenciamento de configurações em uma aplicação NestJS. Ele permite a criação de arquivos de configuração em diferentes formatos, como JSON, YAML e ENV, e a leitura dessas configurações em diferentes ambientes.
Click aqui para ver a documentação
A biblioteca @nestjs/terminus é um módulo oficial do NestJS que fornece recursos para monitorar a saúde de uma aplicação. Com o @nestjs/terminus, é possível criar endpoints de saúde que verificam a integridade de recursos, serviços e outras dependências da aplicação.
A verificação de saúde de uma aplicação é um recurso importante para garantir a disponibilidade e a confiabilidade de uma aplicação. Com o @nestjs/terminus, é possível criar endpoints que verificam se os serviços de uma aplicação estão funcionando corretamente e se as dependências externas, como bancos de dados e serviços de armazenamento, estão disponíveis.
Click aqui para ver a documentação
O Class-validator é uma biblioteca utilizada em conjunto com o NestJS para validação de objetos e dados em uma aplicação. Com ela, é possível validar os dados recebidos por um endpoint, por exemplo, antes de persisti-los no banco de dados ou de usar esses dados em outras operações da aplicação.
A validação pode ser feita por meio de decorators, que são anotações que definem regras de validação em cima de campos ou propriedades de uma classe. Por exemplo, o decorator @IsEmail() pode ser utilizado para validar se um campo é um email válido, e o decorator @Length() pode ser utilizado para validar se um campo tem um tamanho mínimo e máximo.
O Class-validator suporta uma grande variedade de regras de validação, como validação de tipo, validação de tamanho, validação de formato, validação de presença, validação de unicidade, entre outras. Além disso, é possível criar regras de validação personalizadas, definindo uma função que recebe o objeto a ser validado e retorna uma mensagem de erro, caso a validação falhe.
O Class-validator também suporta validação de objetos aninhados, ou seja, objetos que contêm outros objetos como propriedades. Para isso, é possível utilizar o decorator @ValidateNested(), que valida todos os campos do objeto aninhado.
Click aqui para ver a documentação
A arquitetura proposta apresenta uma separação clara de responsabilidades em camadas. Além disso, a utilização de padrões de projeto pode facilitar a implementação das camadas. A adoção desta arquitetura pode contribuir para a qualidade e a consistência dos projetos desenvolvidos com NestJS.
A CLI (Command Line Interface) do NestJS é uma ferramenta poderosa que ajuda os desenvolvedores a criar e gerenciar projetos NestJS com facilidade. Ela fornece um conjunto de comandos pré-configurados que ajudam a criar módulos, controladores, serviços, repositórios, além de outras funcionalidades.
Aqui estão algumas razões pelas quais é importante usar a CLI do NestJS:
Produtividade: A CLI do NestJS fornece comandos para gerar automaticamente a estrutura de um projeto NestJS, bem como componentes individuais, como módulos, controladores, serviços e repositórios. Isso permite que os desenvolvedores se concentrem na lógica do negócio, em vez de gastar tempo configurando a estrutura do projeto.
Consistência: A CLI do NestJS segue as melhores práticas e convenções recomendadas pelo framework. Isso garante que o projeto seja estruturado de maneira consistente e que as convenções de nomenclatura sejam seguidas corretamente.
Escalabilidade: A CLI do NestJS ajuda a manter a estrutura do projeto organizada, permitindo que os desenvolvedores adicionem novos módulos, controladores e serviços de maneira eficiente. Isso torna o projeto mais escalável e fácil de gerenciar.
Manutenção: A CLI do NestJS também fornece comandos para ajudar a manter o projeto atualizado e limpo. Por exemplo, é possível usar a CLI para remover componentes desnecessários e limpar arquivos de log antigos.
Facilidade de uso: A CLI do NestJS é fácil de usar e oferece suporte a uma ampla variedade de opções e configurações personalizadas. Isso permite que os desenvolvedores criem projetos NestJS personalizados de acordo com suas necessidades específicas.
Em resumo, a CLI do NestJS é uma ferramenta essencial para aumentar a produtividade, garantir a consistência e escalabilidade de um projeto, além de facilitar a manutenção e a evolução do código.