O projeto Futebol Club consiste em desenvolver uma API utilizando a metodologia TDD (Desenvolvimento Orientado por Testes) e integrar as aplicações front-end e back-end usando o Docker Compose. A API será consumida por um front-end já disponibilizado, que exibirá informações sobre partidas e classificações de futebol.
O objetivo principal do projeto é aplicar todos os conhecimentos adquiridos até o momento, incluindo a dockerização das aplicações, a modelagem de dados com MySQL usando o Sequelize, a criação e associação de tabelas, a construção de uma API REST com endpoints para consumir os modelos criados e a implementação de um CRUD com TypeScript utilizando o ORM.
Como responsável pelo desenvolvimento do back-end dockerizado, você utilizou os princípios de Programação Orientada a Objetos (POO) e os princípios SOLID para garantir a qualidade do código. Além disso, foi implementada a autenticação com JWT (JSON Web Token) para algumas rotas que exigem permissão, garantindo que apenas usuários autenticados possam fazer alterações.
No geral, o projeto busca simular um cenário real, onde é necessário integrar o front-end e o back-end, utilizando um banco de dados relacional e seguindo as regras de negócio estabelecidas. O resultado final será uma API funcional que fornecerá os dados necessários para o front-end exibir as informações de forma adequada aos usuários.
- Dockerização dos apps, network, volume e compose;
- Modelagem de dados com MySQL através do Sequelize;
- Criação e associação de tabelas usando models do sequelize;
- Construção de uma API REST com endpoints para consumir os models criados;
- Construção de um CRUD com TypeScript, utilizando ORM;
- Autenticação de rotas utilizando JWT.
- Programação Orientada a Objetos e princípios SOLID:
- Implementar, em TypeScript:
Classes
,Instâncias
,Atributos
eMétodos
.
- React js
- TypeScript
- Node.js
- Express.js
- Sequelize
- JSON Web Token (JWT)
- Bcrypt.js
- Mocha
- Chai
- Sinon
Diagrama e Tabelas
- MySQL
O banco de dados contém:
- tabela
users
com usuários válidos com hash das senhas e alguns inválidos, estes útimos utilizados para os testes avaliativos. - tabela
teams
com a lista de todos os times que estão participando do campeonato. - tabela
matches
com algumas partidadas finalizadas e outras em andamento.
Visão geral
Endpoint | Método HTTP | Descrição |
---|---|---|
/login |
POST | Faz o login com usuários do banco de dados |
/login/role |
GET | 🔐 Retorna o role do usuário logado (user ou adm) |
/teams |
GET | Retorna todos os times do campeonato |
/teams/:id |
GET | Retorna o time especificado no id |
/matches |
GET | Retorna todas as partidas |
/matches |
POST | 🔐 Insere uma nova partida em andamento. |
/matches?inProgress=true |
GET | Retorna as partidas em andamento. |
/matches?inProgress=false |
GET | Retorna as partidas finalizadas. |
/matches/:id |
PATCH | 🔐 Atualiza a partida de acordo com seu id. |
/matches/:id/finish |
PATCH | 🔐 Finaliza uma partida em andamento. |
/leaderboard |
GET | Retorna a classificação geral do campeonato. |
/leaderboar/home |
GET | Retorna a classificação dos times mandantes. |
/leaderboard/away |
GET | Retorna a classificação dos times visitantes. |
🔐 : Necessário que o token
gerado no login seja enviado no headers como "Authorization".
Clique nas setas para ver mais
-
Método POST
Exemplo de corpo da requisção válido{ "email": "user@user.com", "password": "secret_user", }
Respostas
-
Status: 200 OK
{ "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MSwicm9sZSI6ImFkbWluIiwiaWF0IjoxNjU0NTI3MTg5fQ.XS_9AA82iNoiVaASi0NtJpqOQ_gHSHhxrpIdigiT-fc" // jsonwebtoken gerado }
-
Status: 400 Bad Request
{ "message": "All fields must be filled" }
-
Status: 401 Unauthorized
{ "message": "Invalid email or password" }
-
-
🔐 Método GET
Respostas - Status: 200 OK ```json { "role": "admin" } ```-
Status: 401 Unauthorized
{ "message": "Token not found" }
{ "message": "Token must be a valid token" }
-
-
Método GET
Resposta
- Status: 200 OK
[ { "id": 1, "teamName": "Avaí/Kindermann" }, { "id": 2, "teamName": "Bahia" }, { "id": 3, "teamName": "Botafogo" }, ... ]
- Status: 200 OK
-
Método GET
Resposta
- Status: 200 OK
{ "id": 5, "teamName": "Cruzeiro" }
- Status: 200 OK
-
Método GET
Resposta
- Status: 200 OK
[ { "id": 1, "homeTeamId": 16, "homeTeamGoals": 1, "awayTeamId": 8, "awayTeamGoals": 1, "inProgress": false, "homeTeam": { "teamName": "São Paulo" }, "awayTeam": { "teamName": "Grêmio" } }, ... { "id": 41, "homeTeamId": 16, "homeTeamGoals": 2, "awayTeamId": 9, "awayTeamGoals": 0, "inProgress": true, "homeTeam": { "teamName": "São Paulo" }, "awayTeam": { "teamName": "Internacional" } } ]
- Status: 200 OK
-
🔐 Método POST
Requisição{ "homeTeamId": 16, // O valor deve ser o id do time "awayTeamId": 8, // O valor deve ser o id do time "homeTeamGoals": 2, "awayTeamGoals": 1, }
Respostas
-
Status: 201 Created
{ "id": 1, "homeTeamId": 16, "homeTeamGoals": 2, "awayTeamId": 8, "awayTeamGoals": 1, "inProgress": true, }
-
Status: 401 Unauthorized
{ "message": "Token not found" }
{ "message": "Token must be a valid token" }
-
Status: 404 Not Found
{ "message": "There is no team with such id!" }
-
Status: 422 Unprocessable Entity
{ "message": "It is not possible to create a match with two equal teams" }
-
-
Método GET
Opções de query: true ou falseEx:
matches?inProgress=true
Resposta
- Status: 200 OK
[ { "id": 41, "homeTeamId": 16, "homeTeamGoals": 2, "awayTeamId": 9, "awayTeamGoals": 0, "inProgress": true, "homeTeam": { "teamName": "São Paulo" }, "awayTeam": { "teamName": "Internacional" } }, { "id": 42, "homeTeamId": 6, "homeTeamGoals": 1, "awayTeamId": 1, "awayTeamGoals": 0, "inProgress": true, "homeTeam": { "teamName": "Ferroviária" }, "awayTeam": { "teamName": "Avaí/Kindermann" } } ]
- Status: 200 OK
-
🔐 Método PATCH
Requisição:
{ "homeTeamGoals": 3, "awayTeamGoals": 1 }
Respostas
-
Status: 200 OK
{ "message": "Updated match!" }
-
Status: 401 Unauthorized
{ "message": "Token not found" }
{ "message": "Token must be a valid token" }
-
-
🔐 Método PATCH
Respostas-
Status: 200 OK
{ "message": "Finished" }
-
Status: 401 Unauthorized
{ "message": "Token not found" }
{ "message": "Token must be a valid token" }
-
-
Método GET
Resposta
- Status: 200 OK
[ { "name": "Palmeiras", "totalPoints": 13, "totalGames": 5, "totalVictories": 4, "totalDraws": 1, "totalLosses": 0, "goalsFavor": 17, "goalsOwn": 5, "goalsBalance": 12, "efficiency": "86.67" }, ... { "name": "Napoli-SC", "totalPoints": 2, "totalGames": 6, "totalVictories": 0, "totalDraws": 2, "totalLosses": 4, "goalsFavor": 3, "goalsOwn": 15, "goalsBalance": -12, "efficiency": "11.11" } ]
- Status: 200 OK
-
Método GET
Resposta
- Status: 200 OK
[ { "name": "Santos", "totalPoints": 9, "totalGames": 3, "totalVictories": 3, "totalDraws": 0, "totalLosses": 0, "goalsFavor": 9, "goalsOwn": 3, "goalsBalance": 6, "efficiency": "100.00" }, ... { "name": "Bahia", "totalPoints": 0, "totalGames": 3, "totalVictories": 0, "totalDraws": 0, "totalLosses": 3, "goalsFavor": 0, "goalsOwn": 4, "goalsBalance": -4, "efficiency": "0.00" } ]
- Status: 200 OK
-
Método GET
Resposta
- Status: 200 OK
[ { "name": "Palmeiras", "totalPoints": 6, "totalGames": 2, "totalVictories": 2, "totalDraws": 0, "totalLosses": 0, "goalsFavor": 7, "goalsOwn": 0, "goalsBalance": 7, "efficiency": "100.00" }, ... { "name": "Napoli-SC", "totalPoints": 0, "totalGames": 3, "totalVictories": 0, "totalDraws": 0, "totalLosses": 3, "goalsFavor": 1, "goalsOwn": 10, "goalsBalance": -9, "efficiency": "0.00" } ]
- Status: 200 OK
Caso deseje contribuir ou simplesmente rodar o projeto na sua máquina, siga as orientações:
. Clone o repositório
git clone git@github.com:brenolg/Futebol-Club-API-Sequelize.git
. Instale as dependências no diretório raiz
npm install
- Na raíz do projeto rode o comando:
npm run compose:up
- Em seguida abra o terminal interativo do container:
docker exec -it app_backend sh
- Instale as dependências dentro do container:
npm install
Para rodar os testes de integração desenvolvidos por mim, entre na pasta backend e rode o comando:
npm test
Clique na seta para ver a lista de requisitos.
Docker
Configuração dos dockerfiles
referente ao front e back-end, para integrar as aplicações através do docker-compose, para que elas funcionem consumido o banco de dados.
Fluxo Teams
- Desenvolva uma migration e um model para a tabela de times, utilizando Sequelize.
(TDD)
Desenvolva testes de integração do back-end referente a implementação do requisito seguinte.- Desenvolva o endpoint
/teams
no back-end de forma que ele possa retornar a lista com todos os times corretamente. (TDD)
Evolua os testes de integração da sua rota /teams, agora considerando o contrato do próximo requisito.- Desenvolva o endpoint
/teams/:id
no back-end de forma que ele possa retornar dados de um time específico.
Fluxo User e Login
- Desenvolva uma migration e um model para a tabela de pessoas usuárias, utilizando Sequelize.
(TDD)
Desenvolva testes baseando-se no contrato do endpoint/login
do próximo requisito.- Desenvolva o endpoint
/login
no back-end de maneira que ele permita o acesso com preenchimento obrigatório deemail
epassword
no front-end e retorne umtoken
. (TDD)
Evolua os testes de integração da sua rota/login
, agora considerando o contrato do próximo requisito.- Desenvolva o endpoint
/login
no back-end de maneira que ele não permita o acesso com dados inválidos ou não cadastrados no banco de dados, considerando:- As senhas que existem no banco de dados estão encriptadas.
(TDD)
Desenvolva testes baseando-se no contrato do endpoint/login/role
do próximo requisito.- Desenvolva um middleware de validação para o
token
, verificando se ele é válido, e desenvolva o endpoint/login/role
no back-end de maneira que ele retorne os dados corretamente no front-end.⚠️ A rota deve recebe um header com parâmetro authorization, onde ficará armazenado otoken
gerado no login;
Fluxo Matches
- Desenvolva uma migration e um model para a tabela de partidas, utilizando Sequelize.
(TDD)
Desenvolva teste de integração, agora da sua rota/matches
, considerando os contratos dos próximos requisitos.- Desenvolva o endpoint
/matches
de forma retorna uma lista de partidas e que todos os dados de partidas sem nenhum filtro apareçam corretamente na tela de partidas no front-end. - Desenvolva o endpoint
/matches
de forma que seja possível filtrar somente as partidas em andamento, e também filtrar somente as partidas finalizadas, na tela de partidas do front-end.- Essa requisição deverá usar
query string
para definir o parâmetro.
- Essa requisição deverá usar
- Desenvolva o endpoint
/matches/:id/finish
de modo que seja possível finalizar uma partida no banco de dados.⚠️ Não é possível alterar uma partida sem umtoken
;
- Desenvolva o endpoint
/matches/:id
de forma que seja possível atualizar partidas em andamento.⚠️ Não é possível atualizar uma partida sem umtoken
;
(TDD)
Desenvolva testes de integração, agora da sua rota/matches
, considerando os contratos dos próximos requisitos.- Desenvolva o endpoint
/matches
de modo que seja possível cadastrar uma nova partida em andamento no banco de dados e retornar os dados inserida no banco de dados.⚠️ Não é possível atualizar uma partida sem umtoken
;
- Desenvolva o endpoint
/matches
de forma que não seja possível inserir uma partida com times iguais nem com um time que não existe na tabela de times.
Fluxo Leaderboards
-
Regras de negócio para classificação dos times
Todas as regras de negócio e cálculos necessários deverão ser realizados no back-end. A aplicação front-end apenas renderizará essas informações.
- A tabela deverá renderizar somente as partidas que já foram FINALIZADAS.
Classificação: Posição na classificação; Time: Nome do time; P: Total de Pontos; J: Total de Jogos; V: Total de Vitórias; E: Total de Empates; D: Total de Derrotas; GP: Gols marcados a favor; GC: Gols sofridos; SG: Saldo total de gols; %: Aproveitamento do time.
O resultado deverá ser ordenado sempre de forma decrescente, levando em consideração a quantidade de pontos que o time acumulou.
Em caso de empate noTotal de Pontos
, você deve levar em consideração os seguintes critérios para desempate:- 1º Total de Vitórias;
- 2º Saldo de gols;
- 3º Gols a favor;
-
Leaderboard Home
- Desenvolva o endpoint
/leaderboard/home
de forma que retorne as informações do desempenho dos times da casa com as seguintes propriedades:name
,totalPoints
,totalGames
,totalVictories
,totalDraws
,totalLosses
,goalsFavor
egoalsOwn
. - Desenvolva o endpoint
/leaderboard/home
de forma que seja possível filtrar as classificações dos times da casa na tela de classificação do front-end com os dados iniciais do banco de dados, incluindo as propriedadesgoalsBalance
eefficiency
, além das propriedades do requisito anterior. - Desenvolva o endpoint
/leaderboard/home
de forma que seja possível filtrar as classificações dos times da casa na tela de classificação do front-end, e atualizar a tabela ao inserir a partida Corinthians 2 X 1 Internacional.
- Desenvolva o endpoint
-
Leaderboard away
- Desenvolva o endpoint
/leaderboard/away
de forma que retorne as informações do desempenho dos times visitantes com as mesmas propriedades do req. 23. - Desenvolva o endpoint
/leaderboard/away
, de forma que seja possível filtrar as classificações dos times quando visitantes na tela de classificação do front-end, com os dados iniciais do banco de dados, incluindo as propriedadesgoalsBalance
eefficiency
, além das propriedades do requisito anterior. - Desenvolva o endpoint
/leaderboard/away
de forma que seja possível filtrar as classificações dos times quando visitantes na tela de classificação do front-end e atualizar a tabela ao inserir a partida Corinthians 2 X 1 Internacional.
Gostaria de expressar minha profunda gratidão aos monitores e à Trybe por todo o suporte e orientação ao longo do projeto. Sua dedicação, conhecimento e disposição em ajudar foram fundamentais para o meu aprendizado e crescimento profissional. Sou grato pela oportunidade de fazer parte dessa comunidade de aprendizagem e por todo o apoio recebido durante essa jornada.