Skip to content

Extensão via plugins

Um plugin de terceiro no ddf funciona como uma peça de Lego: encaixa numa Porta já existente sem exigir nenhuma alteração na estrutura em volta. Um Extrator novo ou um Gerador novo entra no wizard do mesmo jeito que ExtratorPostgres ou GeradorDbt já entram, e nenhum dos dois é tratado como cidadão de segunda classe: os Adapters nativos da v1 passam pela mesma via de descoberta que um plugin de terceiro usaria.

Como o encaixe funciona

Extrator e Gerador são reexportados em domain/ports/__init__.py como caminho de import público, e descobertos via importlib.metadata.entry_points, nos grupos ddf.extratores e ddf.geradores. Um pacote instalado que declara um entry point nesses grupos é encontrado automaticamente na próxima execução do ddf, sem precisar registrar nada manualmente dentro do projeto.

Um entry point de ddf.extratores aponta para uma instância de ExtratorRegistrado (classe do Extrator, mais a função que sabe construí-lo interativamente perguntando credenciais). Um entry point de ddf.geradores aponta para uma classe de Gerador com construtor sem argumentos. mypy --strict valida os dois contra o Protocol correspondente em tempo de desenvolvimento do plugin; o ddf valida de novo em tempo de execução (issubclass/isinstance), e isola qualquer plugin que falhe nessa checagem como um Aviso, sem impedir que os demais plugins e os Adapters nativos continuem funcionando.

# pyproject.toml de um plugin de terceiro (esqueleto mínimo)
[project]
name = "ddf-extrator-sqlite"
version = "0.1.0"
dependencies = ["ddf-framework"]

[project.entry-points."ddf.extratores"]
SQLite = "ddf_extrator_sqlite.registro:_REGISTRO_SQLITE"
# ddf_extrator_sqlite/registro.py
from ddf.domain.ports.extrator import ExtratorRegistrado

from .extrator_sqlite import ExtratorSQLite, construir_extrator_sqlite

_REGISTRO_SQLITE = ExtratorRegistrado(
    classe_extrator=ExtratorSQLite,
    construir=construir_extrator_sqlite,
)

Com o pacote instalado (pip install ddf-extrator-sqlite), "SQLite" aparece como opção de fonte no wizard, ao lado de PostgreSQL e MariaDB, sem nenhuma mudança em código do ddf em si. O mesmo teste que garante isso (um Adapter novo adicionado sem editar nenhum Adapter existente) é descrito em Testes e qualidade.

O tamanho real da peça

A analogia de Lego descreve o mecanismo de encaixe corretamente, mas não descreve o tamanho da peça que se encaixa. Escrever um Extrator novo de verdade tem um custo real, que vale deixar explícito em vez de sugerir que "implementar o Protocol" é o trabalho inteiro.

extractors/comum/ cobre uma fração pequena do volume de um Extrator. Entre ExtratorPostgres e ExtratorMariaDB, o código compartilhado em extractors/comum/ soma cerca de 15% do total de linhas dos dois; o resto é específico de cada motor. A lógica de paralelismo intra-tabela, em particular, foi deixada deliberadamente fora do código compartilhado: o ciclo de vida de conexão e a semântica de particionamento divergem demais entre os dois motores para compensar a unificação (física, via ctid, no Postgres; lógica, via faixa de chave primária, no MariaDB, ver Pipeline e paralelismo). Um terceiro Extrator relacional (SQL Server, Oracle) herda esse padrão de divisão, não o trabalho já feito para Postgres e MariaDB.

Suporte pleno como destino do GeradorDbt é uma segunda frente de trabalho. Um Extrator novo entra no wizard e produz TabelaExtraida corretamente sem precisar de nada além do Protocol. Mas para o projeto dbt gerado a partir dele rodar de verdade contra esse motor novo, o GeradorDbt precisa de templates Jinja próprios por motor, o mesmo padrão adapter.dispatch que hoje distingue postgres__cast_type.sql de mariadb__cast_type.sql (ver Tecnologias). Quem lê só a Porta Extrator não descobre essa dependência, porque ela vive inteiramente do lado do GeradorDbt.

O Port Extrator pressupõe hierarquia relacional com catálogo consultável. Os métodos de Extrator (listar_escopos, listar_tabelas, extrair_tabela) descrevem uma estrutura de schema → tabela → coluna, com um catálogo interrogável para obter tipo, chave e restrição. Isso é neutro entre os bancos relacionais reais que o ddf já suporta, mas não é necessariamente neutro para uma fonte não-relacional (um arquivo, uma API). Esse é o escopo atual da Porta. Não é uma limitação escondida.

Contribua conosco

Se você tem uma fonte de dados ou um formato de artefato que o ddf ainda não cobre, o caminho é abrir uma issue ou um PR implementando o Protocol correspondente. O mesmo mecanismo de entry points descrito acima funciona tanto para um pacote publicado à parte quanto para um Adapter incorporado ao próprio repositório do ddf. Repositório e issues: github.com/ThiagoLimaC/ddf.