Esse projeto foi criado para poder demonstrar como é a modificação de uma api construída em programação funcional para a programação orientada a objetos. O projeto foi estruturado de forma que somente a solução (pasta app
) sofra evolução visto que o express é uma biblioteca externa e a mesma já trabalha com programação funcional. Também serão aplicados ao logo das mudanças (definidas pelas branches) conceitos como injeção de dependência e solid.
O projeto foi criado em typescript sobre o NodeJS utilizando como banco de dados o MySQL. Para fazer a inicialização do ambiente será necessário ter uma instância do MySQL rodando e, para isso aconselhamos a criação da mesma usando o Docker através do comando abaixo:
docker run --name mysql -p 3306:3306 -e MYSQL_ROOT_PASSWORD=root --restart=always -d mysql:5
Caso já tenha uma instância do mysql rodando como um serviço ou como uma imagem do docker e queira usá-la, você deve criar um arquivo .env
na pasta root conforme o arquivo abaixo:
DB_HOST="127.0.0.1"
DB_PORT="3306"
DB_NAME="api-fp2oop"
DB_USER="root"
DB_PASS="root"
Para facilitar o processo de criação do banco de dados e inserir alguma informação, foi criado um script de migração. para utilizá-lo execute o comando npm db:create
.
Será construída uma API RESTFul em Typescript para fazer o CRUD de users
, utilizando o express.js para prover as rotas, o joi para validações e o MySQL para persistência dos dados.
As evoluções do projeto serão marcadas através das mudanças entre as branches. Cada mudança entende-se como uma evolução gradual.
O projeto inicialmente será criado com a arquitetura MSC (Model-Service-Controller) para que possamos apresentar a evolução do mesmo. A seguir estão as branches que serão criadas no projeto junto com o link de cada uma:
Nesse passo é feita a configuração mínima para a criação de uma aplicação em typescript que utiliza como padrão um modelo de regras básico, personalizável e de acordo com as melhores práticas para typescript: eslint-config-standard-with-typescript.
Além disso, para que possamos utilizar a importação de arquivos através dos path's do typescript, também é feito uso da biblioteca tsconfig-paths. Essa lib faz a leitura dos paths declarados no arquivo do typescript e então já faz a resolução sem erros inclusive ao fazer o build da solução.
Nesse passo, o projeto desenvolvido aplica o padrão MSC (Model-Service-Controller) onde o Model cuida de gerenciar o banco de dados, o Service mantém todas as regras de negócio da solução e o Controller faz a interface da API. Além disso também foram adicionados outras pastas como:
errors
: para manter todos os erros personalizados na aplicação. Como boa prática é interessante mapear os possíveis erros.validations
: para manter todas as validações necessárias ao receber as informações do frontend.
Também existe um arquivo centralizador que serve para declaração dos tipos da aplicação, o domain.ts
Para que possamos aplicar as boas práticas à nossa aplicação sem que haja a dependência do express, a mesma é desacoplada em 2 camadas sendo as rotas e os controllers. Isso se mostra necessário pois existem outras formas de se formar uma API (ex: graphql) e outras formas de se chamar a aplicação como a utilização de filas (ex: rabbitmq), outros protocolos como o gRPC e afins. Toda solução deve ser construída de forma que possa extraír uma biblioteca sem impactar no todo.
Pensando ainda em otimização da solução, aqui nós extraímos todo o código da nossa solução que é acessado através da API RESTful. Isso é importante pois caso eu queira seguir com o raciocínio do item 3, utilizando minha solução também com um RabbitMQ como por exemplo, ele não deve se misturar ao código comum da API visto que são 2 contextos diferentes mas ele deve acessar minha solução.
A solução foi construída com constantes onde os recursos são métodos dentro destes objetos para demonstrar como uma classe pode ser tratada em um ambiente modular. Para a conversão da mesma basta fazer a transformação em classes e adicionar instâncias das mesmas onde for necessário
Pensando em SOLID, um dos principais conceitos para um "código limpo" seria a inversão (ou injeção) de dependência. A idéia é que para a classe funcionar você passe a mesma o que ela precisa fazer. Assim é possível criar múltiplas formas de utilização da mesma sem a necessidade de alterá-la.
Em Typescript podemos fazer a injeção utilizando a bibliteca TypeDI assim como é feito em outras linguagens.