O poder de um banco de grafos e como ele pode ajudar a entender seu negócio

Intro

Você já ouviu falar sobre banco de dados de grafos? Neo4j, AWS Neptune e Dgraph são exemplos de bancos de dados focados em redes de grafos, mas afinal, o que eles resolvem?

Nos dias atuais, toda informação relevante é salva em um banco de dados. Mas como transformar esses dados em insights e visualizações úteis? Entender como uma informação se conecta a outra é um fator que permite que uma equipe desenvolva e entregue ainda mais valor ao seu produto.

Conceito de grafos

Quando falamos sobre grafos, mais especificamente no mundo da tecnologia, estamos falando de um tipo de estrutura de dados formada por vértices e arestas (edges), que se conectam por meio de linhas.

Quando aplicamos esse conceito à área de desenvolvimento, nosso CRUD simples e suas operações RESTful passam a ser algo mais complexo. Em bancos SQL e NoSQL é possível criar representações de grafos, no entanto, diversos joins e agregações têm um custo operacional muito alto. Cruzar milhões de registros para identificar, por exemplo, uma possível fraude em um sistema é algo que levaria bastante tempo, já que é preciso relacionar informações e encontrar padrões semelhantes. Existem abordagens, como matrizes de pesos, que tornam essa busca por similaridade mais eficiente. Ainda assim, é com bancos de grafos que essa busca se torna ainda mais performática.

Aplicando na prática: Projetinho

Para sair da teoria, decidi construir um pipeline real: extrair dados de uma organização open source no GitHub via API GraphQL e transformar essas informações em um grafo no Neo4j. O objetivo era aprender a sintaxe de um novo banco, mas usando um caso concreto, capaz de responder perguntas que, com um banco relacional, seriam bem mais custosas de resolver.

A arquitetura do pipeline

O projeto foi construído em .NET, dividido em camadas que separam bem as responsabilidades:

  • Domain: as entidades do domínio (Person, Repository, Contribution, Interaction, Module, Technology).
  • Providers.Github: os coletores (Collectors) responsáveis por buscar os dados na API GraphQL do GitHub, um por tipo de entidade (ContributorCollector, RepositoryCollector, ModuleCollector, entre outros).
  • Graph: os repositórios (Repositories) que sabem como persistir cada entidade e cada tipo de relação no Neo4j, como ContributionGraphRepository, InteractionGraphRepository, ModuleContributionGraphRepository e TechnologyGraphRepository.

Essa separação por tipo de relação (contribuição, interação, módulo, tecnologia) reflete diretamente a modelagem do grafo: cada repositório de grafo é responsável por criar um tipo específico de aresta.

Um exemplo de código: persistindo uma contribuição

O trecho abaixo mostra o ContributionGraphRepository, responsável por criar a relação CONTRIBUTED_TO entre uma pessoa e um repositório:

public sealed class ContributionGraphRepository(Neo4jClient neo4j, ILogger<ContributionGraphRepository> logger)
{
    private const string MergeQuery = """
        MATCH (p:Person { login: $login })
        MATCH (r:Repository { fullName: $fullName })
        MERGE (p)-[c:CONTRIBUTED_TO]->(r)
        SET c.commits   = $commits,
            c.syncedAt  = $syncedAt
        """;

    public async Task SaveAsync(Contribution contribution, CancellationToken cancellationToken = default)
    {
        await neo4j.ExecuteAsync(MergeQuery, new
        {
            login = contribution.Contributor.Login,
            fullName = contribution.Repository.FullName,
            commits = contribution.Commits,
            syncedAt = DateTimeOffset.UtcNow.ToUnixTimeSeconds(),
        }, cancellationToken);
    }
}

Repare no uso de MERGE em vez de CREATE: isso garante idempotência. Se o pipeline rodar novamente para o mesmo par pessoa/repositório, a relação não é duplicada, apenas os campos commits e syncedAt são atualizados. Esse é um padrão que se repete em todos os repositórios de grafo do projeto, cada um cuidando de um tipo de nó ou relação diferente.

O modelo do grafo

No grafo, cada contribuidor é representado como um nó, assim como cada repositório. As arestas conectam contribuidores a repositórios, indicando quem contribui para qual projeto, e também conectam contribuidores entre si quando eles atuam no mesmo repositório, revelando quem interage com quem dentro da organização.

O que esse grafo revela

Com essa estrutura montada, algumas perguntas que antes exigiriam cruzar várias tabelas manualmente passam a ter resposta visual imediata:

  • Quem sustenta um projeto sozinho? Na rede, esses casos aparecem de forma isolada: um ou dois contribuidores cujas conexões apontam para um repositório que nenhum outro nó também sustenta. Como essa região do grafo fica visualmente separada do restante, identificar esse "bus factor" crítico se torna quase imediato.
  • Quem são os contribuidores com mais domínio técnico? Nós com muitas conexões (para múltiplos repositórios e outros contribuidores) indicam pessoas centrais na organização, potenciais pontos de referência para resolver questões técnicas.
  • Quais pessoas trabalham nos mesmos módulos? Contribuidores conectados aos mesmos repositórios formam clusters naturais, mostrando afinidades de atuação dentro do time.

Ao atribuir pesos às arestas, por exemplo, com base na frequência de contribuições, essas relações ficam ainda mais claras visualmente: quanto mais forte a conexão, mais espessa (ou mais próxima) a linha entre os nós, permitindo identificar padrões só de olhar o grafo, sem precisar rodar uma única query.

O resultado na prática

Depois de rodar o pipeline completo contra a organização no GitHub, dá para consultar o grafo direto no Neo4j Browser. Uma query simples já revela a rede de interações entre contribuidores:

MATCH p=()-[:INTERACTED_WITH]->() RETURN p LIMIT 25;

Grafo de interações entre contribuidores

Cada aresta INTERACTED_WITH representa uma interação real entre duas pessoas dentro de um repositório (comentário, review, menção). Só de olhar a imagem já dá para notar sub-redes: um grupo mais isolado à esquerda e um núcleo bem mais conectado à direita, girando em torno de nomes como Gabriel Vieira, Daniel Reis e Ranie.

Outra pergunta interessante é: quem contribui para um repositório específico? Filtrando por um projeto (4noobs, nesse caso), a resposta aparece de forma imediata:

MATCH (p:Person)-[:CONTRIBUTED_TO]->(r:Repository { name: "4noobs" })
RETURN p, r;

Grafo de contribuidores do repositório 4noobs

Esse tipo de visualização, um nó central cercado por todos que já contribuíram, é exatamente o tipo de coisa que, em SQL, exigiria um JOIN entre contributions e repositories filtrado por repository_id, e ainda assim você teria que montar o grafo manualmente em alguma ferramenta de visualização à parte. Aqui, ele já nasce pronto.

Código e demo

O projeto completo está aberto no GitHub, incluindo o pipeline de extração, a modelagem do grafo e os repositórios de persistência:

🔗 github.com/fernanduandrade/he4rt-discovery

E para quem quiser explorar o grafo sem precisar rodar nada localmente, montei um front-end simples que consome esses dados:

🔗 he4rt-connections.vercel.app

O que isso significa para produtos e negócios

O exemplo do GitHub é só uma vitrine simples, mas o mesmo raciocínio se aplica a praticamente qualquer produto que tenha pessoas, entidades e interações entre elas, o que é praticamente todo produto digital.

Pense em um marketplace: cada usuário, cada vendedor, cada produto e cada transação vira nó ou aresta. De repente, perguntas como "quais vendedores estão conectados por comprarem dos mesmos fornecedores?" ou "existe um grupo de contas que interage só entre si, indicando uma possível fraude coordenada?" deixam de ser relatórios complexos de BI e passam a ser leituras diretas do grafo.

Em um produto B2B, o mesmo modelo revela quem são os usuários-chave dentro de uma conta corporativa, quais times colaboram entre si e onde estão os pontos de risco, por exemplo, um cliente cujo único usuário ativo é a pessoa que está prestes a sair da empresa. É o mesmo "bus factor" que identificamos entre contribuidores de um repositório, só que aplicado a receita.

O ganho real não está em trocar SQL por Cypher. Está em mudar a pergunta que o time consegue fazer aos dados. Em vez de "quantos usuários fizeram X", passa a ser "como esses usuários se conectam, e o que essa rede está me dizendo que uma tabela não conta". Para produto, isso significa identificar usuários influentes antes de um lançamento. Para negócio, significa enxergar riscos e oportunidades que hoje ficam escondidos entre linhas de tabelas que nunca se cruzam de verdade.

No fim, um banco de grafos não é sobre ter mais uma ferramenta na stack. É sobre reconhecer que, em muitos produtos, a informação mais valiosa não está nos dados em si, mas nas conexões entre eles.