AmlNetworkTraversal.java

package com.datastructures.graphs.graphbfsdfs.applied;

import com.datastructures.graphs.graphbfsdfs.classic.Graph;

import java.util.List;

/**
 * Anti-money-laundering network traversal (compliance tooling for a bank/payments fraud team): models
 * account-to-account transaction relationships as an undirected graph - an edge means money
 * moved directly between the two accounts, regardless of direction - and, given a flagged
 * account, finds every other account reachable from it. That reachable set is the connected
 * cluster of accounts potentially involved in the same scheme: money moved two, three, or more
 * hops away from the flagged account is still traceable back to it, which a query limited to
 * "direct counterparties only" would miss entirely.
 *
 * <p>Either traversal (BFS or DFS) visits the same reachable set; this class uses BFS so
 * {@link #accountsReachableFrom} returns accounts ordered by how many transaction hops separate
 * them from the flagged account - the closest, most directly implicated accounts come first,
 * which is a natural triage order for an investigator working the result.
 */
public final class AmlNetworkTraversal {

    private final Graph<String> transactionNetwork = new Graph<>();

    /** Records that money moved directly between these two accounts, in either direction. */
    public void recordTransaction(String fromAccountId, String toAccountId) {
        transactionNetwork.addEdge(fromAccountId, toAccountId);
    }

    /**
     * Every account reachable from {@code flaggedAccountId} - including itself - ordered by
     * transaction-hop distance, closest first. Accounts outside this network's connected
     * component are never even visited, which is exactly what makes this cheaper than a query
     * over every account in the bank.
     */
    public List<String> accountsReachableFrom(String flaggedAccountId) {
        return transactionNetwork.bfs(flaggedAccountId);
    }
}