Giter VIP home page Giter VIP logo

api-cpf-validator's Introduction

Hello, I’m Ivan A. F. Aguiar

  • 👀 I’m interested in software engineering, focused in back-end, by now.
  • 🌱 I’m currently learning Node.js + TS - SQL - NoSQL - Elastic Search - Scrum -AWS - Lambda

api-cpf-validator's People

Contributors

ivnagst avatar

Watchers

 avatar  avatar

api-cpf-validator's Issues

Organizar arquitetura do projeto

Para uma boa mantenabilidade, o ideal é que o projeto seja separado em camadas com responsabilidades claras, e sem invasão de escopo entre elas.

Uma arquitetura que funciona bem com typescript é a onion. Abaixo, há um artigo que explica como usá-la com inversify.
https://dev.to/remojansen/implementing-the-onion-architecture-in-nodejs-with-typescript-and-inversifyjs-10ad

Veja uma imagem, extraída deste artigo, da organização dessa arquitetura:
image

Alguns insights sobre cada camada:

  • userInterface: Essa é a camada que vai se comunicar com o usuário, e só nela devemos deixar rastro do que é usado para isso. Exemplo: objetos do express, como o req e res, não podem sair daí. Controllers, ficam aí também. A função startServer também deve ficar aí, porque tudo isso tem a ver com a interface com o usuário. A declaração de injeção de dependencia, para simplicidade, também pode ficar aí por enquanto.
  • ApplicationService: Nesta camada vamos colocar os "handlers" da aplicação. Cada handler representa um "comando". Se você tiver uma rota GET something, por exemplo, você cria um GetSomethingHandler. Esta é uma camada abstrata de negócio, e não devemos inserir nada nela que se refira a interface com o usuário ou infraestrutura. As únicas coisas que você injeta nos handlers, são serviços de domínio (no máximo o logger, se achar que faz sentido). A responsabilidade do handler é orquestrar a dinâmica entre serviços de domínio. Exemplo: se você tiver dois serviços, cada um com uma responsabilidade diferente, e ambos precisam ser usados para atender certo comando, é o handler que chama eles. Um serviço não chama o outro, é responsabilidade do handler chamar um, pegar o retorno que for necessário, e chamar o outro;
  • DomainService: Nesta camada os serviços de domínio são inseridos. Aqui também, você pode declarar classes abstratas que representam repositórios por exemplo, não podemos ter nenhuma referência direta a elementos de infraestrutura (como mongo, express), nesta camada, qualquer coisa que é de infra, você cria uma classe abstrata aqui e isso irá ser seu símbolo de injeção. A implementação da interface você fará em outra camada. Você também pode criar classes de parser aqui, que podem ser injetadas nos DomainService, caso precise;
  • DomainModel: Nesta camada você não coloca código, apenas contratos, interfaces de objetos (não de serviços, nada com métodos). Os modelos de dados que você usa na sua aplicação, mas, de novo:: sem fazer referência a nenhum elemento de infra (como mongoose, por exemplo);
  • Infrastructure: Nesta camada, você implementa as interfaces de infraestrutura que foram declaradas no DomainService.
  • Test: a camada de teste falaremos em outro momento.

É importante seguir essas regras ao organiar o projeto assim:

  • DomainModel não pode fazer referência a nenhuma outra camada, nem a bibliotecas externas;
  • DomainService só pode fazer referência a DomianModel, nada mais;
  • ApplicationService só pode fazer referência a DomainService e DomainModel;
  • Infrastructure só pode fazer referência a DomainService e DomainModel;
  • UserInterface só pode fazer referência a ApplicationService e DomainModel;

Quando uma classe de repositorio é criada, você pode fazer algo assim, por exemplo:

export abstract class CustomerRepository {
   abstract getCustomerById(id: string): Promise<Customer | undefied>;
}

Então, na infrastructure, pode implementar ela, tratando ela como se fosse interface (o typescript permite isso):

export class MongoCustomerRepository implements CustomerRepository {
  async getCustomerById(id: string) {
    // Implementação aqui
  }
}

É estranho usar classe como interface, mas isso te trás uma vantagem: na hora de declarar a injeção, você pode usar a classe abstrata como símbolo de injeção e deixar seu código mais limpo:

bind(CustomerRepository).to(MongoCustomerRepository).inSingletonScope();

E aí, na hora de injetar:

constructor(private repo: CustomerRepository) {}

Isso dispensa o uso do decorator @Inject. O @Injectable ainda será necessário nas classes concretas, independente da camada. Isso é uma exceção às regras citadas acima. Algumas bibliotecas externas também podem ser consideradas exceção e serem usadas em qualquer camada, caso o papel delas não tenha a ver com infra estrutura, como as libs moment ou uuid, por exemplo.

Finalmente, agora que você chegou nesse ponto onde o projeto tá todo usando injeção, pegue essas informações e organize melhor o projeto, para seguir essas regras arquiteturais!

Utilizar inversify + inversify-express-util para estrutura de http

Atualize o projeto para que as rotas dos controllers seja feita via decorators do inversify-express-util.

Usando ele e o inversify, você já não declara as rotas na mão e a criação das instancias dos controllers e serviços é feita via injeção de dependencia.
Injeção, aqui, consistirá em passar a classe de serviço como parâmetro do construtor nos controllers. O próprio inversify-express-util irá cuidar da injeção dos controllers, de acordo com as rotas declaradas.

Para uma classe ser injetável, você precisa marcar ela com o decorator @Injectable.
Para injetar uma classe, você não precisa usar @Inject, se você habilitar, no inversify, a opção "autoInject"

Você pode usar a api-search-intention como exemplo de como isso é construído, e também pode usar a documentação do inversify-express-util como fonte de conhecimento pra fazer essa melhoria

Ajuste da estrutura da API

Hoje, a iniciação da API é realizada no arquivo app. É necessário alinhar este modo de inicialização.

Utilizar TypeScript

Configurar e utilizar Typescript no lugar de Javascript no projeto

Além de configurar typescript e trocar os js por ts, também quero que você configure eslint, prettier e editorconfig, para termos um lint automatizado

Corrigir bugs

  • A rota de inclusão de CPF inclui duas vezes o mesmo número
  • A rota de status só retorna a quantidade de registros inseridos na segunda chamada

Melhorar estrutura de código

Este if verifica se docs está definido, no entanto, no if logo antes já foi verificado se ele não é "não definido". Este código tem vários pontos que seguem o mesmo padrão.
Refatore o código para deixar a estrutura de ifs melhor, sem precisar repetir condições redundantes.

Lembrando que esse que citei é só um exemplo, avalia o código todo para poder corrigir o problema em todos os lugares dele

Utilizar corretmente eslint

O eslint foi configurado, mas não está sendo utiliado. Não há comandos no package.json para executar ele, como também não há u pipeline no projeto para validar lint.

Para criar o pipeline de validação, vc pode seguir o exemplo deste arquivo:
https://github.com/Codibre/remembered-redis/blob/master/.github/workflows/lint.yml

Uma sugestão de comandos de lint q vc pode incluir, são os scripts que contem a palavra lint no nome, aqui:
https://github.com/Codibre/remembered-redis/blob/master/package.json

Você pode automatizar a execução do lint no momento de enviar o commit, se passar a utilizar o husky. As instruções que tem no pacote npm são suficientes:
https://www.npmjs.com/package/husky

Melhorar organização da injeção de dependência

  • Garantir que apenas uma classe seja instanciada na mão (de preferencia o server) e todo o resto seja obtido via injeção de dependencia, seja via container.get, seja informando no constructor
  • O banco deve ser inicializado no próprio initialize do server, obtendo a classe q controla isso via container.get
  • As configurações também devem ser obtidas via injeção de dependencia, de forma a deixar em um lugar centralizado o "configFactory", que é o lugar que monta o objeto de configuração. De preferencia, criar uma classe que representa a configuração, com os cmapos esperados e usar ela como símbolo de injeção.
  • Configuração obtida via lib config ou via process.env, preferencialmente deve ser toda maiúscula com palavras separadas por _. Pensa que o seu projeto pode rodar em uma máquina windows, que é case insensitive. Já o seu objet ode configuração, pode ficar om o nome dbUri bonitinho, por exemplo, já que vc vai ter uma função configFactory que transforma uma coisa em outra.
  • Evitar colocar lógica fora de função ou método, seja inicializações de variáveis, seja execução de código mais complexo. Encapsula tudo em funções ou método e cria um arquivo index.ts, que só vai chamar uma função: startServer. Todos os outros lugares não declaram nada fora de função, método ou classe
  • Usar inSingletonScope nas suas injeções, para fazer com que as classes sejam instanciadas apenas uma vez.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.