Sistema de auto-atendimento de fast food. Projeto de conclusão da Fase 03 da pós gradução em Software Architecture. TLDR; Execução em modo produção (deprecated)
- Arquitetura de Solução (Cloud AWS)
- Arquitetura de Software
- Instalação de Dependências Node
- Build da Aplicação
- Executando a Aplicação
- Documentação da API (Swagger)
- Desinstalação & Cleanup
- Testes
- Estrutura Base do Projeto
- Cloud AWS
- Banco de Dados
- DDD
- Cloud AWS
- API Gateway, Cognito, ECS, Lambda, Load Balancer, RDS
- Arquitetura Clean & Modular
- Camada de Application, Enterprise, Presentation e Infrastructure
- Módulo Main, Application, Presentation e Infrastructure
- Principais Tecnologias/Frameworks
- Docker, Kubernetes, Helm, Kubectl, NodeJS, NestJS, TypeORM, NPM, Mysql, Swagger, Typescript, Jest
- Qualidade / Testes
- Validações pré-commit/push
- Validação de cobertura de testes
- Testes unitários, e2e em memória (all green)
- Validação de implementação de testes (modo alerta para implementação de testes de rest apis, services, usecases, validators, repositories)
- CI/CD
- Pipeline Github Actions para integração com a
main
- fast-n-foodious-ci: run-unit-tests - Execução de testes unitários (all green)
- fast-n-foodious-ci: run-e2e-mysql - Execução de testes e2e com mysql (all green)
- fast-n-foodious-ci: run-e2e-in-memory - Execução de testes e2e em memória (all green)
- fast-n-foodious-ci: run-coverage-tests - Execução de validação de cobertura de testes (all green)
- fast-n-foodious-ci: run-check-test-impl - Execução de validação de implementação de testes (mandatório para rest apis, services, usecases, validators, repositories)
- fast-n-foodious-ci: build - Build de imagens docker (AMD & ARM) e publicação no DockerHub
- Pipeline Github Actions para integração com a
- Validações pré-commit/push
Nota: Nas instruções abaixo, se assume que o diretório onde os comandos serão executados será a posta raiz do projeto ~/fast-n-foodious.
$ npm install
$ npm run build
O sistema pode ser executado com ou sem dependências externas.
NODE_ENV
como variável de ambiente, com os seguintes valores:
# env_name:
- local-mock-repository # Variáveis usadas para rodar a aplicação em ambiente local, SEM dependência de container mysql
# Exemplo de caso de uso: debugar local rodando com o banco em memória
# $ NODE_ENV=local-mock-repository npm run start:debug
- local # Variáveis usadas para rodar a aplicação em ambiente local, COM dependência de container mysql
# Presume mysql rodando e a necessidade de atachar a aplicação ao container para desenvolver
# Exemplo de caso de uso: debugar local e apontando para o banco no container.
# $ NODE_ENV=local npm run start:debug
- prod # Variáveis usadas para rodar a aplicação em ambiente de produção, COM dependøencia de container mysql
# $ NODE_ENV=prod npm run start:debug
# Desenvolvimento
$ NODE_ENV={env_name} npm run start
# Modo Watch
$ NODE_ENV={env_name} npm run start:dev
# Modo Debug
$ NODE_ENV={env_name} npm run start:debug
# Modo Produção
$ npm run start:prod
Nota: Se informado o env_name DIFERENTE de local-mock-repository
, o modo de desenvolvimento, watch, debug e produção vai depender de ter um container mysql em execução.
Utilizado apenas para desenvolvimento local, modo watch, debug, testes unitários e e2e
. Executa a aplicação em modo local, com repositório em memória:
$ NODE_ENV=local-mock-repository npm run start
Utilizado apenas para desenvolvimento local, modo watch, debug, testes e2e
. Inicia o contianer mysql com as variáveis locais e inicia a aplicação (fora do container)
com as variáveis locais:
$ docker-compose --env-file ./envs/local.env up mysql
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
83c9b4d8880a mysql:8.0 "docker-entrypoint.s…" 3 seconds ago Up 2 seconds 0.0.0.0:3306->3306/tcp, 33060/tcp mysql
# Executa a aplicação com as variáveis locais, conectando no container do mysql
$ NODE_ENV=local npm run start
Nota 1: O K8S foi substituído pelo serviço gerenciado AWS Fargate. A construção da insfraestrura é realizada através de IaC (Terraform) com seus respectivos scripts em repositórios específicos de Storage, Compute e Network. A documentação abaixo apenas ilustra a solução v2.0.0 e foi mantida aqui caso seja necessário subir a aplicação de uma maneira mais fácil para avaliação dos instrutores.
Nota 2: O container da aplicação depende do mysql estar up & running. Então seja paciente, o tempo para o container do mysql estar disponível pode veriar, dependendo da disponibilidade de recursos e suas configurações de hardware locais.
Inicia o container da aplicação e do mysql com as variáveis de produção, utilizando o docker compose:
$ docker-compose --env-file ./envs/prod.env build
$ docker-compose --env-file ./envs/prod.env up -d
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
2a0f11e4ffe3 fast-n-foodious "docker-entrypoint.s…" 5 seconds ago Up 4 seconds 0.0.0.0:3000->3000/tcp fast-n-foodious
06ebf6b90fa7 mysql:8.0 "docker-entrypoint.s…" 5 seconds ago Up 4 seconds 0.0.0.0:3306->3306/tcp, 33060/tcp mysql
Inicia o container da aplicação e do mysql com as variáveis de produção, utilizando imagens docker
do mysql e da aplicação:
$ docker network create fast-n-foodious-network
$ docker run -d --rm --name mysql -p 3306:3306 \
--env-file ./envs/prod.env --network fast-n-foodious-network \
-v ./scripts/schema:/docker-entrypoint-initdb.d \
-v mysql-data:/data/db \
mysql:8.0
$ docker run -d --rm --name fast-n-foodious -p 3000:3000 \
--env-file ./envs/prod.env --network fast-n-foodious-network \
ottero/fast-n-foodious:latest
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
88bf7eae7e46 ottero/fast-n-foodious:latest "docker-entrypoint.s…" 2 seconds ago Up 1 second 0.0.0.0:3000->3000/tcp fast-n-foodious
8b0268d435a6 mysql:8.0 "docker-entrypoint.s…" 6 seconds ago Up 5 seconds 0.0.0.0:3306->3306/tcp, 33060/tcp mysql
Inicia o pod da aplicação e do mysql com as variáveis de produção, assim como suas dependências (services, deployments, replicasets, hpas, configmaps, secrets, pv, pvc) utilizando o helm: Nota: Assume k8s pod/metrics-server up & running para habilitação de escalabilidade via HPA
$ helm install fast-n-foodious helm/
NAME: fast-n-foodious
LAST DEPLOYED: Mon Aug 21 22:02:05 2023
NAMESPACE: default
STATUS: deployed
REVISION: 1
TEST SUITE: None
$ kubectl get all
NAME READY STATUS RESTARTS AGE
pod/fast-n-foodious-5c6cbcbf76-v4bgd 1/1 Running 1 (2m29s ago) 3m28s
pod/mysql-595c5c9d4f-x7grb 1/1 Running 0 3m28s
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/fast-n-foodious-svc LoadBalancer 10.97.158.122 localhost 80:30000/TCP 3m28s
service/kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 9d
service/mysql ClusterIP 10.109.101.116 <none> 3306/TCP 3m28s
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/fast-n-foodious 1/1 1 1 3m28s
deployment.apps/mysql 1/1 1 1 3m28s
NAME DESIRED CURRENT READY AGE
replicaset.apps/fast-n-foodious-5c6cbcbf76 1 1 1 3m28s
replicaset.apps/mysql-595c5c9d4f 1 1 1 3m28s
NAME REFERENCE TARGETS MINPODS MAXPODS REPLICAS AGE
horizontalpodautoscaler.autoscaling/fast-n-foodious-hpa Deployment/fast-n-foodious 46%/70%, 0%/70% 1 3 1 3m28s
Inicia o pod da aplicação e do mysql com as variáveis de produção, assim como suas dependências (services, deployments, replicasets, hpas, configmaps, secrets, pv, pvc) utilizando o CLI kubectl: Nota: Assume k8s pod/metrics-server up & running para habilitação de escalabilidade via HPA
$ kubectl apply -f k8s/fast-n-foodious-secret.yml
secret/fast-n-foodious-secret created
$ kubectl apply -f k8s/fast-n-foodious-configmap.yml
configmap/fast-n-foodious-env created
configmap/mysql-env created
$ kubectl apply -f k8s/fast-n-foodious-pv.yml
persistentvolume/fast-n-foodious-pv created
$ kubectl apply -f k8s/fast-n-foodious-pvc.yml
persistentvolumeclaim/fast-n-foodious-pvc created
$ kubectl apply -f k8s/fast-n-foodious-deployment.yml
deployment.apps/fast-n-foodious created
deployment.apps/mysql created
$ kubectl apply -f k8s/fast-n-foodious-service.yml
service/fast-n-foodious-svc created
service/mysql created
$ kubectl apply -f k8s/fast-n-foodious-hpa.yml
horizontalpodautoscaler.autoscaling/fast-n-foodious-hpa created
$ kubectl get all
NAME READY STATUS RESTARTS AGE
pod/fast-n-foodious-7fc6f95bdb-krcnm 1/1 Running 0 2m58s
pod/mysql-595c5c9d4f-5vpj8 1/1 Running 0 2m58s
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/fast-n-foodious-svc LoadBalancer 10.110.74.44 localhost 80:30000/TCP 2m53s
service/kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 5m52s
service/mysql ClusterIP 10.108.3.249 <none> 3306/TCP 2m53s
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/fast-n-foodious 1/1 1 1 2m59s
deployment.apps/mysql 1/1 1 1 2m59s
NAME DESIRED CURRENT READY AGE
replicaset.apps/fast-n-foodious-7fc6f95bdb 1 1 1 2m59s
replicaset.apps/mysql-595c5c9d4f 1 1 1 2m58s
NAME REFERENCE TARGETS MINPODS MAXPODS REPLICAS AGE
horizontalpodautoscaler.autoscaling/fast-n-foodious-hpa Deployment/fast-n-foodious 69%/80%, 0%/80% 1 3 1 2m48s
docker
http://localhost:3000/api
Para realizar a desistalação da aplicação e o cleanup da infraestrutura, basta realizar os comandos abaixos de acordo com o modo de instalação.
- Se você utilizou o
docker
para subir a aplicação:
$ docker stop mysql fast-n-foodious
mysql
fast-n-foodious
$ docker volume rm mysql-data
mysql-data
$ docker network rm fast-n-foodious-network
fast-n-foodious-network
docker image rm ottero/fast-n-foodious
Untagged: ottero/fast-n-foodious:latest
Untagged: ottero/fast-n-foodious@sha256:58d0731f992f2755ee311a25603fde8c8c9ecd57e3f5aad34c32b41783284625
Deleted: sha256:e206061037e125c6b6b93bcc3b3ef61a59d8919753759d34527e38abe17c712e
Deleted: sha256:8cc3b430e851d9d31ff5049bb95e8032398a32203b7fbc49d1ac0ef65b4d1387
Deleted: sha256:a7fa60af5472f99af1f84d0f245d8e64f3897dcbd02f0c63f1817a09479a31cd
Deleted: sha256:3b012aad6f4a48c30a61d8834cebd0a48d3ef2e0680cd86545243618f782d778
Deleted: sha256:f93cb6531dabccc23848e273402d3fbef0515206efab1a29ccc1be81bf273dea
- Se você utilizou o
docker compose
para subir a aplicação:
$ docker-compose --env-file ./envs/prod.env down -v
[+] Running 4/4
✔ Container fast-n-foodious Removed 0.8s
✔ Container mysql Removed 1.1s
✔ Volume fast-n-foodious_mysql-data Removed 0.0s
✔ Network fast-n-foodious_fast-n-foodious-network Removed 0.1s
$ docker image rm fast-n-foodious-fast-n-foodious
Untagged: fast-n-foodious-fast-n-foodious:latest
Deleted: sha256:357edf598a86260a5d755b8739b8be3ecd761ed51f8c9a84a5d32b93971e3e5e
- Se você utilizou o
helm
para subir a aplicação:
$ helm uninstall fast-n-foodious
release "fast-n-foodious" uninstalled
- Se você utilizou o
kubeclt
para subir a aplicação:
$ kubectl delete -f k8s/fast-n-foodious-hpa.yml
horizontalpodautoscaler.autoscaling "fast-n-foodious-hpa" deleted
$ kubectl delete -f k8s/fast-n-foodious-service.yml
service "fast-n-foodious-svc" deleted
service "mysql" deleted
$ kubectl delete -f k8s/fast-n-foodious-deployment.yml
deployment.apps "fast-n-foodious" deleted
deployment.apps "mysql" deleted
$ kubectl delete -f k8s/fast-n-foodious-pvc.yml
persistentvolumeclaim "fast-n-foodious-pvc" deleted
$ kubectl delete -f k8s/fast-n-foodious-pv.yml
persistentvolume "fast-n-foodious-pv" deleted
$ kubectl delete -f k8s/fast-n-foodious-configmap.yml
configmap "fast-n-foodious-env" deleted
configmap "mysql-env" deleted
$ kubectl delete -f k8s/fast-n-foodious-secret.yml
secret "fast-n-foodious-secret" deleted
$ docker image rm ottero/fast-n-foodious
Untagged: ottero/fast-n-foodious:latest
Untagged: ottero/fast-n-foodious@sha256:58d0731f992f2755ee311a25603fde8c8c9ecd57e3f5aad34c32b41783284625
Deleted: sha256:e206061037e125c6b6b93bcc3b3ef61a59d8919753759d34527e38abe17c712e
Deleted: sha256:8cc3b430e851d9d31ff5049bb95e8032398a32203b7fbc49d1ac0ef65b4d1387
Deleted: sha256:a7fa60af5472f99af1f84d0f245d8e64f3897dcbd02f0c63f1817a09479a31cd
Deleted: sha256:3b012aad6f4a48c30a61d8834cebd0a48d3ef2e0680cd86545243618f782d778
Deleted: sha256:f93cb6531dabccc23848e273402d3fbef0515206efab1a29ccc1be81bf273dea
- Extra: se os testes de stress foram realizados no cluster kubernetes, via job k6:
$ kubectl delete -f k8s/fast-n-foodious-job.yml
job.batch "k6-stress-job" deleted
configmap "k6-stress-env" deleted
$ docker image rm 24hoursmedia/k6-xarch
Untagged: 24hoursmedia/k6-xarch:latest
Untagged: 24hoursmedia/k6-xarch@sha256:62f55c01e327d15bef89275168cab9a7bb11c8450203bf15d052cfe2654d8a29
Deleted: sha256:0ea08d7adac52324b25f57d126491c6b7c2bf48ea0c714c893cdcebc1f2b8929
Deleted: sha256:4f90d3b645cdd7184811448c570951ee7c3c032770c1956f25e8fcdfd4d79e9b
Deleted: sha256:6f16c4dda6e7ae2562218ba06bae1285ff33934b991620db4f591ac60d35ee5c
Deleted: sha256:0f7b3ff8b310adb0c38fa8108967e51e3431bc4b7ce350de93839eeffcefd34c
# Build com docker-compose utilizando env específica
$ docker-compose --env-file ./envs/{env-name}.env build
# Execução dos serviços registrados no docker-compose utilizando env específica
$ docker-compose --env-file ./envs/{env-name}.env up
# Execução de um serviço registrados no docker-compose utilizando env específica
$ docker-compose --env-file ./envs/{env-name}.env up {service}
# Interrupção dos serviços registrados no docker-compose utilizando env específica
$ docker-compose --env-file ./envs/{env-name}.env down
# Interrupção de um serviço registrados no docker-compose utilizando env específica
$ docker-compose --env-file ./envs/{env-name}.env down {service}
Nota: Os serviços registrados no docker-compose são:
- fast-n-foodious
- mysql
O projeto cobre testes unitários, testes e2e e testes isolados de api (para desenvolvedor), além de verifiar a cobertura dos testes:
# Execução de testes unitários
$ npm run test
# Execução de cobertura de testes
$ npm run test:cov
# Execução de testes e2e SEM dependência de banco de dados (in-memory repository)
$ NODE_ENV=local-mock-repository npm run test:e2e
# Execução de testes e2e COM dependência de banco de dados (mysql repository)
# 1. Necessita do container mysql em execução!
# 2. Considere remover o volume criado no mysql caso execute o teste mais de uma vez!
$ NODE_ENV=local npm run test:e2e
Excução de testes de stress cluster k8s, utilizando job k6. Nota: A execução tem duração de 60s, estressando o path /v1/categoria. Assume a aplicação e mysql up & running no cluster kubernetes
$ kubectl apply -f k8s/fast-n-foodious-job.yml
job.batch/k6-stress-job created
configmap/k6-stress-env created
$ kubectl get po
NAME READY STATUS RESTARTS AGE
fast-n-foodious-5c6cbcbf76-n5vn5 1/1 Running 1 (6m49s ago) 7m46s
fast-n-foodious-5c6cbcbf76-q5q7t 1/1 Running 0 106s
k6-stress-job-fkjv9 1/1 Running 0 6s
mysql-595c5c9d4f-chlrx 1/1 Running 0 7m46s
$ kubectl logs -f k6-stress-job-fkjv9
/\ |‾‾| /‾‾/ /‾‾/
/\ / \ | |/ / / /
/ \/ \ | ( / ‾‾\
/ \ | |\ \ | (‾) |
/ __________ \ |__| \__\ \_____/ .io
execution: local
.github/ # Configurações de pipelines CI/CD
docs/ # Documentação da aplicação
envs/ # Configurações de ambiente
helm/ # Configuração de descriptors Helm
k8s/ # Configuração de descriptors kubernetes
scripts/ # Scripts gerais de inicialização e validação (git prepush, precommit - cobertura de testes, testes unitários, e2e MySQL e memória)
src/ # Source da solução
├── application # Camada de Application (use cases, validators)
│ ├── categoria
│ ├── cliente
│ │ └── providers # Registro de providers (services, usecases, validators). utilizados via DI
│ │ └── service # Serviços (controllers) de composição de casos de uso
│ │ └── usecase # Casos de usos
│ │ └── validation # Validators (regras de negócio)
│ ├── item-pedido
│ ├── pagamento
│ ├── pedido
│ └── produto
├── enterprise # Camada Enterprise (domínio)
│ ├── categoria
│ ├── cliente
│ │ ├── model # Entidades de domínio
│ ├── exception # Exceções de domínio
│ ├── item-pedido
│ ├── pagamento
│ ├── pedido
│ ├── produto
│ ├── repository # Portas de repositórios da camana de domínio
│ ├── service # Portas de serviços da camana de domínio
│ └── validation # Contrato de validações da camada de domínio
├── infrastructure # Camada Infrastructure (banco de dados, ORM)
│ ├── exception # Exceções de infraestrutura
│ └── persistence
│ ├── categoria
│ ├── cliente
│ │ ├── entity # Entitdades ORM
│ │ └── repository # Repositórios (mysql, in-memory)
│ ├── item-pedido
│ ├── mysql # Configurações de banco de dados MySQL
│ ├── pagamento
│ ├── pedido
│ ├── produto
│ ├── providers # Registro de providers (repositorório in-memory, typeorm). utilizados via DI
├── presentation # Camada Presentation (rest api)
│ └── rest
│ │ ├── categoria
│ │ ├── cliente
│ │ │ ├── api # Rest API
│ │ │ ├── request # Contratos de entrada
│ │ │ └── response # Contratos de saída
│ │ ├── handler # Handlers para tratamento centralizado de exceções (ValidationException, DomainException)
│ │ ├── item-pedido
│ │ ├── pagamento
│ │ ├── pedido
│ │ ├── produto
│ │ ├── response # Contrato de resposta de erro http padrão
│ └── swagger # Configurações (constantes) Swagger
└── shared # Itens compartilhados
test/ # Implementações de testes
├── api # Testes de API (utilitário de desenvolvimento)
├── e2e # Testes E2E
└── stress # Testes de stress (k6 e/ou cluster k8s)