Connectors: MongoDB e Neo4j
Índice
O que é
Os connectors são serviços dedicados que centralizam o acesso aos bancos de dados de todo o sistema. Anteriormente, a lógica de persistência estava distribuída entre os serviços da pipeline (processors), o que gerava duplicação de código e lógicas de persistências que durante a evolução do sistema se tornaram causas de bugs, por exemplo:
- Cada serviço tinha uma lógica de geração do hash de deduplicação causando a inserção de nós e relações (
HAS_SKILLeREQUIRES_SKILL) duplicadas no Neo4j, - Número de conexões crescendo conforme a adição de novos serviços na pipeline,
- Dificuldade de manter a integridade dos dados e a evolução do schema.
A solução adotada (ADR-033) define dois connectors independentes:
| Serviço | Responsabilidade |
|---|---|
connector-mongodb | Dados brutos, processados e de treinamento |
connector-neo4j | Grafo de conhecimento: habilidades, currículos e oportunidades |
Ambos se comunicam com os demais serviços exclusivamente via RabbitMQ, seguindo o padrão RPC over Message Queue para leituras síncronas e Fire-and-forget para escritas assíncronas.
Como Funciona
Padrão de Comunicação
Escritas: Fire-and-Forget (Assíncrono)
Para operações que não exigem retorno imediato, o produtor envia um evento e segue sua execução sem aguardar confirmação do banco:
O UUID da entidade é gerado pelo produtor (client-side UUID) antes de publicar o evento. Isso permite que o serviço referencie a entidade em etapas posteriores sem aguardar confirmação do banco.
Leituras: RPC com Direct Reply-to (Síncrono)
Para consultas que exigem o resultado de uma query (por exemplo, quando a API precisa do perfil de um usuário para prosseguir com a requisição), utiliza-se o padrão Direct Reply-to do RabbitMQ:
Cada chamada RPC inclui um request_id único e possui um TTL configurado para expiração automática em caso de indisponibilidade do connector, o que evita bloqueios indefinidos no processamento.
connector-mongodb
O connector-mongodb é o único serviço com driver PyMongo instalado. Ele gerencia as seguintes coleções, com responsabilidades separadas:
| Coleção | Domínio | Conteúdo | Evento de Origem |
|---|---|---|---|
raw_opportunities | Oportunidades | Dados brutos do scraper | OpportunityScraped |
processed_opportunities | Oportunidades | Skills extraídas + metadados da vaga | OpportunitySkillLinked / OpportunitySkillCustom |
llm_training_data | - | Histórico de extrações (auditoria e fine-tuning) | OpportunitySkillLinked / OpportunitySkillCustom / ResumeSkillLinked / ResumeSkillCustom |
raw_resumes | Currículos | Texto bruto submetido pelo usuário ou carregado | ResumeUploaded / ResumeRawLoaded |
processed_resumes | Currículos | Skills extraídas + metadados do candidato | ResumeSkillLinked / ResumeSkillCustom |
profiles | Candidatos | Dados cadastrais, onboarding e currículo vinculado | ProfileRegister / ProfileUpdateResume / ProfileCompleteOnboarding |
entity_linking | - | Detalhes de análise e convergência de Entity Linking | EntityLinkingAnalysisRecorded |
Deduplicação via SHA-256
A fim de evitar a duplicação de conteúdo bruto no banco de dados, o connector calcula e armazena o hash SHA-256 dos campos da entidade (exceto campos de data e hora).
O hash é armazenado no campo hash. O connector utiliza o identificador da entidade (_id = entity_id) em conjunto com a operação $setOnInsert do PyMongo para garantir que o hash e os dados brutos sejam persistidos apenas no primeiro registro, descartando duplicatas sem gerar exceções.
connector-neo4j
O connector-neo4j é o único serviço com driver neo4j-driver Python instalado. Ele gerencia quatro tipos de nós no grafo de conhecimento:
| Nó | Chave de Identidade | Chave de Versão | Propriedades Principais |
|---|---|---|---|
(:Resume) | profile_id | id | id, profile_id, knowledge_area, source |
(:Opportunity) | id | - | id, title, company, source |
(:Skill) | uri (ESCO URI) | - | uri, name, type, embedding |
(:CustomSkill) | name | - | name, type |
Deduplicação
O connector utiliza a instrução MERGE em todos os nós para garantir a idempotência. Constraints de unicidade são aplicadas no Neo4j durante a inicialização do serviço:
CREATE CONSTRAINT resume_id IF NOT EXISTS
FOR (r:Resume) REQUIRE r.id IS UNIQUE;
CREATE CONSTRAINT skill_uri IF NOT EXISTS
FOR (s:Skill) REQUIRE s.uri IS UNIQUE;
CREATE CONSTRAINT custom_skill_name IF NOT EXISTS
FOR (cs:CustomSkill) REQUIRE cs.name IS UNIQUE;
CREATE CONSTRAINT opportunity_id IF NOT EXISTS
FOR (o:Opportunity) REQUIRE o.id IS UNIQUE;
A persistência de currículos requer tratamento específico para lidar com a submissão de múltiplos currículos pelo mesmo usuário ao longo do tempo. O sistema executa a instrução MERGE na chave profile_id e remove os relacionamentos HAS_SKILL anteriores se uma nova versão (novo id) for identificada:
// Se a versão mudou, limpa as relações antigas antes de atualizar
OPTIONAL MATCH (existing:Resume {profile_id: $merge_value})
WHERE existing.id IS NOT NULL AND existing.id <> $version_value
OPTIONAL MATCH (existing)-[r:HAS_SKILL]->()
DELETE r
// Merge e atualização atômica com timestamps
WITH count(*) as dummy
MERGE (n:Resume {profile_id: $merge_value})
ON CREATE SET n.created_at = datetime()
ON MATCH SET n.updated_at = datetime()
SET n += $properties
O fluxo garante que o nó (:Resume) de um usuário sempre reflita apenas as competências do último currículo processado.
ADR's Relacionadas
| ADR | Data | Decisão |
|---|---|---|
| ADR-004 | Dez 2025 | Estratégia de deduplicação por SHA-256 no MongoDB |
| ADR-033 | Abr 2026 | Centralização e isolamento da lógica de banco em connectors via MQ |
| ADR-039 | Abr 2026 | Estratégia de deduplicação de currículos com limpeza de versão no Neo4j |