🏗 Arquitetura — Luxor Data Pipelines¶
O Luxor Data Pipelines foi projetado para rodar 100% no Azure utilizando Azure Functions como mecanismo de execução.
A arquitetura foi pensada para garantir modularidade, escalabilidade e facilidade de manutenção dos pipelines de dados.
📂 Organização de Diretórios¶
A estrutura é dividida em duas partes principais:
-
pipelines/- Contém a lógica principal de cada processo de ETL.
- Cada arquivo representa um pipeline independente.
- Cada script deve expor um método
run()que recebe parâmetros opcionais para controlar a execução.
-
Diretórios
run_[NOME_DO_PIPELINE]/- Cada diretório corresponde a uma Azure Function individual.
- Responsável por orquestrar a execução do pipeline definido em
pipelines/. - Contém:
functions.json→ Configuração da Azure Function (tipo de trigger, agendamento, bindings, etc.).__init__.py→ Ponto de entrada da Azure Function. Importa a DAG (pipeline) e executa viamain().[nome_do_pipeline].py→ Define a DAG do pipeline, importando funções/módulos depipelines/e compondo a execução.
🔄 Fluxo de Execução¶
flowchart LR
subgraph "Azure Function"
direction LR
A[Trigger: Timer/HTTP] --> B["main() no '__init__.py'"]
B --> C["Função run() em run_[pipeline].py"]
C --> D[Execução de scripts no pipelines/]
end
- O gatilho (Timer Trigger ou outro) dispara a execução.
- O
main()do__init__.pychama orun()da DAG definida no arquivo.pydo diretóriorun_.... - O
run()da DAG importa e executa as funçõesrun()dos módulos empipelines/. - O processamento é feito (extração, transformação, carga) e os resultados são salvos no ADLS.
📌 Exemplo de Estrutura¶
luxor-data-pipelines/
│
├── pipelines/
│ └── hist_returns.py
│
└── run_hist_returns/
├── __init__.py
├── functions.json
└── run_hist_returns.py
Exemplo de pipelines/hist_returns.py¶
def run(ref_date=None):
AZURE_STORAGE_CONNECTION_STRING = os.environ.get("AZURE_STORAGE_CONNECTION_STRING")
logging.info("Running hist_returns.run()")
lq = LuxorQuery(adls_connection_string=AZURE_STORAGE_CONNECTION_STRING)
if ref_date is None:
ref_date = dt.date.today()
ref_date = dt.date(ref_date.year, ref_date.month, 1) - dt.timedelta(days=1)
ref_date = lq.get_month_end(ref_date)
logging.info(f"Rodando para {ref_date}")
returns_data = get_returns(lq, ref_date)
if returns_data is not None and not returns_data.empty:
incremental_load(lq, 'hist_returns', returns_data, normalize_columns=True)
else:
logging.info(f"Nenhum dado gerado para {ref_date}.")
Exemplo de run_hist_returns/run_hist_returns.py¶
from pipelines import hist_returns
def run():
hist_returns.run()
Exemplo de run_hist_returns/__init__.py¶
import logging
from dotenv import load_dotenv
from run_hist_returns.run_hist_returns import run
load_dotenv()
def main(mytimer):
logging.info("Executando função run_hist_returns")
try:
run()
logging.info("Execução concluída com sucesso.")
except Exception as e:
logging.error(f"Erro: {e}")
raise
Exemplo de run_hist_returns/functions.json¶
{
"scriptFile": "__init__.py",
"bindings": [
{
"name": "mytimer",
"type": "timerTrigger",
"direction": "in",
"schedule": "0 30 18 * * 1-5"
}
]
}
💡 Boas Práticas¶
- Sempre manter a lógica de negócio em
pipelines/, deixando o diretóriorun_...apenas para orquestração. - Usar variáveis de ambiente para credenciais e configurações.
- Garantir que cada
run()seja idempotente para evitar duplicação de dados. - Configurar agendamentos (
schedule) de acordo com a necessidade do negócio. - Monitorar logs no Azure para identificar falhas rapidamente.