← Voltar aos projetos

Projeto Gestão Financeira - End-to-End

Gestão Financeira

Publicado em: EM DESENVOLVIMENTO

Descrição do Desafio

Projeto de Desenvolvimento End-to-End, ou seja, conta com um sistema de Gestão Financeira com Front-end e Back-end, arquitetura e gerenciamento de containers e observabilidade.

Contexto do Servidor

Antes de inciar este projeto, meu servidor VPS possuia apenas um sistema operacional Rocky Linux na versão 9.5.
Sendo assim foi necessário utilizar alguns comandos com o objetivo de instalar o Docker de maneira correta e segura.

[root ~] sudo dnf config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo

Comando utilizado para apontar o servidor oficial e seguir com a adicição do repositório dentro da VPS.


[root ~] sudo dnf install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin

Comando utilizado para instalar os pacotes necessários.


[root ~] sudo systemctl enable --now docker

No Rocky Linux, é necessário habilitar um módulo para que o serviço inicie de modo automático sempre que o servidor ligar.


[root ~] sudo systemctl status docker

Comando para validar o status do serviço.


O Rocky Linux vem com um Firewall, o firewalld, ativo por padrão. Então é preciso ter em mente que se o projeto for rodar na porta 8000, por exemplo, será preciso avisar o servidor.

[root ~] sudo firewall-cmd --state

Comando utilizado para validar que o Firewall está ativo. Se o retorno for running, ele está ativo.


[root ~] sudo firewall-cmd --list-all

Esse comando lista todas as regras atuais do servidor. Para validar as informações referentes as portas abertas, é só procurar pela linha ports: . Se ela estiver vazia, significa que apenas os serviços padrão como: SSH na porta 22, estão abertos.


Para este projeto vou utilizar a porta 8000 para evitar conflito com outros sites que já estão rodando.

[root ~] sudo firewall-cmd --permanent --add-port=8000/tcp

Este é o comando para abrir a porta 8000. Após rodar o comando, deve retornar success.

[root ~] sudo firewall-cmd --reload

Comando utilizado para aplicar um reload no Firewall e aplicar a nova mudança.

Nesse ponto aqui a estrutura inicial do Docker está configurada corretamente em minha VPS.

Estrutura de Pastas e Arquivos

Comecei criando o diretório do projeto dentro do servidor:

[root ~] mkdir gestao_financeira

Agora vou seguir para o VS Code para criar os arquivos iniciais.

VS Code

Para construir o ambiente dentro do Docker e manter o conrtole de versão de maneira segura e correta alguns arquivos padrão devem ser criados:

Vou falar sobre cada um deles:

Seguindo agora, vou falar um pouco sobre o Framework utilizado no Projeto.

Django Framework

Mas antes de seguirmos:

O que é um Framework?

Um Framework é um conjunto de ferramentas e bibliotecas e padrões predefinidos que funcionam como base para o desenvolvimento de um software, o que facilita a criação de aplicações de maneira mais rápida, eficiente e padronizada. O Framework elimina a necessidade de reescrever códigos comuns.

O que é Django?

É um Framework Python que peermite criar aplicações web de forma rápida e eficiente, cuidando de tarefas comuns como autenticação~, segurança e interação com banco de dados. Dessa forma, o desenvolvedor pode focar na lógica do projeto.

Ele segue o padrão MVT (Model-View-Template) e oferece ferramentas como ORM (Object-Relational-Mapper) para facilitar o trabalho com banco de dados e um painel administrativo.

Para desenvolver um Projeto utilizando o Framework Django, é necessário entrar que os Frameworks possuem uma estrutura que deve ser respeitada para que a aplicação funcione corretamente. Sendo assim, vou explicar um pouco sobre a estrutura do Django e alguns conceitos básicos que serão pontuados na etapa de Desenvolvimento.

Arquitetura MVT (Model-View-Template)

Conceitos

Alguns conceitos básicos e importantes que devemos conhecer para entender melhor o Desenvolvimento do sistema.

Estrutura de Arquivos Django

No Django a estrutura é organizada em projetos e aplicativos baseada no padrão MVT.

E por fim, vamos falar sobre o Banco de Dados escolhido.

PostgreSQL

O PostgreSQL é um SGBD (Sistema de Gerenciamento de Banco de Dados) Relacional de código aberto. Ele não apenas armazena informações; ele garante que as relações entre elas sejam sólidas e confiáveis.

No PoupAí, o Postgres atua como o repositório seguro para cada transação, meta e categoria criada pelos usuários.

Porque optei por utilizar o Postgres e não o SQLite que é padrão do Django?

Durante o desenvolvimento local, o Django costuma usar o SQLite. Porém, a minha ideia desde o começo era seguir com um deploy para o meu servidor VPS, então escolhi o Postgres pelos seguintes motivos:

O Postgres é "Relacional" porque organiza os dados em tabelas que se conectam através de Chaves. No PoupAí, temos três conceitos fundamentais:

O Postgre segue o princípio ACID, que é vital para sistemas de gestão financeira:

Agora que você entendeu alguns conceitos básicos e as ferramentas utilizadas, posso seguir para uma parte um pouco mais complexa.

Arquitetura

[ADICIONAR IMAGEM]

A imagem acima mostra a arquitetura que criei para este projeto.

O PoupAí possui uma robustez que não se limita apenas ao código, mas na forma como os componentes se comunicam de maneira segura e eficente dentro da infraestrutura. O diamagra da arquitetura detalha 5 (cinco) camadas principais.

Nesse ponto aqui, você já deve ter entendido o que é o Django e seus conceitos principais, o motivo de eu ter escolhido utilizar o PostgreSQL e a Arquitetura da aplicação.

Agora podemos seguir para o desenvolvimento do ambiente e da aplicação

Desenvolvimento

Vou dividir esse tópico em duas partes e vou dividir essas partes em subtópicos explicando os passos:

Agora posso iniciar a explicação.

Desenvolvimento do Ambiente

O desenvolvimento do ambiente envolve toda a parte da criação e configuração do container. Esse passo antecede o desenevolvimento do sistema.

.gitignore

Como foi explicado anteriormente, o arquivo .gitignore fica na raiz do projeto e funciona como um filtro para o Git, ou seja, tudo o que for declarado nesse arquivo será ignorado pelo Git e não será enviado para o repositório.

Existem algumas coisas que são comumente usadas em muitos projetos, então é sim possível buscar algum template de .gitignore e você apenas adiciona o que é específico do seu projeto.

Vou explicar aqui apenas o que é específico para o projeto:

Os demais arquivos que o .gitignore está ignorando podem ser vistos no repositório do projeto no GitHub.

Dockerfile

É como uma receita de bolo para criar uma Imagem Docker. Ele contém um conjunto de instruções em sequência que o Docker lê para montar um ambiente isolado, o que garante que o projeto rode exatamente da mesma forma tanto localmente quanto em meu Servidor VPS.

Vou explicar o código detalhadamente:

FROM python:3.11.3-alpine3.18
LABEL mantainer="https://suzanacavalcante.com.br"

FROM: Define a imagem pai. Aqui estou utilizando a versão Alpine, que é uma versão Linux muito leve e tem foco em segurança, o que é perfeito para a produção.

LABEL: Apenas um metadado informando quem é o mantenedor do projeto.


ENV PYTHONDONTWRITEBYTECODE 1
ENV PYTHONUNBUFFERED 1

PYTHONDONTWRITEBYTECODE: Impede que o Python gere arquivos .pyc dentro do container.

PYTHONUNBUFFERED: Garante que os logs do Python apareçam em tempo real no terminal do Docker.


COPY ./djangoapp /djangoapp
COPY ./scripts /scripts
WORKDIR /djangoapp
EXPOSE 8000

COPY: Copia os arquivos informados do local de desenvolvimento para dentro do container.

WORKDIR: Define que, a partir de agora, qualquer comando será executado dentro da pasta /djangoapp.

EXPOSE: Informa que o Container terá a porta 8000.


RUN python -m venv /venv && \
/venv/bin/pip install --upgrade pip && \
/venv/bin/pip install -r /djangoapp/requirements.txt && \
adduser --disabled-password --no-create-home duser && \ 
mkdir -p /data/web/static && \ 
mkdir -p /data/web/media && \ 
chown -R duser:duser /data/web/static && \
chmod -R 755 /data/web/static && \ 
chmod -R +x /scripts 
                    

RUN python -m venv /venv && \: Cria o ambiente virtual

/venv/bin/pip install --upgrade pip && \: Atualiza o gerenciador de pacotes

/venv/bin/pip install -r /djangoapp/requirements.txt && \: Instala as bibliotecas do PoupAí

adduser --disabled-password --no-create-home duser && \: Cria um usuário do sistema

mkdir -p /data/web/static && \: Cria pastas para CSS/JS

mkdir -p /data/web/media && \: Cria pastas para uploads

chown -R duser:duser /data/web/static && \: Dá permissão para o usuário duser

chmod -R 755 /data/web/static && \: Define permissões de leitura/escrita

chmod -R +x /scripts: Torna seus scripts de inicialização executáveis


ENV PATH="/scripts:/venv/bin:$PATH"
USER duser
CMD ["commands.sh"]

ENV PATH: Informa ao Linux do Container onde buscar quando um comando for iniciado.

USER duser: Essencial para a segurança do Container. Por padrão, o Docker roda como root, então, ao mudar para duser, é garantido que, se alguèm invadir a aplicação, não terá controle total sobre o servidor.

CMD: É o comando final e só roda quando o container está ativo. Aqui estou chamando o script que inicia o servidor Django.


docker-compose.yml

É uma ferramenta de orquestração que permite definir e rodar aplicações de múltiplos containers. No caso do PoupAí, existem dois containers principais: o servidor web (Django) e o banco de dados (PostgreSQL). Este arquivo especifica como eles devem se conectar, quais portas utilizar e etc...

version: '3.9'
services:

version: Define a versão da sintaxe do Docker Compose que será utilizada.

services: Inicia a lista de todos os containers que farão parte da aplicação.


 djangoapp:
  restart: always
  container_name: djangoapp
  build:
    context: .
  ports:
    - 8000:8000

restart: always: Se o container cair, o Docker tentará ligá-lo automaticamente.

build: context: .: Diz ao Docker para procurar o Dockerfile na pasta atual para construir a imagem deste serviço.

ports: - 8000: 8000: Faz a ponte entre a requisição de acesso à aplicação e o container.


volumes:
    - ./djangoapp:/djangoapp
    - ./data/web/static:/data/web/static
    - ./data/web/media:/data/web/media

volumes: É um espelhamento de diretórios. O que é armazenado dentro do diretório ./djangoapp do Container refle instantaneamente na VPS. É fundamental para que os arquivos estáticos não sumam quando o container for reiniciado, desligado ou excluído.


env_file:
    - .env
  command: python manage.py runserver 0.0.0.0:8000
  depends_on:
    - db

env_file: Carrega as variáveis de ambiente do arquivo .env.

command: Sobrescreve o comando final do Dockerfile. Aqui, o comando python manage.py runserver 0.0.0.0:8000, reinicia o servidor de desenvolvimento do Django.

depends_on: Informa ao Docker que o Django só deve ser iniciado após a criação do banco de dados.


db:
  image: postgres:16-alpine
  container_name: finance_db
  restart: always

image: Para criar o serviço db em um Container optei por utilizar uma imagem oficial e pronta do PostgreSQL 16 na versão Alpine que é mais leve.

restart: Informa ao Docker que sempre que houver alguma mudança nesse serviço o mesmo deve ser reiniciado.


environment:
    - POSTGRES_DB=${POSTGRES_DB}
    - POSTGRES_USER=${POSTGRES_USER}
    - POSTGRES_PASSWORD=${POSTGRES_PASSWORD}

environment: Define as configurações obrigatórias do Banco de Dados. O símbolo ${} indica que o Compose está buscando o conteúdo dentro do arquivo .env.


.env

Conforme expliquei algumas vezes anteiormente, o arquivo .env armazena as variáveis de ambiente que, basicamente, são variáveis que armazenam senhas importantes como senhas de banco de dados, senhas secretas para conectar com o Framework, senhas de conexão com APIs externas e etc, ou seja, são informações secretas que não devem ser compartilhadas com qualquer pessoa.

Por esse motivo, não vou mostrar o código exato deste arquivo. Mas, vou compartilhar o arquivo .env-example que é um arquivo exemplo que contém um Template do que o seu arquivo .env deve conter para você conseguir rodar o código localmente.

SECRET_KEY="CHANGE-ME"

# 0 False, 1 True
DEBUG="1"

# Comma Separated values
ALLOWED_HOSTS="127.0.0.1, localhost"

DB_ENGINE="django.db.backends.postgresql"
POSTGRES_DB="CHANGE-ME"
POSTGRES_USER="CHANGE-ME"
POSTGRES_PASSWORD="CHANGE-ME"
POSTGRES_HOST="localhost"
POSTGRES_PORT="5432"

SECRET_KEY: Entre aspas você deve colocar o código secreto de conexão com o Django. Esse código deve ser gerado especificamente para o projeto de atuação. O comando é python -c 'from django.core.management.utils import get_random_secret_key; print(get_random_secret_key())'.

DEBUG: O debug é muito utilizado pelos desenvolvedores quando uma aplicação está em construção ou manutenção pois logs de erro são apresentados na tela. O número 1 indica que o modo Debug está ativo e 0 indica que está desativado.

ALLOWED_HOSTS: Informa ao Docker quais clients podem acessar o Container e consequentemente ter acesso à aplicação.

DB_ENGINE: Informa ao Docker qual engine será responsável por conectar a aplicação ao banco de dados.

POSTGRES


.dockerignore

Assim como o .gitignore este arquivo ignora outros arquivos, porém ao invés de não enviar para o repositório, o .dockerignore não envia os arquivos declados para o Container. Isso é uma boa prática no cenário de DevOps, pois ao ignorar os arquivos declarados, o Container fica mais leve.

E também é possível utilizar um template e adicionar coisas específicas do seu projeto. Dê uma olhada no arquivo .dockerignore no repositório do projeto no GitHub.


Scripts

Fiz esse diretório especificamente para armazenar scripts bash dentro do Container.

Atualmente existe apenas um arquivo de script neste diretório e vou explicar abaixo o código.

comands.sh


# Encerra a execução do arquivo quando algum comando falhar
set -e

while ! nc -z $POSTGRES_HOST $POSTGRES_PORT; do
    echo "🟡 Aguardando a Inicialização do Banco de Dados Postgres ($POSTGRES_HOST $POSTGRES_PORT)..."
    sleep 0.1
done

echo "✅ O Banco de Dados Postgres foi Inicializado com Sucesso ($POSTGRES_HOST $POSTGRES_PORT)"

python manage.py collectstatic
python manage.py migrate
python manage.py runserver

#!/bin/sh: Este comando semelhante a um comentário indica ao sistema operacional que o conteúdo deste arquivo é um script automatizado.

set -e: Um comando simples que, ao encontrar algum erro durante a execução de qualquer comando, encerra a execução do script.

O laço de repetição while é responsável por informar ao usuário se o Banco de Dados está inicializando ou se já foi inicializado.

python manage.py collectstatic: Reúne todos os arquivos estáticos de todos os apps do Django e centraliza em um único diretório (que é especificado no arquivo settings.py).
Mas porque isso? Durante o desenvolvimento da aplicação, o Django serve os arquivos de cada app de maneira individual, porém é uma boa prática fazer com que o servidor web acesse todos os arquivos estáticos em apenas um lugar, pois, dessa forma, o acesso fica mais rápido.

python manage.py migrate: Este comando aplica as alterações de estruttura do banco de dados. Ele lê os arquivos na pasta migrations/ e cria ou altera as tabelas no PostgreSQL.

python manage.py runserver: Inicia o servidor de desenvolvimento do Django.


Desenvolvimento do da Aplicação

O desenvolvimento da aplicação envolve toda a parte de modelagem e criação do banco de dados, desenvolvimento das páginas, estrutura do Django e configuração das rotas de conexão.

O Django trabalha com o conceito de modularidade.

A aplicação é como se fosse um Lego, onde o projeto completo é a base onde as peças se encaixam, e cada App é um bloco de lego específico.

No Django, um App é uma subpasta dentro do projeto com o objetivo de fazer apenas uma coisa.

No caso do PoupAí existem dois Apps: project e accounts que segue o padrão de Separação de Preocupações (Separation of Concerns), ou seja, mesmo que façam parte do mesmo site, eles têm objetivos diferentes dentro da arquitetura do Django.

project

Este App possui o arquivo settings.py e funciona como o um maestro.

O objetivo principal aqui é gerenciar as configurações globais e a integração de todas as outras partes do sistema.


accounts

Este App é funcional. O objetivo dele é isolar tudo o que diz respeito ao Usuário, cuidar do ciclo de vida da conta do usuário e segurança de acesso.

Aqui temos:

manage.py


requirements.txt


Como os arquivos dos Apps interagem entre si?