Skip to content

GitLab

  • Menu
Projects Groups Snippets
    • Loading...
  • Help
    • Help
    • Support
    • Community forum
    • Submit feedback
    • Contribute to GitLab
  • Sign in
  • wiki wiki
  • Project information
    • Project information
    • Activity
    • Labels
    • Planning hierarchy
    • Members
  • Repository
    • Repository
    • Files
    • Commits
    • Branches
    • Tags
    • Contributors
    • Graph
    • Compare
  • Issues 0
    • Issues 0
    • List
    • Boards
    • Service Desk
    • Milestones
  • Merge requests 0
    • Merge requests 0
  • CI/CD
    • CI/CD
    • Pipelines
    • Jobs
    • Schedules
  • Deployments
    • Deployments
    • Environments
    • Releases
  • Monitor
    • Monitor
    • Incidents
  • Analytics
    • Analytics
    • Value stream
    • CI/CD
    • Repository
  • Wiki
    • Wiki
  • Snippets
    • Snippets
  • Activity
  • Graph
  • Create a new issue
  • Jobs
  • Commits
  • Issue Boards
Collapse sidebar
  • dTool
  • wikiwiki
  • Wiki
  • guia_desenvolvimento_backend

Last edited by Rafael Victor Ruwer Araujo Apr 22, 2020
Page history

guia_desenvolvimento_backend

Voltar para arquitetura

Guia de desenvolvimento - Backend

Este documento é leitura obrigatória antes de iniciar o desenvolvimento do backend. Ele contém informações importantes sobre arquitetura, documentação e organização física do projeto.

Índice

  • Organização física
  • Documentação
  • Arquitetura
    • Controllers
      • Funcionamento básico
      • Organização física
      • Criando novas controllers
      • Implementando controllers
    • Services
      • Organização física
      • Criando novos serviços
      • Implementando serviços
    • Models (DAOs)
      • Organização física
      • Criando novos modelos
      • Implementando modelos

Organização física

As principais estruturas do projeto são:

  • README.md: informações sobre o projeto, instruções de configuração do ambiente e documentação dos scripts de execução e manutenção
  • package.json e package-lock.json: gerenciamento de dependências com NPM
  • .[babel,eslint,husky,jest,prettier]rc.js, .eslintignore e nodemon.json: configurações de pacotes auxiliares (transpiler, linter, formatter, git hooks, hot reload, testes, ...)
  • .env: variáveis de ambiente para execução do servidor
  • docs/: documentação
    • update.js: script para atualizar a documentação local
    • dTool API.postman_collection_.json: collection do Postman com requests da API
    • DT.postman_environment.json: environment do Postman com placeholders para as variáveis
    • DT-development.postman_environment.json: environment do Postman com valores padrão para desenvolvimento
  • dist/: código transpilado (que é executado pelo NodeJS)
  • src/
    • loaders/: funções de setup da aplicação
    • models/: modelos/DAOs/persistência (ver mais)
    • routes/: controllers/rotas (ver mais)
    • services/: serviços/negócio (ver mais)
    • app.js: ponto de entrada do servidor ("main")
    • config.js: exporta objeto com variáveis de ambiente (.env) para uso pela aplicação

Documentação

Todas as rotas da aplicação serão documentadas usando o Postman. O Postman é uma aplicação que permite a fácil execução de requests HTTP e análise dos retornos, ajudando a testar uma rota durante seu desenvolvimento (se o retorno da rota está de acordo com o especificado na documentação).

Você pode consultar a documentação das rotas a serem desenvolvidas pelo navegador e importar a definição das rotas para o aplicativo usando o botão Run in Postman. Junto a cada rota, serão anexados exemplos de entrada e retorno esperado, servindo como referência para o desenvolvimento tanto do backend quanto do app.

Arquitetura

O backend está organizado em uma arquitetura com três camadas:

  • controllers (pasta src/routes): trata os parâmetros das rotas, utiliza serviços para executar as ações necessárias para o correto funcionamento da rota e controem o retorno da rota (código de status, formato, ...);
  • services (pasta src/services): camada de negócio, recebe os parâmetros já parseados pelas controllers e usa os DAOs para acesso ao banco de dados;
  • models (pasta src/models): camada de persistência, provê acesso ao banco de dados.

Seguindo o padrão arquitetural em camadas, é importante que objetos de uma camada troquem mensagens apenas com objetos da camada imediatamente inferior, ou objetos da mesma camada. Por exemplo: controllers podem acessar/usar apenas services; services podem usar outros services ou modelos/DAOs (mas não podem acessar controllers).

Controllers

A camada de controllers (controladoras) é responsável por definir o tratamento para cada rota da API. Elas usam diretamente a API exposta pelo framework Express, tratando os parâmetros de cada rota (sejam parâmetros recebidos no body do request, como parte do caminho da rota ou como query), convertendo-os para um formato/estrutura aceito pelo(s) serviço(s) usados, fazendo invocações aos serviços e construindo as respostas de acordo com o retorno dos serviços.

Funcionamento básico

  1. tratar parâmetros usando API do Express;
  2. ajustar parâmetros para formato/estrutura esperada pelo(s) serviço(s) usado(s);
  3. executar a(s) chamada(s) ao(s) serviço(s);
  4. construir a resposta do request (código de status e payload) a partir da resposta do(s) serviço(s).

Organização física

Todas as controllers estão na pasta src/routes, e devem ser organizados de acordo com a rota tratada. Por exemplo:

  • o arquivo src/routes/technology.js trata os requests de todos os verbos aplicáveis (GET, POST, ...) à rota (e subrotas) /api/technology: GET /api/technology/3, POST /api/tecnology, ...
  • da mesma forma, o arquivo src/routes/healthInstitution.js cuida da rota /api/healthInstitution.

O arquivo src/routes/index.js exporta uma função que faz a configuração de todas as rotas da API. Essa função é invocada por um loader como parte do processo de inicialização do servidor.

Criando novas controllers

Sempre que for preciso criar o controller para uma nova rota:

  • o arquivo do controller deve exportar uma função que recebe um Router
    • a função exportada pode ser async (export default async (appRouter) => { ... })
  • a controller deve ser adicionada ao processo de configuração no arquivo src/routes/index.js:
    1. importar a controller: import controller from "./controller"
    2. invocar a função da nova controller: controller(appRouter)

Implementando controllers

Os serviços que podem ser usados pelas controllers estão na pasta src/services, e devem ser importádos de lá.

Importante

Para manter a padronização, um serviço não deve ser importado a partir do arquivo onde está definido, mas apenas do arquivo de onde todos os serviços são exportados:

  • ❌ import AlphaService from "../services/AlphaService"
  • ✅ import { AlphaService } from "../services"

O acesso a serviços é feito com o uso de um service locator: um objeto central que instancia e provê os serviços para uso pelas controllers. Para usar um serviço, é preciso:

  1. importar o service locator: import { Container } from "typedi" (estamos usando o pacote typedi)
  2. importar a classe do serviço: import { AlphaService } from "../services"
  3. obter uma referência ao serviço: const alphaService = Container.get(AlphaService)

Services

A camada de serviços é responsável pela lógica de negócio da aplicação. Ela faz as validações necessárias sobre os parâmetros, "joga" (throw) erros para tratamento pelas controllers, e faz modificações no banco de dados quando necessário. Um serviço também pode usar outro(s) serviço(s) para seu funcionamento.

Geralmente, um serviço é responsável pelas regras de negócio relacionadas a uma entidade de negócio. Por exemplo: HealthInstitutionService agrupa regras relativas às instituições de saúde; TechnologyService, relativos às tecnologias (procedimentos); e assim por diante.

Nota

Ainda não há uma definição exata quanto à granularidade e limite de responsabilidades dos serviços. Isso será discutido ao longo do projeto, caso a caso.

Organização física

Todos os serviços estão na pasta src/services. Serviços que podem ser usados por controllers devem ser exportados no arquivo src/services/index.js.

Criando novos serviços

Sempre que for necessário criar um novo serviço:

  1. criar um arquivo com o nome da classe do serviço: src/services/BetaService.js
  2. criar e exportar a classe do serviço: export default class BetaService { ... }
  3. re-exportar o novo serviço no arquivo src/services/index.js: export { default as BetaService } from "./BetaService"

Implementando serviços

Não é possível definir uma regra geral de formato de implementação dos serviços: depende do objetivo do método e entidade relacionada. Entretanto, alguns métodos CRUD de serviços possuem uma implementação parecida:

  1. validação das entradas
  2. invocação de alguma operação da camada de modelos (create, recover, update, delete, ...)
  3. filtro sobre o retorno da camada de serviço para retorno à controller

Ressaltando: não há um esqueleto de como é a implementação de um método de serviço.

Models (DAOs)

A camada de modelos encapsula a persistência dos dados e comunicação com o banco de dados. Os objetos dessa camada também são chamados de DAO (Data Access Object), por conta do padrão de projeto que implementam.

Como o backend comunica-se com o SGBD PostgreSQL, essa camada é desenvolvida com o pacote Sequelize, que faz o processo de mapeamento entre os modelos relacional e orientado a objetos (processo conhecido como ORM).

O uso do Sequelize como ORM permite acelerar o desenvolvimento ao não ter que implementar métodos CRUD simples que o Sequelize já provê, com create, list, update, entre outros.

Essa camada está intimamente ligada ao modelo físico de armazenamento dos dados no PostgreSQL: a definição de um modelo depende das tabelas, seus campos, chave primária e chaves estrangeiras (relacionamentos).

Organização física

Todos os modelos devem ficar na pasta src/models. O nome do arquivo de definição de cada modelo deve ser composto pelo nome do modelo (em UpperCammelCase), seguido de DAO. Cada arquivo deve definir apenas um modelo.

Criando novos modelos

Supondo que seja preciso criar um modelo para representar uma pessoa:

  1. criar um arquivo src/models/PersonDAO.js
  2. adicionar os imports necessários do Sequelize: import { Model, DataTypes } from "sequelize"
  3. exportar uma classe default que herda da classe Model do Sequelize: export default class PersonDAO extends Model { ... } (geralmente não há métodos customizados na classe; mais na próxima seção)
  4. exportar uma função, de nome setup, que recebe como parâmetro o objeto do Sequelize para definição do modelo: export const setup = (sequelize) => { ... }
  5. definir o modelo dentro da função setup usando a API do Sequelize.

Implementando modelos

Geralmente, não é preciso implementar operações customizadas na maioria dos modelos, apenas as operações providas pelo Sequelize são suficientes (create, list, delete, ...).

Entretanto, se necessário, é possível criar métodos customizados na classe do modelo:

export default class PersonDAO extends Model {
  static performComplexQuery(parameters) {
    ...
  }
}
Clone repository
  • arquitetura
  • banco_dados
  • configuracao
  • docs usuario
  • gerenciamento_projeto
  • guia_desenvolvimento_backend
  • Home
  • instalacao
  • materiais_estudo
  • mockups
  • requisitos
  • reunioes
  • sprints
  • time