Economizando tempo no start de um novo projeto

Economizando tempo no start de um novo projeto

Quanto tempo você gasta só pra configurar a base de um novo projeto?

Por mais simples ou complexo que seja um serviço, nós temos que construir a sua base, criar entidades, criar a estrutura de pastas, aí então cria um repositório e um usecase/service pra cada entidade, ai configura a api, as rotas, nóóóó, mó trampo!

Já pensou em automatizar a criação de tudo isso pra já sair codando sua regra de negócio? Se você não aguenta mais perder tempo escrevendo a base do projeto, esse artigo é para você!


O projeto mais rápido da sua vida

Hoje iremos criar uma api baseada em PostgreSQL com duas camadas de apresentação, sendo uma Graphql e outra REST.

Para o prato de hoje, usaremos os ingredientes:

Pra começar nosso "fast project" nós precisamos primeiro de tudo, modelar a nossa base. Imagine que estamos criando uma app de e-commerce, em um ecomerce nós teríamos 3 entidades: usuário, produto e compra.

O primeiro passo para construir nosso projeto será criar as entidades, pois será a partir dela que nossa CLI irá construir todo o resto: usecases, repositories, migrations, graphql, rest etc.

Pra construir a nossa entidade, nós iremos usar a lib do @herbs/herbs que será essencial para que nossa CLI possa captar alguns metadados para construir todo o resto (caso queira saber mais sobre essa lib, confira a documentação).

Construir uma entidade com o Herbs é muito simples, basta importar o módulo "entity" e passar os parâmetros entity("entityName", { fields }).

Lembrando que iremos colocar todas essas entidades dentro de uma pasta chamada “entities”.

const { entity, field } = require('@herbsjs/herbs')


const Customer = entity('Customer', {
    id: field(Number),
    cellphone: field(String),
    email: field(String),
    gender: field(String),
    age: field(Number),
    discountClub: field(Boolean),
    city: field(String),
    state: field(String),
    address: field(String),
    zipcode: field(String),
    lastName: field(String),
    fisrtName: field(String),
    civilDocument: field(String),
})

module.exports = Customer
entities/customer.js

E além de criar nossa entidade nua e crua, que tal adicionar algumas validações? Pra fazer isso com o Herbs é muito simples, basta adicionar um objeto com algumas opções ao lado do tipo do campo field(Tipo, { option }).

Caso queira ver mais sobre os tipos de validações, você pode conferir a documentação do Herbs aqui.

const { entity, field } = require('@herbsjs/herbs')

const requiredString = {
    validation: {
        presence: true,
        length: { minimum: 3, maximum: 255 },
    }
}

const Customer = entity('Customer', {
    id: field(Number, {
        // Optional option
        validation: {
            presence: true,
            numericality: {
                greaterThan: 0,
                onlyInteger: true
            }
        }
    }),
    cellphone: field(String, {
        validation: {
            format: /^\+[1-9]{1}[0-9]{3,14}$/
        }
    }),
    email: field(String, {
        validation: { ...requiredString.validation, email: true } 
    }),
    gender: field(String, {
        validation: { ...requiredString.validation, contains: { allowed: ["M", "W", "X"] } } 
    }),
    age: field(Number),
    discountClub: field(Boolean),
    city: field(String, requiredString),
    state: field(String, requiredString),
    address: field(String, requiredString),
    zipcode: field(String, requiredString),
    lastName: field(String, requiredString),
    fisrtName: field(String, requiredString),
    civilDocument: field(String, requiredString),
})

module.exports = Customer
entities/customer.js

Agora que aprendemos a criar uma entidade que será usada pela nossa CLI, vamos criar o restante das entidades.

const { entity, field } = require('@herbsjs/herbs')

const required =  { validation: { presence: true } }
const requiredString = {
    validation: {
        ...required.validation,
        length: { minimum: 3, maximum: 255 },
    }
}

module.exports = entity('Product', {
    id: field(Number, {
        // Optional option
        validation: {
            presence: true,
            numericality: {
                greaterThan: 0,
                onlyInteger: true
            }
        }
    }),
    limitDiscount: field(Number, { 
        validation: { 
            greaterThan: 0,
            lessThan: 100,
            onlyInteger: true 
        }
    }),
    size:  field(String, required),
    stock: field(Number, required),
    color: field(String, required),
    name: field(String, requiredString),
    brand: field(String, requiredString),
    category: field(String, requiredString),
    description: field(String, requiredString),
    price: field(Number, { validation: { presence: true, greaterThan: 0 } }),
})
entities/product.js
const { entity, field } = require('@herbsjs/herbs')

const required =  { validation: { presence: true } }

module.exports = entity('Sale', {
    id: field(Number, {
        // Optional option
        validation: {
            presence: true,
            numericality: {
                greaterThan: 0,
                onlyInteger: true
            }
        }
    }),
    productID: field(Number, required),
    customerID: field(Number, required),
    total: field(Number, required),
    quantity: field(Number, required)
})
entities/sale.js

No final teremos esse resultado:

Gerando o projeto

E aí chegou a tão sonhada hora, onde nós geramos nosso projeto baseado nas nossas entidades!


Para isso, iremos usar a CLI do Herbs como já citado anteriormente e, pra que possamos usar essa ferramenta, primeiro de tudo, precisamos instalá-la.

npm install @herbs/herbs-cli -g

A CLI do Herbs tem o intuito de te ajudar a criar o seu projeto de uma forma mais rápido que utilize todo o ecossistema Herbs, que será o que abordaremos hoje, mas além disso, ele também te ajuda a dar manutençao na aplicaçao durante o desenvolvimento, porém isso será abordado em um outro artigo :)

Depois que instalamos nossa CLI, ao chamá-la com o comando new nos será feito uma série de perguntas para que possa ser gerada nossa app de uma forma o mais customizada possível.


Apesar de termos selecionado as opções acima, note que enquanto as perguntas são feitas você pode criar uma app só com graphql ou só com rest, com mongo, com outra licença, etc.

Note que a última pergunta é uma questão opcional, caso você não especifique a pasta onde suas entidades se encontram, será gerado um projeto baseado em uma entidade padrão de “User”, mas não estamos aqui pra gerar um “boilerplate” não é mesmo? Então vamos preeche-la com o caminho de onde nossas entidades se encontram.

Assim que voce der “Enter”, será gerado o nosso app e você irá encontrar uma pasta com o nome do projeto.


E olha que legaaaaaal! Quanta coisa gerada automaticamente! Meu, que economia de tempo, da até uma satisfação!

Note que todo o projeto está feito: migrations, repostories e tudo mais!

Explicando o projeto

Tá, mas eai? O que temos nesse projeto autogerado?

O diagrama abaixo mostra um pouco como o projeto está organizado(que você pode conferir na home da doc do Herbs), a app é gerado baseando-se no clean archtecture, onde nós temos o nosso dominio com nossas entidades e nossos usecases e em volta disso nos temos o restante da nossa aplicação (api, banco de dados, clients, documentaçao e etc) e todas essas demais camadas usam o que chamamos de “colas” para que o seu desenvolvimento seja facilitado, lembrando que nenhuma cola é um gerador de código, são apenas ferramentas pra agilizar o desenvolvimento do seu código.

Mas, apesar de termos toda uma arquitetura bem definida, vamos ser um pouco mais práticos e falar da estrutura de pastas do nosso projeto.

OBS: lembrando que arquitetura não é estrutura de pastas! :P

A ideia aqui é que a partir dos metadados gerados pelos usecases como o nome do usecase, estrutura dos dados de entrada e saída, e tudo mais, a apresentação seja gerada, e essas camadas são geradas com a ajuda das colas, por exemplo:

Para poder gerar as mutations da camada Graphql, nós importamos todos os usecases, iteramos sobre eles e então passamos esse usecase para a cola “herbs2gql” que nos devolve uma string com os scheemas relacionados à mutation.

O conceito de “cola” percorre durante toda a aplicação, da camada de apresentação até o repositório, lembrando que elas não são obrigatórias e que elas servem apenas para ajudar a você desenvolver e dar manutenção no seu projeto. :)

Dentro dos nossos usecases podemos ver que não só a criação das nossas entidades é feita, mas também existe uma validação mínima, o que é muito bom pois geramos nosso projeto como ele já veio com algumas regras básicas implementadas. É muita economia de tempo!

Rodando o projeto

Depois que geramos e entendemos o código, vamos colocar esse trem pra rodar! Basta dar o comando npm run start e ver a mágica acontecer. Note que todas as rotas REST são impressas no terminal e que todas são baseadas em nossos usecases.

Além disso, se você abrir seu browser na rota http://localhost:3000/graphql verá todos os scheemas gerados para a camada de apresentação Graphql e sua documentação

Como se não bastasse ter um app pronto, o herbs gera para você uma documentação dinamica, então imagina só, você nunca mais vai precisar se preocupar com a documentação da sua API pois já vai ser tudo gerado automaticamente!

A partir daí é só implementar a nossa regra de negócio e subir pra produção!