Backend do projeto de TCC do curso técninco de Desenvolvimento de Sistemas do SENAI Cetind.
Automatizar a planinlha de competência de docentes utilizada pelo SENAI.
- G77166 (segundo semestre de 2022)
- Ariely Floquet
- Francisco Mascrenhas
- Rafael Vecchi
- GET: lista todos os docentes ativos
- POST: cria um novo docente a partir do request
- GET: retorna docente
- POST: atualiza docente de acordo com o payload da request
- DELETE: desativa docente
Pelo Linux, os comandos a seguir devem ser precedidos por sudo
para dar
permissão de administrador.
Pelo Windows, rodar pelo Powershell iniciado como administrador ou usar a interface gráfica do Docker (Docker Desktop)
docker compose build
acrescente -d
ao final para rodar no plano de fundo
docker compose up
docker compose down
docker exec -it web bash
No terminal do container web
pytest
Versões compatíveis dessas dependências precisam estar instaladas para rodar o projeto.
- Python 3.10.6 (linguagem usada)
- Docker 20.10.17 (administra ambientes isolados)
O Docker irá instalar essas dependências
- Poetry 1.1.14 (gerenciador de pacotes)
- Django 4.1 (framework web)
- Django Rest Framework 3.13.1 (framework REST API)
- PostgreSQL 14.4 (banco de dados)
- psycopg2-binary 2.9.3 (requisito do PostgreSQL)
- Pytest 7.1.2 (framework de testes)
- Pytest Django 4.5.2 (plugin para testar aplicação Django com Pytest)
- Mockito 1.3.5 (framework para mock e spy em testes)
- Factory Boy 3.2.1 (ferramentas para criar fábricas de teste)
- constantes
- classes
- todo o resto
O diretório de testes unitários deve espelhar a estrutura que o projeto (módulos dominio e aplicacao)
As três convenções abaixo servem para que os testes sejam identificados pelo Pytest:
- O nome dos arquivos contento testes deve começar com
test_
- O nome das classes de teste deve começar com
Test
- O nome dos métodos de teste deve começar com
test_
Deve haveer uma classe de teste para cada classe a ser testada, de mesmo nome. A entidade Docente
, por
exemplo, será testada pela classe TestDocente
Deve ser dividido em três partes
- O QUE: nome do método a ser testado
- QUANDO: situação a ser testada
- ENTAO: resultado esperado
Exemplo: queremos testar se o método construir
do Docente
cria e atribui uma nova IdDeDocente
quando
a id
não é informada. O nome do teste seria:
class TestDocente(TestCase):
def test_construir_QUANDO_id_nao_informado_ENTAO_atribui_id_criado(self) -> None:
*Escrever QUANDO e ENTAO em maiúsculo mesmo
Deve ser dividido em três partes separadas por uma linha em branco:
- ATRIBUIR: construir o cenário do teste
- AGIR: chamar o método a ser testado
- AFIRMAR: comparar o resultado com o esperado
Exemplo: queremos testar se o método para_entidade
do OTDDocente
retorna a entidade esperada
(consideremos que Docente
tenha um único atributo nome
):
# ATRIBUIR
otd_docente = OTDDocente(nome='Nome do Docente')
# AGIR
docente_resultante = otd_docente.para_entidade()
# AFIRMAR
docente_esperado = Docente(nome=otd.nome)
self.assertEqual(docente_resultante, docente_esperado)
*Não incluir comentários
Sempre em portugês sem acento, a menos quando referencia algo externo (nesse caso deve usar o nome original)
- Começar o nome de uma classe sempre como O QUE ela é, seguido de DO QUE ela é
- Não usar acento
- Omitir a palavra 'de'
- Um modelo de docente deve chamar-se ModeloDocente
- Uma fábrica de testes de modelo de docente deve chamar-se FabricaTesteModeloDocente
Sempre que possível, organizar do mais externo (no topo) para o mais interno. Dividir em três sessões separadas por um espaço:
from __future__ import annotations
Demais imports externos (não criados por nós)
Imports de nossas criações (dominio ou aplicacao)
Essa camada é a ponte entre nosso domínio (ver abaixo) e a aplicação Django. É composta por:
No container ficam instanciadas classes que dependem de outras classes externas ao domínio, por exemplo: repositórios concretos, casos de uso.
Migrações do banco de dados. Ao alterar a forma que as entidades são representadas
no banco de dados, fazemos as migrações (para atualizar o banco) rodando os
comandos python manage.py makemigrations
e python manage.py migrate
(dentro do container do Docker)
Aqui ficam as representações das nossas entidades no banco de dados.
Aqui ficam os repositórios concretos que implementam os repositórios abstratos contidos no domínio
Serializers são responsáveis por converter entre OTD e JSON, traduzindo entre informações entre nossos casos de uso e a API.
Contém as rotas da nossa API. Para evitar problemas caso um dia a API venha a mudar, devemos separar por versões. A princípio iremos construir as rotas da apiv1 (versão 1 da API).
Aqui ficam as funções chamadas pelo sistema quando uma request for requisitada em uma rota. É onde fica a lógica da API.
Aqui ficam as configurações gerais do Django. A princípio não vamos mexer aqui.
Nessa camada vamos descrever nossa lógica de negócio em python puro. Ela deve ser independente e isolada do mundo externo, ou seja, não pode importar nada de fora. Faremos isso para isolar nossa lógica de negócio da lógica de infraestrutura. Devemos discutir como organiza-la e documentar nossas decisões nesse arquivo README. Nosso trabalho aconotecerá nessa camada na maior parte do tempo. A princípio, nela temos:
Aqui ficam classes que representam os casos de uso definidos na modelagem. A lógica de negócios está centralizda aqui, e são essas classes que serão chamadas quando alguma tarefa for requisitada. Essas classes devem conter um único método público chamado 'executar'.
Essa é a fonte da verdade quantos às nossas entidades. Elas serão manipuladas ao realizar as tarefas, as outras representações são meramente reflexos das entidades aqui descritas.
Nossos erros customizados. Um erro não é necessariamente um bug. Erros são esperados e carregam informações, e serão tratados pelo sistema. Todos os erros de domínio devem herdar da classe ErroDeDominio, para que fique clara a separação entre nossos erros customizados e os demais.
São classes muito simples (@dataclass) que representam atributos de nossas entidades. Elas devem ser capazes de se auto-validar e se comparar. Por exemplo, numa etidade 'Pessoa' com um atributo 'nome', esse atributo não seria uma string, e sim o objeto de valor chamado 'NomeDePessoa'. Ao instanciar esse objeto, ele faria as validações necessárias e lançaria um erro caso o nome fosse inválido.
OTD (Objeto de Transferência de Dados) são a última fronteira entre o domínio e mundo externo. Casos de uso recebem e retornam OTDs. Serializers os traduzem.
Repositórios são responsáveis por guardar objetos. Aqui ficam classes abstratas (que cumprem o papel de interfaces) dos repositórios. As classes concretas serão implementadas na camada de aplicação, pois irão lidar com questões da infraestrutura (banco de dados). Declaramos essas interfaces no domínio para que possamos fazer referência a elas sem 'sujar' o domínio com lógica de infraestrutura.
Aqui ficam os testes do projeto. É de extrema importância que tudo seja testado. Antes de submeter qualquer alteração é preciso rodar os testes e verificar que todos estão passando. A estrutura desse diretório é a seguinte:
Aqui ficam guardadas as fábricas de teste. Elas existem para facilitar a testagem.
Testes de integração têm o objetivo de testar a aplicação como um todo, com todas as partes integradas
Testes unitários têm o objetivo de testar cada mínima parte de forma isolada. Cada classe, método e função devem ser testados exaustivamente.
O Docker usa esse arquivo para definir as variáveis de ambiente.
O Git usa esse arquivo para saber quais pastas e arquivos devem ser ignorados pelo versionamento.
Esse arquivo descreve os serviços, redes e volumes da aplicação Docker.
É basicamente um script que o Docker usa para construir uma imagem.
É a porta de entrada da aplicação Django. Usamos ele para executar uma série de
tarefas do Django. Rodar esse arquivo (python manage.py
) traz uma lista de
comandos possíveis. Para rodar o servidor, por exemplo, usamos o comando
python manage.py runserver
. O Docker roda o servidor automaticamente ao
ativar um container, mas para outras tarefas, como migraçã de banco de dados,
iremos usá-lo manualmente (de dentro do container).
Arquivo usado pelo Poetry para previnir atualizações indesejáveis das dependências, garantindo assim um ambiente estável e previsível.
Arquivo usado pelo Poetry que contém informações do projeto e suas dependências.
- criar classe em
dominio/objetos_de_valor/
- criar testes (quando necessário) em
testes/unitarios/dominios/objetos_de_valor/
- criar fabrica teste em
testes/fabricas/dominio/objetos_de_valor/
- atualizar entidade que irá conter o objeto de valor criado em
dominio/entidades/
(ex: atualizarDocente
para incluirEmailDeDocente
) - atualizar fabrica teste da entidade modificada em
testes/fabricas/dominio/entidades/
- atualizar testes da entidade modificada em
testes/unitarios/dominio/entidades/
- atualizar OTD da entidade para incluir novo objeto de valor em
dominio/otds/
- atualizar testes dos OTDs modificados em
testes/unitarios/dominio/otds/
- atualizar modelo da entidade em
aplicacao/models/
- atualizar testes do modelo modificado em
testes/unitarios/aplicacao/models/
- atualizar fabrica teste do modelo modificado em
testes/fabricas/aplicacao/models/
- migrar modelo atualizado para o banco de dados. Executar no terminal do Docker container:
python manage.py makemigrations
epython manage.py migrate
- atualizar serializer dos OTDs referentes à entidade modificada em
aplicacao/serializers/