- 👀 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 Introduction
api-cpf-validator's People
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:
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.
Conexão com o MongoDb sendo realizada por variável.
Atualmente, a conexão com o banco de dados é realizada no arquivo dbConnect.ts. No entanto, é necessário mudar a forma como a conexão é inicializada, movendo-a para uma função assíncrona.
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
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.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.