The FinCrime Graph investigation overview, with alert, case and transaction summaries

The problem

Financial-crime teams drown in alerts that live in separate tools. Alerts, customer data and case notes sit in different places, so analysts spend their time copying context instead of investigating.

The patterns that matter are structural. Money looping back to where it started, or funds split below reporting limits, are easy to see on a graph and almost invisible in a transaction table.

Regulators and auditors ask who decided what, on which evidence, and whether that evidence was changed afterwards. Spreadsheets and email cannot answer that reliably.

The approach

One workspace for the whole path: signal, alert, case, evidence, finding, decision and outcome. Detection rules raise alerts, a graph database shows how accounts are connected, and customer risk scoring and watchlist screening add context.

An analyst opens a case and attaches evidence. The case then passes through independent review, a maker-checker decision, controlled execution and a sealed outcome, and every step is written to a hash-chained audit ledger.

What it does

  • Detection of five typologies: circular flow, structuring, large cross-border transfers, high-risk counterparties and velocity anomalies
  • An alert queue with server-side search, severity and status filters, live counts and deep links
  • An investigation overview whose panels load independently, so one failing source never blanks the page
  • A case workspace with notes, evidence, assignment, a searchable timeline and a PDF case report
  • A triage queue that scores and orders open cases and shows who owns them
  • Network investigation on a Neo4j graph: money paths, account neighbourhoods and circular structures
  • Customer risk scores built from six factors, with the contributing factors shown
  • Watchlist screening with fuzzy name matching, aliases and a clear or escalate review step
  • A compliance command centre with SLA clocks and breaches
  • Chain of custody with SHA-256 integrity hashes, and four-eyes review at every checkpoint
  • Governed findings, decisions, execution, outcome attestations and conclusion release
  • Detection rule governance with a change history, a hash-chained audit ledger and a data ingestion centre
  • A command palette, a URL for every screen, saved views, CSV export and a phone layout

Where the engineering is

Two databases, one source of truth

PostgreSQL is the system of record for customers, accounts, transactions, alerts, cases, evidence and the audit trail. Neo4j holds a projection of accounts and transfers, so path, neighbourhood and cycle questions are one Cypher query instead of many joins.

The graph is rebuilt from PostgreSQL in a single transaction, in batches, so readers never see a half-built graph, and a graph problem can never corrupt the records of a case.

The network investigation workbench showing two circular money flows between accounts, Open the full-size image (opens in a new tab)
A circular flow found on the money-movement graph.

Governance as code, not as a policy document

Maker-checker review, role gates, integrity hashes and the audit ledger are enforced by the services themselves, so an analyst cannot skip a step by accident.

The four-eyes rule applies at every checkpoint: the evidence reviewer must differ from the custodian, the finding reviewer from the submitter, the decision reviewer from the proposer and the release reviewer from the preparer. Findings can only rest on evidence that has been verified and accepted.

Decision governance with a versioned proposal awaiting independent review, Open the full-size image (opens in a new tab)
Maker-checker approval of case decisions.

Proving that evidence was not altered

Every item of case evidence has a custody log with SHA-256 integrity hashes, so anyone can check that it is the same as when it was attached.

Security events go into an audit ledger where each entry is chained to the one before, so removing or editing history is detectable. Administrators can search the ledger by date range and verify the chain.

The security audit ledger with source events, ledger entries and a verified hash chain, Open the full-size image (opens in a new tab)
A hash-chained record of security-relevant events.

Identity from the token, and safe by default

Case notes and audit entries are attributed to the signed-in user taken from the JWT. Request parameters that claim to say who acted are ignored, so nobody can write under another name.

A guard refuses to start the production profile while the demo secret or demo passwords are still in use. Sign-in is rate limited per user name, audit text is clipped to its column width, and free text travels in the request body instead of the URL so it stays out of access logs.

Twenty modules that feel like one product

Grouped navigation, one command palette, shared list tools and a shared design layer keep a wide product coherent. Every screen has its own URL, so refresh, back and forward, and shared links all work.

Overview data loads with Promise.allSettled, every module has its own error boundary, and an expired session returns the user to sign-in with a clear message. One broken dependency costs one panel, not the whole screen.

The command palette searching alerts, cases and transactions, Open the full-size image (opens in a new tab)
Jump anywhere, or search alerts, cases and transactions.

Screens

How it's built

LayerTechnology
BackendJava 21 and Spring Boot 4.1, a stateless REST API with 91 endpoints across 26 controllers
SecuritySpring Security with signed JWTs, role-based method security, per-user sign-in lockout and an insecure-defaults guard
DatabasePostgreSQL 17 as the system of record, with 18 Flyway migrations and 39 tables
GraphNeo4j for the money-flow graph, queried with Cypher
FrontendNext.js 16, React 19, TypeScript and Cytoscape.js for the network graph
Testing97 automated unit tests, and browser tests against a mock API
Architecture

Results

  • Runs end to end on synthetic data, from detection to a sealed case outcome.
  • 20 modules and 91 REST endpoints across 26 controllers.
  • 18 Flyway migrations and 39 tables.
  • 97 automated tests.

What I would build next

  • A real user store with password hashing, and an httpOnly cookie session instead of a token in session storage.
  • Paged analytics lists and a bounded graph query for large data.
  • Integration tests against real PostgreSQL and Neo4j containers, and continuous integration.
  • Local-time display and user preferences.

What I learned

  • Modelling workflow states explicitly makes governance rules easy to test.
  • Server-side paging, filtering and counts matter early; in-memory filtering only works while the data is small.
  • Accessibility work, such as focus management, combobox semantics and reduced motion, is cheaper when it is part of the first build of a component.

Built with

  • Java
  • Spring Boot
  • Spring Security
  • PostgreSQL
  • Neo4j
  • Next.js
  • React
  • TypeScript

56 results