Transaction and Account Data Model

This document defines a standardised Neo4j data model for banking transactions and customer data.
Key Features for Automated Processing:
-
Comprehensive node labels with precise property definitions and data types
-
Explicit relationship patterns with directional semantics
-
Complete constraint definitions ensuring data integrity
-
Executable demonstration code for validation and testing
-
Standard naming conventions following Neo4j best practices
Raw Text Version of Data Model:
1. Node Labels and Properties
Account
Labels:
-
Account
: (Required) Base label for all bank accounts in the system -
Internal
: Label for accounts held within this bank (our customers' accounts). Essential for identifying fund flows leaving the organisation (Internal→External transactions) or incoming liquidity (External→Internal transactions) -
External
: Label for accounts held at other financial institutions. Critical for monitoring cross-institutional transfers and identifying potential money laundering patterns -
HighRiskJurisdiction
: Label for accounts in countries with elevated regulatory or compliance risks
Properties:
-
accountNumber
(String): Unique account identifier (e.g., sort code and account number, IBAN) -
accountType
(String): Optional: Type of account (e.g., "CURRENT", "SAVINGS", "BUSINESS", "LOAN") -
openDate
(DateTime): Optional: Date and time when the account was first opened -
closedDate
(DateTime): Optional: Date and time when the account was permanently closed (null if still active) -
suspendedDate
(DateTime): Optional: Date and time when the account was temporarily suspended (null if not suspended)
Address
Properties:
-
addressLine1
(String): Primary address line containing house/building number and street name -
addressLine2
(String): Optional: Secondary address details such as flat number, building name, or estate -
postTown
(String): Post town as defined by Royal Mail (the town or city for postal delivery) -
postCode
(String): UK postcode or international postal code for the address -
region
(String): Optional: County, state, or administrative region -
latitude
(Float): Optional: Geographic latitude coordinate for precise location mapping -
longitude
(Float): Optional: Geographic longitude coordinate for precise location mapping -
createdAt
(DateTime): Timestamp when this address was first recorded in the system
Counterparty
Properties:
-
counterpartyId
(String): Unique system identifier for the counterparty entity -
name
(String): Legal name of the counterparty (person’s full name or registered company name) -
type
(String): Classification of counterparty (e.g., "INDIVIDUAL", "BUSINESS", "GOVERNMENT", "CHARITY") -
registrationNumber
(String): Optional: Official registration number (Companies House number, charity number, etc.) -
createdAt
(DateTime): Timestamp when this counterparty was first recorded in the system
Country
Properties:
-
code
(String): Two-letter ISO 3166-1 alpha-2 country code (e.g., "GB" for United Kingdom, "US" for United States) -
name
(String): Official full country name in English
Customer
Properties:
-
customerId
(String): Unique system identifier for the customer record -
firstName
(String): Optional: Customer’s given name (first name) -
middleName
(String): Optional: Customer’s middle name(s) or initial(s) -
lastName
(String): Optional: Customer’s family name (surname) -
dateOfBirth
(Date): Optional: Customer’s date of birth (used for identity verification and age calculations) -
placeOfBirth
(String): Optional: City or location where the customer was born (as stated on official documents) -
countryOfBirth
(String): Optional: ISO 3166-1 country code where the customer was born (may differ from nationality)
Device
Properties:
-
deviceId
(String): Unique fingerprint or identifier for the device accessing the system -
deviceType
(String): Optional: Category of device (e.g., "mobile", "desktop", "tablet", "unknown") -
userAgent
(String): Optional: Browser or application user agent string containing device and software information -
createdAt
(DateTime): Timestamp when this device was first detected and recorded in the system
DrivingLicense
Properties:
-
licenseNumber
(String): Unique driving licence number as printed on the physical document -
issueDate
(Date): Optional: Date when the driving licence was originally issued -
expiryDate
(Date): Optional: Date when the driving licence expires and requires renewal -
issuingCountry
(String): Optional: ISO 3166-1 country code of the authority that issued the licence -
createdAt
(DateTime): Timestamp when this driving licence record was first created in the system
Properties:
-
address
(String): Complete email address (e.g., "john.smith@example.com") -
domain
(String): Optional: Domain portion of the email address (e.g., "example.com") -
createdAt
(DateTime): Timestamp when this email address was first recorded in the system
Face
Properties:
-
faceId
(String): Unique identifier for the face biometric record -
embedding
(List of Float): High-dimensional vector representation of facial features (typically 512-1536 dimensions) -
createdAt
(DateTime): Timestamp when this facial biometric record was created in the system
IP
Properties:
-
ipAddress
(String): Internet Protocol address used to access the system (IPv4 or IPv6 format) -
createdAt
(DateTime): Timestamp when this IP address was first observed in the system
ISP
Properties:
-
name
(String): Name of the Internet Service Provider or telecommunications company -
createdAt
(DateTime): Timestamp when this ISP was first recorded in the system
Location
Properties:
-
city
(String): Name of the city or major population centre -
postCode
(String): Postal code or ZIP code for the general area (may be partial for privacy) -
country
(String): ISO 3166-1 country code where this location is situated -
latitude
(Float): Optional: Geographic latitude coordinate for the location centre -
longitude
(Float): Optional: Geographic longitude coordinate for the location centre -
createdAt
(DateTime): Timestamp when this location was first recorded in the system
Movement
Properties:
-
movementId
(String): Unique system identifier for this individual movement -
amount
(Float): Monetary value of this specific movement (always positive) -
currency
(String): Three-letter ISO 4217 currency code (e.g., "GBP", "USD", "EUR") -
date
(DateTime): Date and time when this movement was executed or scheduled -
description
(String): Optional: Human-readable description explaining the movement’s purpose (e.g., "Insurance claim payment 1 of 3") -
status
(String): Optional: Current processing status (e.g., "PENDING", "COMPLETED", "CANCELLED", "FAILED") -
sequenceNumber
(Integer): Chronological order of this movement within a series (starts from 1) -
authorisedBy
(String): Optional: User ID or name of the person who authorised this movement -
validatedBy
(String): Optional: User ID or name of the person who provided secondary approval (for dual control processes) -
createdAt
(DateTime): Timestamp when this movement record was initially created in the system
Passport
Properties:
-
passportNumber
(String): Unique passport number as printed on the document -
issueDate
(Date): Optional: Date when the passport was originally issued -
expiryDate
(Date): Optional: Date when the passport expires and requires renewal -
issuingCountry
(String): Optional: ISO 3166-1 country code of the government that issued this passport -
nationality
(String): Optional: Nationality or citizenship recorded in the passport (may differ from issuing country) -
createdAt
(DateTime): Timestamp when this passport record was first created in the system
Phone
Properties:
-
number
(String): Complete phone number including country code (e.g., "447971020304") -
countryCode
(String): Optional: International dialling code with plus prefix (e.g., "+44", "+1") -
createdAt
(DateTime): Timestamp when this phone number was first recorded in the system
Session
Properties:
-
sessionId
(String): Unique identifier for the user session or login attempt -
status
(String): Optional: Outcome of the session (e.g., "success", "failed", "suspicious", "timeout") -
createdAt
(DateTime): Timestamp when the session was initiated
Transaction
Properties:
-
transactionId
(String): Unique system identifier for the transaction -
amount
(Float): Monetary value of the transaction (always positive, direction indicated by relationships) -
currency
(String): Three-letter ISO 4217 currency code (e.g., "GBP", "USD", "EUR") -
date
(DateTime): Date and time when the transaction was processed or initiated -
message
(String): Optional: Payment reference or description text (e.g., "Swift payment for services", "Invoice #12345") -
type
(String): Optional: Payment method or transaction type (e.g., "SWIFT", "ACH", "FASTER_PAYMENT", "CARD")
2. Relationship Types and Properties
:BENEFITS_TO
-
Direction: Transaction→Account
-
Properties: None
-
Description: Links a transaction to the account that receives the funds (the destination or beneficiary account)
:HAS_ACCOUNT
-
Direction: Customer→Account
-
Properties:
-
role
(String): Customer’s role on the account (e.g., "owner", "joint", "authorised_signatory") -
since
(DateTime): Date and time when this account relationship was established
-
:HAS_ACCOUNT
-
Direction: Counterparty→Account
-
Properties:
-
since
(DateTime): Date and time when this counterparty-account relationship was first recorded
-
:HAS_ADDRESS
-
Direction: Customer→Address
-
Properties:
-
addedAt
(DateTime): When this address was first linked to the customer -
lastChangedAt
(DateTime): When the address details were last updated -
isCurrent
(Boolean): Whether this is the customer’s current residential address
-
:HAS_ADDRESS
-
Direction: Counterparty→Address
-
Properties:
-
since
(DateTime): When this address was first linked to the counterparty -
isCurrent
(Boolean): Whether this is the counterparty’s current registered address
-
:HAS_DRIVING_LICENSE
-
Direction: Customer→DrivingLicense
-
Properties:
-
verificationDate
(DateTime): When the driving licence was verified by the bank -
verificationMethod
(String): How verification was performed (e.g., "BRANCH_VERIFIED", "DIGITAL_SCAN", "VIDEO_KYC") -
verificationStatus
(String): Current verification status (e.g., "VERIFIED", "PENDING", "REJECTED", "EXPIRED")
-
:HAS_EMAIL
-
Direction: Customer→Email
-
Properties:
-
since
(DateTime): When this email address was first linked to the customer record
-
:HAS_FACE
-
Direction: Customer→Face
-
Properties:
-
verificationDate
(DateTime): When the facial biometric was verified against the customer -
verificationMethod
(String): How facial verification was performed (e.g., "SELFIE", "VIDEO_KYC", "BRANCH_PHOTO") -
verificationStatus
(String): Current verification status (e.g., "VERIFIED", "PENDING", "REJECTED")
-
:HAS_NATIONALITY
-
Direction: Customer→Country
-
Properties: None
-
Description: Links a customer to their country of citizenship or nationality (customers may have multiple nationalities)
:HAS_PASSPORT
-
Direction: Customer→Passport
-
Properties:
-
verificationDate
(DateTime): When the passport was verified by the bank -
verificationMethod
(String): How verification was performed (e.g., "BRANCH_VERIFIED", "DIGITAL_SCAN", "VIDEO_KYC") -
verificationStatus
(String): Current verification status (e.g., "VERIFIED", "PENDING", "REJECTED", "EXPIRED")
-
:HAS_PHONE
-
Direction: Customer→Phone
-
Properties:
-
since
(DateTime): When this phone number was first linked to the customer record
-
:IMPLIED
-
Direction: Transaction→Movement
-
Properties:
-
totalMovements
(Integer): Total number of movements that will result from this transaction
-
-
Description: Links a transaction to its component movements when a single transaction results in multiple separate payments (e.g., insurance claim paid in instalments, loan disbursement in tranches)
:IS_ALLOCATED_TO
-
Direction: IP→ISP
-
Properties:
-
createdAt
(DateTime): Timestamp when this IP address allocation was first recorded
-
-
Description: Links an IP address to the Internet Service Provider that owns or manages that IP address range
:IS_HOSTED
-
Direction: Account→Country
-
Properties: None
-
Description: Links an account to the country where it is held or domiciled (using ISO 3166-1 country codes)
:LOCATED_IN
-
Direction: Address→Country
-
Properties: None
-
Description: Links a physical address to the country where it is located (using ISO 3166-1 country codes)
:LOCATED_IN
-
Direction: IP→Location
-
Properties:
-
createdAt
(DateTime): Timestamp when this IP geolocation was first determined
-
-
Description: Links an IP address to its approximate geographic location based on geolocation services
:LOCATED_IN
-
Direction: Location→Country
-
Properties: None
-
Description: Links a geographic location to the country where it is situated (using ISO 3166-1 country codes)
:PERFORMS
-
Direction: Account→Transaction
-
Properties: None
-
Description: Links the account that initiated or sent the transaction (the source or debtor account)
:SESSION_USES_DEVICE
-
Direction: Session→Device
-
Properties: None
-
Description: Records which device was used to establish or conduct the session
3. Constraints and Indexes
// Constraints
CREATE CONSTRAINT customer_id IF NOT EXISTS
FOR (c:Customer) REQUIRE c.customerId IS NODE KEY;
CREATE CONSTRAINT email_address IF NOT EXISTS
FOR (e:Email) REQUIRE e.address IS NODE KEY;
CREATE CONSTRAINT phone_number IF NOT EXISTS
FOR (p:Phone) REQUIRE p.number IS NODE KEY;
CREATE CONSTRAINT passport_number IF NOT EXISTS
FOR (p:Passport) REQUIRE (p.passportNumber, p.issuingCountry) IS NODE KEY;
CREATE CONSTRAINT driving_licence_number IF NOT EXISTS
FOR (d:DrivingLicense) REQUIRE (d.licenseNumber, d.issuingCountry) IS NODE KEY;
CREATE CONSTRAINT device_id IF NOT EXISTS
FOR (d:Device) REQUIRE d.deviceId IS NODE KEY;
CREATE CONSTRAINT ip_address IF NOT EXISTS
FOR (i:IP) REQUIRE i.ipAddress IS NODE KEY;
CREATE CONSTRAINT session_id IF NOT EXISTS
FOR (s:Session) REQUIRE s.sessionId IS NODE KEY;
CREATE CONSTRAINT account_number IF NOT EXISTS
FOR (a:Account) REQUIRE a.accountNumber IS NODE KEY;
CREATE CONSTRAINT transaction_id IF NOT EXISTS
FOR (t:Transaction) REQUIRE t.transactionId IS NODE KEY;
CREATE CONSTRAINT face_id IF NOT EXISTS
FOR (f:Face) REQUIRE f.faceId IS NODE KEY;
CREATE CONSTRAINT counterparty_id IF NOT EXISTS
FOR (cp:Counterparty) REQUIRE cp.counterpartyId IS NODE KEY;
CREATE CONSTRAINT movement_id IF NOT EXISTS
FOR (m:Movement) REQUIRE m.movementId IS NODE KEY;
CREATE CONSTRAINT isp_name IF NOT EXISTS
FOR (i:ISP) REQUIRE i.name IS NODE KEY;
CREATE CONSTRAINT country_code IF NOT EXISTS
FOR (c:Country) REQUIRE c.code IS NODE KEY;
CREATE CONSTRAINT address_composite IF NOT EXISTS
FOR (a:Address) REQUIRE (a.addressLine1, a.postTown, a.postCode) IS NODE KEY;
// Additional Indexes
CREATE INDEX transaction_date_idx IF NOT EXISTS FOR (t:Transaction) ON (t.date);
CREATE INDEX transaction_amount_idx IF NOT EXISTS FOR (t:Transaction) ON (t.amount);
// Vector index for face embeddings (used for biometric matching)
CALL db.index.vector.createNodeIndex(
'face_embedding_idx',
'Face',
'embedding',
1536, // dimensionality of the face embeddings (typical range: 512-1536)
'cosine' // similarity metric for comparing facial features
);
// Full Text Search
// Full-text index for customer name search
CREATE FULLTEXT INDEX customer_name_idx IF NOT EXISTS FOR (c:Customer) ON EACH [c.firstName, c.lastName, c.middleName];
4. Minimal Demo Code
The following Cypher code creates a complete example dataset demonstrating all the main entities and relationships in the model. This code is suitable for testing and demonstration purposes.
//--------------------
// Create base countries
//--------------------
CREATE (uk:Country {code: "GB", name: "United Kingdom"})
CREATE (us:Country {code: "US", name: "United States"})
//--------------------
// Create customer and their identity information
//--------------------
// Create main customer node
CREATE (c:Customer {
customerId: "CUS001",
firstName: "John",
middleName: "Michael",
lastName: "Smith",
dateOfBirth: date("1990-01-01"),
placeOfBirth: "Manchester",
countryOfBirth: "GB"
})
// Create customer's address
CREATE (addr:Address {
addressLine1: "123 High Street",
addressLine2: "Flat 4B",
postTown: "London",
postCode: "SW1A 1AA",
region: "Greater London",
latitude: 51.5074,
longitude: -0.1278,
createdAt: datetime("2024-01-01T00:00:00")
})
// Create customer's contact information
CREATE (e:Email {
address: "john@example.com",
domain: "example.com",
createdAt: datetime("2024-01-01T00:00:00")
})
CREATE (ph:Phone {
number: "447971020304",
countryCode: "+44",
createdAt: datetime("2024-01-01T00:00:00")
})
// Create customer's identity documents
CREATE (p:Passport {
passportNumber: "123456789",
issueDate: date("2020-01-01"),
expiryDate: date("2030-01-01"),
issuingCountry: "GB",
nationality: "British",
createdAt: datetime("2024-01-01T00:00:00")
})
CREATE (dl:DrivingLicense {
licenseNumber: "SMITH901011J99AB",
issueDate: date("2018-01-01"),
expiryDate: date("2028-01-01"),
issuingCountry: "GB",
createdAt: datetime("2024-01-01T00:00:00")
})
// Create customer's face biometric
CREATE (f:Face {
faceId: "FACE001",
embedding: [0.153, 0.254, 0.255, 0.0, 0.0, 0.253, 0.200, 0.252, 0.254, 0.100, 0.253], // Truncated example of 1536-dimension vector
createdAt: datetime("2024-01-01T00:00:00")
})
//--------------------
// Create all identity relationships
//--------------------
// Link customer to their identity information
CREATE (c)-[:HAS_ADDRESS {
addedAt: datetime("2024-01-01T00:00:00"),
lastChangedAt: datetime("2024-01-01T00:00:00"),
isCurrent: true
}]->(addr)
CREATE (addr)-[:LOCATED_IN]->(uk)
CREATE (c)-[:HAS_EMAIL {since: datetime("2024-01-01T00:00:00")}]->(e)
CREATE (c)-[:HAS_PHONE {since: datetime("2024-01-01T00:00:00")}]->(ph)
CREATE (c)-[:HAS_NATIONALITY]->(uk)
// Link customer to their identity documents
CREATE (c)-[:HAS_PASSPORT {
verificationDate: datetime("2024-01-01T00:00:00"),
verificationMethod: "VIDEO_KYC",
verificationStatus: "VERIFIED"
}]->(p)
CREATE (c)-[:HAS_DRIVING_LICENSE {
verificationDate: datetime("2024-01-01T00:00:00"),
verificationMethod: "BRANCH_VERIFIED",
verificationStatus: "VERIFIED"
}]->(dl)
CREATE (c)-[:HAS_FACE {
verificationDate: datetime("2024-01-01T00:00:00"),
verificationMethod: "SELFIE",
verificationStatus: "VERIFIED"
}]->(f)
//--------------------
// Create digital access nodes
//--------------------
// Create device, IP, ISP, and session information
CREATE (d:Device {
deviceId: "DEV001",
deviceType: "desktop",
userAgent: "Mozilla/5.0 Chrome/91.0",
createdAt: datetime("2024-01-01T00:00:00")
})
CREATE (ip:IP {ipAddress: "192.168.1.1", createdAt: datetime("2024-01-01T00:00:00")})
CREATE (isp:ISP {name: "BT", createdAt: datetime("2024-01-01T00:00:00")})
CREATE (loc:Location {
city: "London",
postCode: "SW1A",
country: "GB",
latitude: 51.5074,
longitude: -0.1278,
createdAt: datetime("2024-01-01T00:00:00")
})
CREATE (s:Session {
sessionId: "SESS001",
status: "success",
createdAt: datetime("2024-03-01T10:00:00")
})
// Create digital access relationships
CREATE (d)-[:USED_BY {lastUsed: datetime("2024-03-01T10:00:00")}]->(c)
CREATE (s)-[:USES_IP]->(ip)
CREATE (ip)-[:IS_ALLOCATED_TO {createdAt: datetime("2024-01-01T00:00:00")}]->(isp)
CREATE (ip)-[:LOCATED_IN {createdAt: datetime("2024-01-01T00:00:00")}]->(loc)
CREATE (loc)-[:LOCATED_IN]->(uk)
CREATE (s)-[:SESSION_USES_DEVICE]->(d)
//--------------------
// Create banking nodes
//--------------------
// Create internal and external accounts
CREATE (a:Account:Internal {
accountNumber: "ACC001",
accountType: "CURRENT",
openDate: datetime("2024-01-01T00:00:00"),
closedDate: null,
suspendedDate: null
})
CREATE (a2:Account:External:HighRiskJurisdiction {
accountNumber: "ACC002",
accountType: null,
openDate: null,
closedDate: null,
suspendedDate: null
})
// Create transaction
CREATE (t1:Transaction {
transactionId: "TXN001",
amount: 1000.00,
currency: "GBP",
date: datetime("2024-03-01T11:00:00"),
message: "Payment for services",
type: "SWIFT"
})
//--------------------
// Create banking relationships
//--------------------
// Link accounts to customer and countries
CREATE (c)-[:HAS_ACCOUNT {role: "OWNER", since: datetime("2024-01-01T00:00:00")}]->(a)
CREATE (a)-[:IS_HOSTED]->(uk)
CREATE (a2)-[:IS_HOSTED]->(us)
// Create transaction flow
CREATE (a)-[:PERFORMS]->(t1)-[:BENEFITS_TO]->(a2)
//--------------------
// Create movements for transaction (example: insurance claim paid in instalments)
//--------------------
// Create movements
CREATE (m1:Movement {
movementId: "MOV001",
amount: 400.00,
currency: "GBP",
date: datetime("2024-03-05T10:00:00"),
description: "Insurance claim payment 1 of 3",
status: "COMPLETED",
sequenceNumber: 1,
authorisedBy: "USER001",
validatedBy: "MANAGER001",
createdAt: datetime("2024-03-01T11:00:00")
})
CREATE (m2:Movement {
movementId: "MOV002",
amount: 400.00,
currency: "GBP",
date: datetime("2024-03-12T10:00:00"),
description: "Insurance claim payment 2 of 3",
status: "COMPLETED",
sequenceNumber: 2,
authorisedBy: "USER001",
validatedBy: "MANAGER002",
createdAt: datetime("2024-03-01T11:00:00")
})
CREATE (m3:Movement {
movementId: "MOV003",
amount: 200.00,
currency: "GBP",
date: datetime("2024-03-19T10:00:00"),
description: "Insurance claim payment 3 of 3",
sequenceNumber: 3,
authorisedBy: "USER001",
validatedBy: null,
createdAt: datetime("2024-03-01T11:00:00")
})
// Link transaction to movements
CREATE (t1)-[:IMPLIED {totalMovements: 3}]->(m1)
CREATE (t1)-[:IMPLIED {totalMovements: 3}]->(m2)
CREATE (t1)-[:IMPLIED {totalMovements: 3}]->(m3)
//--------------------
// Create counterparty
//--------------------
// Create counterparty and their address
CREATE (cp:Counterparty {
counterpartyId: "CP001",
name: "ACME Corporation Ltd",
type: "BUSINESS",
registrationNumber: "12345678",
createdAt: datetime("2024-02-01T00:00:00")
})
CREATE (cpAddr:Address {
addressLine1: "456 Business Park",
addressLine2: "Suite 100",
postTown: "Birmingham",
postCode: "B1 1AA",
region: "West Midlands",
latitude: 52.4862,
longitude: -1.8904,
createdAt: datetime("2024-02-01T00:00:00")
})
// Link counterparty to address and account
CREATE (cp)-[:HAS_ADDRESS {
since: datetime("2024-02-01T00:00:00"),
isCurrent: true
}]->(cpAddr)
CREATE (cpAddr)-[:LOCATED_IN]->(uk)
CREATE (cp)-[:HAS_ACCOUNT {
since: datetime("2024-02-01T00:00:00")
}]->(a2)