Confira neste artigo: com a inclusão de cada vez mais software nas empresas, como mantê-lo escalável, seguro e com qualidade.
Tempo de Leitura: 4 minutos
Durante a pandemia as empresas tiveram que inovar e adotar posturas mais tecnológicas e incluir cada vez mais softwares em seus processos de negócio.
Com isso, entre os desenvolvedores, há muita preocupação em como manter o software escalável, seguro e com qualidade.
Neste artigo, veremos como pensar e desenvolver um software baseado nos princípios SOLID e como podemos aplicar o primeiro princípio (Single Responsibility Principle) e separar a camada de acesso ao banco de dados do restante da aplicação, utilizando NestJS.
Afinal, o que é SOLID?
O princípio SOLID nada mais é do que cinco princípios observados na orientação a objetos e design(ou também conhecido como POO - Programação Orientada a Objetos) de código que foram abordados no artigo The Principles of OOD - Robert C Martin (a.k.a Uncle Bob).
O SOLID ajuda o programador a manter a qualidade de software e a escrever códigos mais limpos, separando as responsabilidades, facilitando a refatoração e estimula o aproveitamento de código.
SOLID: Os cinco princípios da POO
- S - Single Responsibility Principle (Princípio da Responsabilidade Única)
- O - Open-closed Principle (Princípio aberto-fechado)
- L - Liskov Substitution Principle (Princípio de Substituição de Liskov)
- I - Interface Segregation Principle (Princípio da Segregação de Interface)
- D - Dependency Inversion Principle (Princípio da inversão de dependência
Neste artigo, abordaremos somente o primeiro princípio e ensinar você a realizar a separação de responsabilidade utilizando o NestJS.
Single Responsibility Principle (Princípio da Responsabilidade Única)
O princípio da responsabilidade única determina que uma classe deve ter apenas um objetivo (ou responsabilidade), ou seja, ela deve executar apenas uma única função dentro do seu escopo.
Na prática funciona da seguinte forma: imagine que você vá a um restaurante e neste restaurante você encontra vários tipos de funcionários: os garçons, o cozinheiro, o caixa e assim por diante.
Cada funcionário deste restaurante executa funções que tem tudo a ver com sua responsabilidade:
- Cozinheiro: faz o preparo dos alimentos do restaurante.
- Garçom: é responsável por anotar o seu pedido e entregá-lo a você.
- Caixa: recebe o pagamento da sua comanda.
- e assim por diante.
Certo, e como eu implemento este princípio em meu projeto Nest?
Vamos agora iniciar o nosso projeto em Nest. Para este artigo, faremos uma aplicação que contém um cadastro de usuários.
Antes de começarmos, é necessário que tenha instalado na sua máquina o NodeJS e um gerenciador de pacotes de sua preferência, podendo ser o npm ou o yarn.
Também é necessário instalar o NestJS CLI, que será responsável por instalar e configurar as dependências necessárias para rodar nossa aplicação.
No terminal, rode o comando:
nest new user-api
O NestJS irá criar o projeto e solicitar qual gerenciador de dependências será utilizado para instalar as dependências:
Com o processo finalizado, iremos abrir o Visual Studio Code e iniciar nossa configuração.
Abaixo temos a estrutura que o Nest criou para darmos andamento ao projeto:
Configurando o TypeORM
Para realizarmos a configuração do TypeORM no projeto, primeiro precisamos instalar os pacotes @nestjs/typeorm, typeorm e pg (usaremos o banco de dados PostgreSQL). Para isso, basta executar o seguinte comando:
$ yarn add @nestjs/typeorm typeorm pg
Em seguida, criaremos uma pasta config e incluiremos nela um arquivo database.ts, com as seguintes configurações:
import * as dotenv from 'dotenv'; import { ConnectionOptions } from 'typeorm'; dotenv.config(); const { NODE_ENV, DB_USERNAME_DEVELOPMENT, DB_PASSWORD_DEVELOPMENT, DB_NAME_DEVELOPMENT, DB_HOSTNAME_DEVELOPMENT, DB_PORT_DEVELOPMENT, DB_USERNAME_TEST, DB_PASSWORD_TEST, DB_NAME_TEST, DB_HOSTNAME_TEST, DB_PORT_TEST, DB_USERNAME_PRODUCTION, DB_PASSWORD_PRODUCTION, DB_NAME_PRODUCTION, DB_PORT_PRODUCTION, DB_HOSTNAME_PRODUCTION, DB_USERNAME_SANDBOX, DB_PASSWORD_SANDBOX, DB_NAME_SANDBOX, DB_PORT_SANDBOX, DB_HOSTNAME_SANDBOX, } = process.env; const config: Record<string, any> = { development: { type: 'postgres', username: DB_USERNAME_DEVELOPMENT, password: DB_PASSWORD_DEVELOPMENT, database: DB_NAME_DEVELOPMENT, host: DB_HOSTNAME_DEVELOPMENT, port: parseInt(DB_PORT_DEVELOPMENT || '5434', 10), }, test: { type: 'postgres', username: DB_USERNAME_TEST, password: DB_PASSWORD_TEST, database: DB_NAME_TEST, host: DB_HOSTNAME_TEST, port: parseInt(DB_PORT_TEST || '5434', 10), }, sandbox: { type: 'postgres', username: DB_USERNAME_SANDBOX, password: DB_PASSWORD_SANDBOX, database: DB_NAME_SANDBOX, port: parseInt(DB_PORT_SANDBOX || '5432', 10), host: DB_HOSTNAME_SANDBOX, }, production: { type: 'postgres', username: DB_USERNAME_PRODUCTION, password: DB_PASSWORD_PRODUCTION, database: DB_NAME_PRODUCTION, port: parseInt(DB_PORT_PRODUCTION || '5432', 10), host: DB_HOSTNAME_PRODUCTION, }, }; export const getConfig = (): ConnectionOptions => config[NODE_ENV || 'development'];
Em seguida, acessaremos o arquivo app.module.ts e incluiremos as seguintes configurações:
import { Module } from '@nestjs/common'; import { getConfig } from './config/database'; import { TypeOrmModule } from '@nestjs/typeorm'; @Module({ imports: [ TypeOrmModule.forRoot({ ...getConfig(), entities: [], }), ], controllers: [], providers: [], }) export class AppModule {}
Agora criaremos nossa primeira entidade, a entidade Usuário. Na pasta entities, crie um arquivo User.ts, com as seguintes configurações:
import { Column, CreateDateColumn, Entity, PrimaryColumn, UpdateDateColumn, } from 'typeorm'; import { v4 as uuid } from 'uuid'; @Entity('users') export class User { @PrimaryColumn() id: string; @Column() name: string; @Column() username: string; @Column() password: string; @Column({ name: 'is_app' }) isApp: boolean; @CreateDateColumn({ name: 'created_at' }) createdAt: Date; @UpdateDateColumn({ name: 'updated_at' }) updatedAt: Date; constructor() { if (!this.id) this.id = uuid(); } }
Agora criaremos o nosso repositório, que deve estar em uma camada separada da camada de lógica de negócio.
Na pasta repositories, crie o arquivo UserRepository.ts com as seguintes configurações:
import { Repository } from 'typeorm'; import { InjectRepository } from '@nestjs/typeorm'; export class UserRepository { constructor( @InjectRepository(User) private repository: Repository<User>, ) {} async create(): Promise<User> { /* * Código para cadastrar um usuário */ } async findByUsername(): Promise<User> { /* * Código para buscar um usuário pelo username */ } async update(): Promise<void> { /* * Cóigo para atualizar um usuário */ } async remove(): Promise<void> { /* * Código para remover um usuário */ } }
O processo de chamada deste repositório ocorre por injeção de dependência. Crie um módulo de users com o comando:
nest g module users
Este comando gerará a pasta users no seguinte formato:
Crie agora o arquivo users.service.ts, com as seguintes configurações:
import { UserRepository } from 'src/repositories/UserRepository'; export class UserService { constructor(private readonly usersRepository: UserRepository) {} async create() { /* Block code here... */ } }
Pronto! Aplicamos a primeira regra do SOLID separando nossa camada de repositório da camada de lógica do negócio.
Caso queira saber mais sobre o Nest e suas implementações, é só conferir a documentação neste link.
Gostou desse conteúdo?
E aí, dev! Curtiu esse post? Queremos cada vez mais trazer esse tipo de conteúdo aqui para vocês. Mas além de encontrar conteúdos de prática no nosso blog, você também encontra muito mais no nosso fórum. Se cadastre clicando aqui ou no botão abaixo e fique por dentro de tudo que acontece no mundo do desenvolvimento de software.