Technical Design (TD)¶
Esta seção detalha a arquitetura interna e as decisões de implementação do CarFlow. O documento serve como referência para o desenvolvimento, manutenção e evolução do sistema, cobrindo camadas, fluxos de execução, contratos de interface e requisitos não-funcionais.
O foco deste documento é o escopo Build (MVP):
- Consulta Pública: Interface de leitura de preços consolidados (sem login).
- Processamento Batch: Rotina mensal de consolidação de dados.
- Observabilidade: Registro técnico de logs de consulta para auditoria e análise.
1. Arquitetura em Camadas¶
O sistema segue uma arquitetura baseada em camadas (Layered Architecture) para garantir a separação de responsabilidades e facilitar testes.
Responsabilidades dos Componentes¶
- Presentation Layer (Streamlit UI)
- Responsável pela renderização de filtros (widgets) e visualização de dados.
- Gerencia o estado da sessão (Session State) e cache de interface.
- Não contém regra de negócio: apenas valida entradas básicas e invoca o Service.
-
Trata os retornos do serviço para exibir feedback visual (Success, Warning/No Result, Error).
-
Service Layer (Business Logic)
- Núcleo da aplicação. Implementa os casos de uso do sistema.
- Orquestra as chamadas ao repositório e aplica regras de negócio (ex: cálculo de variação percentual, formatação de dados para gráficos).
-
Garante que todo acesso de consulta gere um registro de log (regra de auditoria).
-
Repository Layer (Data Access)
- Abstração do banco de dados utilizando SQLAlchemy.
- Converte objetos de domínio para queries SQL e vice-versa.
-
Isola a aplicação de detalhes específicos do banco (PostgreSQL).
-
Database (PostgreSQL)
- Armazena os dados transacionais (
price_collections), analíticos (monthly_averages) e logs (query_logs).
2. Fluxo de Dados e Dependências¶
O diagrama abaixo ilustra como os dados fluem entre o processo de carga (Batch) e o consumo (Consulta).
Regras de Integridade do Fluxo¶
- Segregação de Leitura/Escrita: A consulta pública acessa estritamente a tabela consolidada
monthly_averages, garantindo performance e protegendo os dados brutos. - Fonte do Batch: O processo de consolidação lê apenas registros com status
APPROVEDna tabelaprice_collections. - Rastreabilidade: Toda interação de consulta (inclusive falhas) persiste um evento imutável em
query_logs.
2.1. Detalhamento das Classes (Code Design)¶
O diagrama de classes abaixo ilustra a separação estrita entre Regra de Negócio (Service) e Persistência (Repository), seguindo o princípio da Inversão de Dependência (DIP).
StatisticsService (Domain Layer)¶
Representa o "cérebro" do processamento Batch.
* Responsabilidade: Contém a lógica matemática pura. Não sabe que existe um banco de dados, nem que existe uma interface web.
* Métodos Chave:
* calculate_mean(data: List[float]) -> Decimal: Recebe uma lista de preços brutos e devolve a média ponderada.
* remove_outliers(data): Implementa o algoritmo IQR (Intervalo Interquartil) para expurgar preços irreais (ex: R$ 10,00 ou R$ 1.000.000,00 para um Gol).
* consolidate_month(ref_date): Orquestra o processo: chama o repositório para buscar dados, limpa, calcula e manda salvar.
IRepository (Interface / Contract)¶
Define o contrato de acesso aos dados.
* Responsabilidade: Garantir que a camada de serviço não dependa de uma implementação específica de banco (PostgreSQL).
* Por que usar: Permite criar um FakeRepository (em memória) para rodar testes unitários ultra-rápidos sem precisar subir um container Docker de banco de dados.
PostgresRepository (Infrastructure Layer)¶
A implementação real do contrato.
* Responsabilidade: Traduzir as chamadas do Service para dialeto SQL usando SQLAlchemy.
* Métodos Chave:
* get_by_filters(...): Monta queries dinâmicas baseadas nos filtros da tela.
* save_bulk(...): Utiliza operações de batch insert para salvar milhares de médias de uma vez, garantindo performance.
MonthlyAverage (Data Model)¶
Representa a estrutura de dados da tabela consolidada.
* Responsabilidade: Mapeamento Objeto-Relacional (ORM).
* Atributos: É uma classe "anêmica" (focada em dados), contendo brand_id, model_id, avg_price e month_ref.
2.2. Decisão de Design: Repository Pattern¶
Adotamos o padrão Repository para isolar a complexidade do SQLAlchemy.
Sem Repository (Anti-pattern no Service):
# Ruim: O Service sabe que existe SQL e Sessão
def calcular_media():
dados = db.execute("SELECT * FROM table") # Acoplado ao SQL
# ...calcula...
db.commit()
Com Repository (Nossa Abordagem):
# Bom: O Service só fala "Python"
def calcular_media(repo: IRepository):
dados = repo.fetch_all() # Abstração
# ...calcula...
repo.save(resultado)
Isso garante que, se mudarmos o banco para MongoDB ou CSV no futuro, a lógica de cálculo de média (Service) não precisa ser alterada, apenas a implementação do Repositório.
3. Detalhamento: Caso de Uso "Consulta Pública"¶
Fluxo de Sequência¶
Contrato da Interface (Service Layer)¶
Entrada (DTO/Argumentos):
brand_id(int, obrigatório)model_id(int, obrigatório)year_model(int, obrigatório)
Saída (Response Object):
- Cenário SUCCESS: Objeto contendo
avg_price(Decimal),month_ref(Date) esamples_count(Int). - Cenário NO_RESULT: Retorno vazio ou
None(interpretado pela UI como "Sem dados para este período"). - Cenário ERROR: Exceção tratada ou objeto de erro contendo mensagem amigável (sem expor stacktrace).
Comportamento e Tratamento de Erros¶
- Resiliência: Falhas de conexão com o banco são capturadas no Repositório e relançadas como exceções de domínio (
DatabaseConnectionError), permitindo que a UI exiba uma mensagem de "Serviço Indisponível" em vez de quebrar. - Log de Auditoria: O registro em
query_logsocorre em um blocofinallyou via hook assíncrono para garantir que seja gravado independente do sucesso da busca.
4. Detalhamento: Caso de Uso "Batch Mensal" (ETL)¶
Fluxo de Sequência¶
Estratégia de Processamento (ETL)¶
O Batch atua como um processo de ETL (Extract, Transform, Load) executado sob demanda ou agendamento.
- Extract: Busca coletas em
price_collectionsondestatus='APPROVED'emonth_refcorresponde ao período alvo. - Transform:
- Agrupamento por
(brand_id, model_id, year_model). - Cálculo de estatísticas: Média (
avg), Contagem (count). -
(Futuro): Remoção de outliers via desvio padrão ou IQR.
-
Load: Persistência na tabela
monthly_averages.
Idempotência e Consistência¶
- O script utiliza a estratégia de UPSERT (Insert on Conflict Update) baseada na chave única composta
(model_id, year_model, month_ref). - Isso permite que o batch seja reexecutado múltiplas vezes para o mesmo mês sem duplicar dados, apenas atualizando os valores consolidados.
5. Decisões Técnicas e Trade-offs¶
5.1. Denormalização Controlada¶
Embora o modelo relacional seja normalizado, as tabelas de negócio (monthly_averages, query_logs) armazenam redundâncias controladas, como brand_id junto ao model_id.
- Motivação: Eliminar a necessidade de JOINs complexos nas consultas de leitura crítica (Dashboard Público), otimizando a performance.
- Garantia: A integridade é assegurada pelo processo de ingestão (Batch) e pelas Foreign Keys.
5.2. Stack Tecnológica¶
- Linguagem: Python 3.10+ (Tipagem estática com Pydantic/Type Hints).
- ORM: SQLAlchemy 2.0 (Gerenciamento de sessões e proteção contra SQL Injection).
- UI: Streamlit (Desenvolvimento rápido de interfaces de dados).
6. Estrutura do Projeto (Source Code)¶
A organização do código fonte reflete a arquitetura em camadas descrita acima, simplificada para o contexto do Streamlit:
src/
├── batch_etl.py # Script do Processo Batch (ETL)
├── database.py # Configuração de conexão DB e Session
├── main.py # Entrypoint da Aplicação (View + Controller)
├── models.py # Todos os Modelos ORM (Entities)
├── repositories.py # Camada de Acesso a Dados (Repository Pattern)
├── seed_data.py # Script para popular banco com dados fictícios
└── services.py # Regras de Negócio (Service Layer)
7. Requisitos Não-Funcionais¶
Performance¶
- Indexação: Índices criados nas colunas de filtro (
brand_id,model_id,year) nas tabelas de leitura. - Cache: Utilização do
@st.cache_datado Streamlit para armazenar listas estáticas (Marcas/Modelos), reduzindo chamadas ao banco.
Segurança¶
- Acesso: Consulta pública restrita a dados agregados; dados brutos (que poderiam identificar lojas ou vendedores) não são expostos na UI pública.
- Privacidade: O
query_logsnão armazena PII (Informação Pessoal Identificável) do usuário da consulta, limitando-se a metadados da requisição.




