
Construímos o Wayler, um orquestrador de processos com recurso ao Docker, num mundo obcecado por IA. Decidimos não recorrer aos "gigantes" da indústria.
Índice
Se passarmos cinco minutos nas redes sociais onde se fala sobre tecnologia, ficamos com a ideia de que a única forma de desenvolver software à data deste artigo passou a ser, sem exceção, o lançamento de agentes para qualquer tarefa. Repito, qualquer tarefa. Isto pode soar anti-IA, mas não nos interpretem mal — na Waymotion usamos modelos e ferramentas de IA todos os dias para escrever código, desenhar sistemas, ajudar a resolver problemas.
Mas, recentemente, perante uma tarefa de migrar dados geoespaciais complexos de serviços REST do ArcGIS e de APIs internas para uma OGC API (ver este caso), decidimos ir contra a corrente: construímos o nosso próprio orquestrador de processos que, neste neste caso, acabou por ser usado para orquestrar um sistema ETL, simples e determinístico (como antigamente).
Chamámos-lhe Wayler (como usámos docker no core das decisões de arquitetura e funcionamento, o nome foi a parte fácil). Eis porque o construímos, porque não optámos pelas escolhas óbvias da indústria (incluindo as open-souce) e como apostar em Docker e em Python nos deu exatamente o controlo e a sustentabilidade de software de que achámos ser suficientes para o objetivo que nos comprometemos.
O «problema» das alternativas
Quando tudo o que precisamos de fazer é recolher dados de uma API, transformá-los para uma norma específica (como o INSPIRE) e carregá-los com segurança para uma base de dados relacional, o ecossistema atual disponível na comunidade open-source apresenta uma dicotomia frustrante, que é crucial ponderar especialmente numa equipa pequena:
- (Spark, Airflow, Mage.ai): ão ferramentas incríveis para data lakes com Big Data e sistemas distribuídos. Mas configurar um cluster ou definir DAGs só para paginar uma fonte de dados (ex.: um endpoint) é como usar um Ferrari para um percurso diário de 5 km.
- (n8n, Zapier): excelentes para webhooks, mas transformam-se facilmente em “esparguete visual” assim que é preciso lidar com transformações (por exemplo, geoespaciais) ou com conversões de coordenadas (EPSG:3763 para EPSG:4326).
Como Engenheiro de Software, acredito que o código-fonte continua a exigir discernimento humano, independentemente da ferramenta que o gera. Por isso, não quisemos abstrair o processo a 100% pois queríamos manter o controlo absoluto sobre a lógica dos scripts de processamento — neste caso, implementados em Python. Se for preciso um IF baseado numa escala de 1:50k vs 1:200k, queremos ver essa condição explicitamente no código.
Quando processos e builds determinísticos são possíveis, na nossa visão, são muitíssimo superiores para a sustentabilidade do software a longo prazo. Não temos de nos questionar por que razão um LLM alucinou um mapeamento de dados (algo que, aos dias de hoje, ainda acontece num processo não determinístico gerado por estes modelos). Um output determinístico simplesmente funciona (ou não), exatamente da mesma maneira, sempre.
Além disso, a nível de infraestrutura, não queríamos depender do sistema operativo do servidor nem de terceiros para gerir o agendamento de processos. Precisávamos de um controlo interno, a correr exclusivamente na “infraestrutura” do Wayler, totalmente isolado.
O Wayler e os “Harpoons”
Para resolver isto, implementámos o Wayler — um orquestrador leve e feito à medida, dependente do Docker.
Na nossa arquitetura, o Wayler funciona como um centro de comando. Não processa os dados em si, apoia-se numa base de dados local para gerir os agendamentos, efetuar novas tentativas de execução e despoletar alertas. Quando chega a hora, simplesmente lança um Harpoon — um script Python com um propósiot especifico, wrapped num Container Docker.
A decisão em usar Docker
O isolamento total. Ao obrigar cada processo a ser a viver num container Docker, garantimos que cada script Python vive no seu próprio ambiente. Não há dependências em que uma atualização do GeoPandas para um script estrague outro. Também torna o lançamento de novas versões incrivelmente simples — construímos e etiquetamos uma nova imagem do Harpoon, atualizamos o registo na base de dados, e a execução agendada seguinte usa o novo código sem afetar o resto da frota.
Além disso, isto permitiu-nos impor um comportamento estrito ao SDK (internamente chamamos-lhe «Iron»). Cada Harpoon herda de uma classe Python BaseHarpoon. Os programadores implementam apenas um método process(), e o SDK trata automaticamente do registo consistente, das sessões de base de dados, da captura de erros e de uma flag —dry-run obrigatória.
Construir um orquestrador assim traz uma ressalva importante: depende 100% do Docker. O Wayler tem de ter acesso ao socket do Docker para criar contentores, monitorizar os respetivos códigos de saída e destruí-los. Mas, para nós, esta dependência foi deliberada, e acreditamos que foi uma decisão altamente vantajosa.
Veja como é simples implementar um Harpoon com o SDK:
from wayler_sdk import BaseHarpoon
from integrations.arcgis import ArcGISClient
class MineralOccurrencesHarpoon(BaseHarpoon):
def process(self):
self.logger.info("Starting Voyage: Fetching ArcGIS data...")
# 1. Fetch data from source
client = ArcGISClient(url=self.config.SOURCE_URL)
raw_data = client.fetch_all_features()
# 2. Procedural, deterministic transformation
self.logger.info(f"Retrieved {len(raw_data)} records. Transforming...")
clean_data = self.transform_to_inspire(raw_data)
# 3. The SDK handles the atomic swap to the DB
self.logger.info("Loading into PostGIS Staging...")
self.atomic_load(
target_table="stg_inspire_ge_minocc",
data=clean_data
)
self.logger.info("Voyage successful!")
if __name__ == "__main__":
# The run() method handles dry-runs, retries, and DB sessions
MineralOccurrencesHarpoon().run()Entrega de dados, sem interrupção
No primeiro caso de uso do Wayler, o principal consumidor destes dados foi um serviço pygeoapi público que lê de uma base de dados PostGIS. Se um Harpoon demora 20 minutos a descarregar e processar milhares de elementos geológicos, a API pública não pode servir dados vazios ou parciais durante essa janela.
Como temos controlo total sobre o Python procedimental, o nosso SDK impõe um padrão de troca atómica:
Para onde vamos a partir daqui (um roteiro possível)
O Wayler revelou-se tão estável que já pensamos alargá-lo a outros processos internos de sincronização, para lá dos dados geoespaciais. Ainda assim, qualquer equipa de engenharia honesta sabe que o seu sistema ainda não é perfeito (e nunca será :D). Temos dois pontos maiores no roteiro de dívida técnica:
- Uma interface própria: neste momento, gerir o Wayler significa correr alguns comandos docker e escrever INSERTs de SQL clínicos na base de dados do orquestrador. Precisamos de construir um painel limpo e bonito para visualizar pipelines, despoletar execuções manuais de Harpoons e acompanhar os registos.
- Gestão de segredos: a nossa próxima grande evolução arquitetónica é integrar um gestor de segredos dedicado, para que os Harpoons passem a ir buscar as suas credenciais dinamicamente em tempo de execução (o mage.ai, por exemplo, faz isto muito bem).
Conclusão
Numa era em que a indústria tecnológica corre para entregar todos os fluxos de trabalho a um LLM, vale a pena lembrar que a sincronização de dados é muitas vezes um prato que se serve frio e determinístico. Adoramos a IA naquilo em que ela é melhor. Mas para migrar dados? Não me parece que precisemos dela. Abraçámos a sustentabilidade do Python procedimental, as fronteiras estritas do Docker e a fiabilidade de um agendador feito por nós.
Como programador que tenta acompanhar os superpoderes dos LLM, admito que houve uma pequena parte de mim que temeu, ao início, que a «diversão» da IA se perdesse ao escolher a via tradicional e determinística em vez de um enxame de agentes autónomos. Mas a verdade é que os Harpoons continuam a ter de ser desenvolvidos. A arquitetura continua a ter de ser desenhada, as APIs continuam a ter de ser sujeitas a engenharia inversa, e a IA está mesmo ali, nos nossos IDE, a ajudar-nos a escrever esse Python procedimental mais depressa e melhor do que nunca. Ficamos com a fiabilidade do código determinístico, sem perder a alegria do «desenvolvimento moderno».
O que me deixa a pensar: por vezes, a coisa mais inovadora que se pode fazer é escrever código que faz exatamente aquilo que lhe mandamos fazer, mesmo que seja um bug.



