Patrick Attankurugu
Patrick Attankurugu
HomeProjectsBlogTech Stack
CV
Patrick Attankurugu
Patrick Attankurugu

Building AI for financial compliance across Africa

HomeProjectsBlogTech StackRamah Foundation

© 2026 Patrick Attankurugu. All rights reserved.

Back to blog
KYB · Graph Databases

Graph Databases for Beneficial Ownership: Mapping UBO Networks with Neo4j

December 2024 · revised July 2026·13 min read

Every KYB regulation on earth asks one deceptively short question: which natural person ultimately owns or controls this company? I have built this kind of plumbing twice, in KYB work at Agregar and in AfricaPEP, where Neo4j is the database of record for political-exposure networks. This post is the case for modeling ownership as a graph, made honestly: the math the regulation implies, the query shapes that follow, and the architecture I would defend, without the fake benchmark table this post used to carry.

The question is a path, so the data is a graph

FATF's standard sets the common test: identify natural persons holding roughly 25 percent or more of a company, directly or indirectly. That single word does all the damage. Direct ownership is a row in a table. Indirect ownership is a walk across several of them, usually crossing a corporate registry boundary at every step.

Diagram of a layered ownership chain: the KYB question points at an operating company in Ghana, which is 80 percent owned by a holding company in another jurisdiction, wholly owned by a trust in a third, whose beneficiary is a natural person. A note shows effective ownership is the product along the path, and that each hop crosses a different registry with different disclosure rules.
Figure 1. Three hops, three registries, three disclosure regimes. The number the regulation asks for is a multiplication along a path, and rows in separate tables do not know they form a path.

Notice what the regulation quietly implies mathematically. Effective ownership is a product over a path: a person holding 50 percent of a holding company that owns 60 percent of the customer holds 30 percent effectively, and with multiple routes between the same two entities you sum over paths. The data structure whose native operations are “walk paths of unknown length” and “aggregate along the way” is a graph. That, and not query speed, is the real argument for a graph database here: the model matches the question. People who design opaque structures are exploiting exactly this mismatch, betting that whoever screens them thinks in single-table rows.

The honest SQL comparison

An earlier version of this post included a benchmark table showing PostgreSQL timing out where Neo4j answered in milliseconds. I have removed it: I never ran that benchmark rigorously, and the literature is full of tuned-relational-vs-untuned-graph comparisons in both directions. The defensible claim is about the shape of the code, and it is damning enough on its own. Here is “find every natural person with effective ownership of at least 25 percent, through up to eight layers” in both systems:

-- PostgreSQL: recursive CTE
WITH RECURSIVE chain AS (
  SELECT o.owner_id, o.company_id,
         o.pct / 100.0        AS eff_pct,
         ARRAY[o.company_id]  AS visited
  FROM ownership o
  WHERE o.company_id = :customer
  UNION ALL
  SELECT o.owner_id, c.company_id,
         c.eff_pct * o.pct / 100.0,
         c.visited || o.company_id
  FROM ownership o
  JOIN chain c ON o.company_id = c.owner_id
  WHERE NOT o.owner_id = ANY(c.visited)   -- manual cycle guard
    AND array_length(c.visited, 1) < 8    -- manual depth guard
)
SELECT p.name, SUM(chain.eff_pct) AS effective
FROM chain JOIN persons p ON p.id = chain.owner_id
GROUP BY p.name
HAVING SUM(chain.eff_pct) >= 0.25;
// Cypher: the same question
MATCH path = (p:Person)-[r:OWNS*1..8]->(c:Company {id: $customer})
WITH p, reduce(pct = 1.0, o IN relationships(path) |
              pct * o.pct / 100.0) AS eff
RETURN p.name, sum(eff) AS effective
ORDER BY effective DESC
// >= 0.25 filter applied on the aggregated result

Both work. But look at where the intelligence lives. In SQL, the traversal machinery (the recursion, the visited-array cycle guard, the depth cap) is code you write, test, and maintain, and it is where the bugs live; forget the cycle guard and your first circular structure hangs the query. In Cypher, traversal is the language's native verb, and what remains is the actual domain logic: a path pattern and a fold over it. When the compliance team asks next month for “the same thing but through trusts and nominee directors too,” that is one pattern change in Cypher and a rewrite in SQL. In a domain where the questions mutate constantly, the maintainability gap matters more than any latency chart I could print.

Cycles are not the edge case. They are the domain.

Circular ownership diagram: Company A owns 30 percent of B, B owns 40 percent of C, C owns 25 percent of A. Side notes: a naive traversal never terminates; effective ownership becomes a converging series at 3 percent per lap rather than a single product; and cycles are legitimate in some conglomerates but a classic opacity device in shell networks.
Figure 2. A three-company cycle. Every assumption a tree-shaped mental model brings, termination, single paths, one multiplication, breaks here.

Ownership data has a property that surprises engineers coming from org charts and category trees: it contains cycles, put there on purpose. A owns some of B, B owns some of C, C owns some of A. Cycles defeat naive recursion (the traversal never terminates), complicate the math (effective ownership around a loop is a converging series, not a product), and, most usefully, they are a signal in themselves: cross-shareholding exists in legitimate conglomerates, but in a shell network a cycle usually means the structure is doing work its paperwork will not admit to. Finding every cycle through a customer is one Cypher pattern:

MATCH path = (c:Company {id: $customer})-[:OWNS*2..8]->(c)
RETURN [n IN nodes(path) | n.name] AS cycle,
       length(path)                 AS hops

I learned to respect cycles the embarrassing way, in AfricaPEP rather than in ownership data: an early relationship-traversal query on family networks assumed tree-shaped data, and a marriage between two politically-connected families produced a loop that pinned a CPU. Graph databases do not make cycle handling automatic, Cypher's default trail semantics and explicit depth bounds are still your responsibility, but they make it a visible, one-line decision instead of bookkeeping buried in a WHERE clause.

Modeling: control is more than ownership

The schema I use is small, because in graph modeling the power is in the traversal, not the attributes. Entities: people, companies, trusts, foundations. Relationships: OWNS with a percentage, effective dates, and, critically, a source property; DIRECTS for officers and nominees; CONTROLS for voting rights and veto mechanisms that confer control without equity; BENEFITS_FROM for trust relationships; and family or association edges, because FATF's definition of exposure extends to relatives and close associates, which is precisely the edge type AfricaPEP's graph carries in production.

Two modeling rules I hold with some conviction. First, provenance lives on the edge: every ownership claim carries where it came from and when it was seen, because in a compliance review “who says so” is as important as “what is said.” I made this mistake once in AfricaPEP in a different costume, storing position dates on a shared node where every holder of an office inherited the same term, and the fix was the same principle: facts about a relationship belong on the relationship. Second, model control channels as distinct edge types rather than one generic “linked to,” because the interesting query is often “who can direct this company's actions through any channel,” and that is a union over typed edges, not a blur over untyped ones.

The architecture: a projection, not a second truth

Architecture diagram: PostgreSQL remains the system of record holding customers, documents, registry filings, screening results and the audit log. A scheduled projection syncs only entities and ownership edges into Neo4j, which answers UBO path, cycle, and shared-officer questions. Flags and computed effective ownership flow back as ordinary relational rows. The graph is labeled as a rebuildable index of relationships, not a second source of truth.
Figure 3. The pattern I use: relational system of record, graph as a rebuildable traversal index, results written back as rows. AfricaPEP inverts which store owns truth, and the choice follows from which questions dominate.

The wrong lesson to take from all this is “move the compliance stack to a graph database.” Case files, documents, screening logs, and audit trails are relational workloads, and auditors expect them where auditors expect them. The pattern that has served me is the projection: PostgreSQL stays the system of record, a scheduled job projects just the entity-and-edge subset into Neo4j, traversal questions run there, and the answers (UBO candidates, computed effective percentages, cycle flags) are written back as ordinary rows. The graph is disposable by design; if it drifts or the schema needs to change, you rebuild it from the source tables.

It is worth saying that this is a choice with two sane settings, and I run both. In AfricaPEP the graph is the system of record, because the product is the network, and PostgreSQL is the projection (a search index for fast fuzzy screening). In KYB work the relational store owns truth and the graph is the index. The rule that generalizes: whichever store answers your dominant question owns the truth, and the other should be rebuildable from it at any time. What you should not build is two databases that both believe they are authoritative.

The part the vendors skip: the data

Neo4j makes traversal elegant, and none of that elegance survives contact with bad edges. The genuinely hard part of beneficial ownership in African markets is upstream of any database. Many corporate registries on the continent are partially digitized or pay-per-search; Ghana's beneficial ownership register, launched under the Companies Act of 2019, is real progress but young; filings lag reality; and the same director appears as “Emeka Adeyemi,” “E. Adeyemi,” and “Adeyemi Emeka” across three filings. Which means every edge in your ownership graph is only as good as the entity resolution that decided those three strings are one person; graph quality is entity resolution wearing a different hat. Open datasets help more every year (OpenOwnership's register and OpenCorporates are the places to start), but for most African jurisdictions you are assembling evidence, not downloading truth, and your schema should admit that: source and date on every edge, and “unknown” as a first-class ownership value rather than a guess.

What I would tell you if you were building this

  • Write the questions before the schema. Who is the UBO, are there cycles, who controls without owning, which customers share officers: the model in this post is just those four queries, reverse-engineered.
  • Treat effective ownership as path math. Product along a path, sum over paths, converging series around cycles. If your implementation cannot state which of the three it is doing, it is doing none of them.
  • Bound every traversal. Depth caps and cycle semantics are your job in every database; the graph just makes the decision legible.
  • Put provenance on edges and keep the graph rebuildable. One store owns truth; the other is an index with a rebuild script.
  • Budget for the data, not the queries. The Cypher in this post is an afternoon. The registry wrangling and entity resolution behind it are the project.

The graph side of this thinking runs live in AfricaPEP, where Neo4j holds political-exposure networks for all 54 African countries, and the story of building it is in the AfricaPEP case study. For where ownership graphs plug into the wider monitoring picture, see why rule-based AML systems fail.

Sources and further reading

  • FATF, Guidance on Beneficial Ownership of Legal Persons (2023) (the Recommendation 24 revision behind modern UBO rules)
  • OpenOwnership and OpenCorporates (the open beneficial-ownership and corporate registries)
  • ICIJ Offshore Leaks Database (the Panama and Pandora Papers investigations that made ownership graphs famous, and were themselves built on Neo4j)
  • Neo4j Cypher manual: patterns and variable-length traversal
Patrick Attankurugu
Patrick Attankurugu
Senior AI Engineer at Agregar Technologies, building production AML, KYC, and KYB compliance systems for African financial institutions. Creator and maintainer of AfricaPEP.