Propostas de melhoria da arquitetura

Plano técnico para tornar o crawler mais consistente, observável, testável e eficiente, baseado na implementação atual do Imóvel Central.

Resumo do diagnóstico

P0

Slug instável

O slug é recalculado a cada sincronização e pode acumular sufixos -1, -2, -3.

P0

Consistência parcial

Imóvel, endereço e imagens são persistidos em etapas separadas, sem transação única.

P1

Indexação incompleta

Alterações em endereço/imagens não garantem reindexação do documento no Meilisearch.

P1

Execução sem histórico

O runner assíncrono retorna 202, mas não mantém estado persistido da execução.

P1

Baixa eficiência de rede

Existe um POST HTTP por imóvel, aumentando latência e pressão no backend.

P2

Testes insuficientes

Há boa base Laravel, mas faltam testes dos scrapers, do serviço de sync e de falhas reais.

Escopo: estas são propostas de evolução. Nenhuma alteração no código de produção foi feita nesta página.

O que corrigir primeiro

PrioridadeProblemaPropostaBenefício
P0Slug muda em toda sincronização.Preservar slug existente; ao criar, gerar slug determinístico e excluir o próprio ID da checagem de unicidade.URLs estáveis, SEO e ausência de colisões.
P0Upsert parcial.Envolver imóvel, endereço e imagens em DB::transaction().Sem registros pela metade.
P0Falhas silenciosas.Registrar execução, erro por imóvel, duração e resultado final.Diagnóstico e reprocessamento confiáveis.
P1Produção pode cair em collection.Exigir SCOUT_DRIVER=meilisearch no ambiente de produção e validar no boot.Evita busca diferente entre ambientes.
P1Endereço/imagem não reindexa.Reindexar o imóvel somente após concluir todas as relações.Busca textual atualizada.
P1Lote legado não limpa corretamente o arquivo.Apagar usando o mesmo prefixo crawler/ usado no upload.Evita acúmulo no storage.
P2Resolução fuzzy sem limiar.Normalizar localidades, cachear opções e rejeitar score baixo.Menos endereços incorretos.

Código atual do upsert · Job de lote

Fluxo recomendado

Schedulercria crawler_run Job por empresafila crawler Scraper Pythonretry + backoff Endpoint batch / individualidempotency key Transaction de upsertproperty + address + images Reindex jobapós commit Meilisearchdocumento completo Métricas + alertassucesso / erro / duração Prune controladodesativa e desindexa Tudo pode ser reprocessado por crawler_run_id.

A principal mudança é transformar a execução em uma unidade rastreável. Cada empresa teria uma execução com estado, métricas, retry e possibilidade de reprocessar apenas falhas.

Persistência e idempotência

Modelo sugerido: crawler_runs

id
company_key
mode
status              pending | running | success | partial | failed
started_at
finished_at
discovered_count
synced_count
skipped_count
error_count
last_error
created_at
updated_at

Regras importantes

Meilisearch: desenho recomendado

Estado atual: o índice local foi verificado com 8.966 documentos, igual ao total do MySQL. A integração funciona no snapshot atual.
ConfiguraçãoHojeRecomendação
DriverLocal: meilisearch; produção tem fallback collection.Falhar no boot se produção não tiver Meili configurado.
FilaLocal false; produção true.Manter fila em produção e ativar after_commit=true.
Camposid, preço, endereço textual e descrição.Adicionar tipo, negócio, empresa e active se forem filtrados no Meili.
FiltrosNenhum filterable/sortable.Configurar apenas campos usados; ou manter explicitamente filtros no MySQL.
ReindexaçãoDisparada pelo evento de Property.Reindexar após endereço/imagens e disponibilizar comando de reconciliação.
# reconciliação operacional sugerida
php artisan scout:import "App\Models\Property"

# verificação futura
php artisan crawler:check-index --company=village
php artisan crawler:reindex-failed --run=...

Modelo Property · Configuração Scout · Configuração de produção

Estratégia de testes

Unitários

Parser de cada scraper, conversão de preço/tipo/endereço, normalização de URLs e tratamento de dados incompletos.

Integração

Payload Python → endpoint Laravel → banco → documento Meili, com banco e Meili de teste.

Contrato

Fixtures JSON por imobiliária para detectar mudanças no formato externo.

Resiliência

Timeout, 429, 500, redirect, token inválido, retry e execução interrompida.

Cenários obrigatórios

Logs, métricas e operação

MétricaPor que acompanharAlerta sugerido
Duração por empresa/modoDetecta degradação do site externo.Acima da média histórica.
Taxa de erro por scraperIdentifica parser quebrado ou API indisponível.Erro > 5% ou zero imóveis.
Imóveis encontrados vs sincronizadosDetecta perdas silenciosas.Diferença acima do limite.
Imóveis desativados pelo pruneEvita desativação em massa por falha do crawler.Pico acima do normal.
Fila Scout pendenteMostra atraso de indexação.Crescimento contínuo.
Diferença MySQL × MeiliConfirma integridade da busca.IDs ou contagens divergentes.

Os logs deveriam ser estruturados em JSON e conter run_id, company, external_id, stage, duration_ms e error_type. Evite registrar payloads completos em produção quando contiverem dados desnecessários.

Roadmap sugerido

FaseEntregasResultado
1 — Segurança e consistênciaCorrigir slug, transação, deleção do lote, fallback do Scout.Elimina os riscos mais graves.
2 — IndexaçãoReindex após relações, after_commit, comando de reconciliação.Banco e busca ficam coerentes.
3 — Confiabilidadecrawler_runs, retries, backoff, status e alertas.Execuções auditáveis e recuperáveis.
4 — TestesFixtures dos cinco scrapers e testes de integração.Mudanças externas quebram o CI cedo.
5 — PerformanceBatch, cache de localidades, índices medidos com EXPLAIN.Menos requests e menor tempo total.
Recomendação final: não começaria por paralelizar os scrapers. Primeiro garantiria idempotência, transação, observabilidade e testes; depois mediria o gargalo real e aplicaria batch/cache/concorrência com segurança.