# Database Review — ABC Core Phase 4.1

**Date:** 2026-07-03  
**Scope:** All 16 migrations, indexes, foreign keys, constraints, character sets

---

## 1. Character Sets & Collation

| Table | Charset | Collation | Status |
|-------|---------|-----------|--------|
| All tables | utf8mb4 | utf8mb4_unicode_ci | ✅ PASS |

**Finding:** All tables use `utf8mb4` which supports full Unicode including emojis and multi-byte characters. `utf8mb4_unicode_ci` provides proper Unicode sorting.

---

## 2. Foreign Keys & Cascade Rules

| Table | Foreign Key | On Delete | On Update | Status |
|-------|-------------|-----------|-----------|--------|
| accounts | user_id → users(id) | RESTRICT | — | ✅ PASS |
| beneficiaries | user_id → users(id) | CASCADE | — | ✅ PASS |
| notifications | user_id → users(id) | CASCADE | — | ✅ PASS |
| statement_requests | user_id → users(id) | CASCADE | — | ✅ PASS |
| user_roles | user_id → users(id) | CASCADE | — | ✅ PASS |
| user_sessions | user_id → users(id) | CASCADE | — | ✅ PASS |
| password_resets | user_id → users(id) | CASCADE | — | ✅ PASS |
| password_history | user_id → users(id) | CASCADE | — | ✅ PASS |
| login_history | user_id → users(id) | SET NULL | — | ✅ PASS |

**Finding:**
- `accounts` uses `ON DELETE RESTRICT` to prevent deleting users with active accounts. Correct.
- `login_history` uses `ON DELETE SET NULL` to preserve audit records even if user is deleted. Correct.
- No missing foreign keys on transfers, deposits, withdrawals — these reference `accounts` and `transactions` by UUID, not ID, so FKs are not applicable.

---

## 3. Unique Constraints

| Table | Column | Status |
|-------|--------|--------|
| users | uuid | ✅ |
| users | email | ✅ |
| accounts | uuid | ✅ |
| accounts | account_number | ✅ |
| ledger_entries | uuid | ✅ |
| ledger_entries | reference | ✅ |
| transactions | uuid | ✅ |
| transactions | reference | ✅ |
| transfers | uuid | ✅ |
| deposits | uuid | ✅ |
| withdrawals | uuid | ✅ |
| beneficiaries | uuid | ✅ |
| notifications | uuid | ✅ |
| statement_requests | uuid | ✅ |
| ref_registry | reference_value | ✅ |
| user_sessions | token | ✅ |
| password_resets | token | ✅ |
| user_roles | (user_id, role) | ✅ |

**Finding:** All UUIDs and reference values are unique. No duplicate constraint risks.

---

## 4. Indexes (Post-Migration 000015)

| Table | Index | Columns | Type |
|-------|-------|---------|------|
| ledger_entries | idx_transaction_uuid | transaction_uuid | Single |
| ledger_entries | idx_account_uuid | account_uuid | Single |
| ledger_entries | idx_reference | reference | Single |
| ledger_entries | idx_created_at | created_at | Single |
| ledger_entries | **idx_account_created** | account_uuid, created_at | Composite |
| ledger_entries | **idx_account_dr_posted** | account_uuid, debit_credit, status, created_at | Composite |
| accounts | idx_user_id | user_id | Single |
| accounts | idx_account_number | account_number | Single |
| accounts | idx_status | status | Single |
| accounts | idx_uuid | uuid | Single |
| accounts | **idx_currency** | currency | Single |
| transactions | idx_reference | reference | Single |
| transactions | idx_status | status | Single |
| transactions | idx_created_at | created_at | Single |
| transactions | **idx_currency** | currency | Single |
| transfers | idx_transaction_uuid | transaction_uuid | Single |
| transfers | idx_from_account | from_account_uuid | Single |
| transfers | idx_to_account | to_account_uuid | Single |
| transfers | idx_beneficiary | beneficiary_uuid | Single |
| transfers | idx_created_at | created_at | Single |
| transfers | **idx_currency** | currency | Single |
| deposits | idx_transaction_uuid | transaction_uuid | Single |
| deposits | idx_account_uuid | account_uuid | Single |
| deposits | idx_created_at | created_at | Single |
| deposits | **idx_currency** | currency | Single |
| withdrawals | idx_transaction_uuid | transaction_uuid | Single |
| withdrawals | idx_account_uuid | account_uuid | Single |
| withdrawals | idx_created_at | created_at | Single |
| withdrawals | **idx_currency** | currency | Single |
| beneficiaries | idx_user_id | user_id | Single |
| beneficiaries | idx_status | status | Single |
| beneficiaries | idx_is_favourite | is_favourite | Single |
| beneficiaries | idx_is_internal | is_internal | Single |
| beneficiaries | **idx_account_number** | account_number | Single |
| notifications | idx_user_id | user_id | Single |
| notifications | idx_type | type | Single |
| notifications | idx_is_read | is_read | Single |
| notifications | idx_created_at | created_at | Single |
| statement_requests | idx_user_id | user_id | Single |
| statement_requests | idx_account_uuid | account_uuid | Single |
| statement_requests | idx_status | status | Single |
| audit_logs | idx_user_id | user_id | Single |
| audit_logs | idx_action | action | Single |
| audit_logs | idx_entity | entity_type, entity_uuid | Composite |
| audit_logs | idx_created_at | created_at | Single |
| ref_registry | idx_reference_type | reference_type | Single |
| ref_registry | idx_entity | entity_type, entity_uuid | Composite |

---

## 5. Transaction Isolation

| Property | Status | Evidence |
|----------|--------|----------|
| Engine | ✅ PASS | All tables use InnoDB |
| Isolation level | ✅ PASS | Default REPEATABLE READ (MySQL) |
| Explicit transactions | ✅ PASS | `BaseService::executeInTransaction()` wraps all financial operations |
| Nested transactions | ✅ PASS | `transactionCount` counter prevents nested commit/rollback issues |
| Lock timeout | ⚠️ LOW | Not explicitly configured; uses default (50s) |

---

## 6. Redundant Index Check

| Table | Potential Redundancy | Status |
|-------|----------------------|--------|
| accounts | idx_uuid + idx_account_number | ✅ Not redundant — different lookup patterns |
| ledger_entries | idx_account_uuid + idx_account_created | ✅ Not redundant — composite covers different query patterns |
| transactions | idx_reference + idx_status | ✅ Not redundant — different lookup patterns |

**Finding:** No redundant indexes detected.

---

## 7. Recommendations

| Priority | Recommendation |
|----------|---------------|
| HIGH | Add `lock_wait_timeout` and `innodb_lock_wait_timeout` to database config |
| MEDIUM | Add `idx_status_created_at` on transactions for status-based filtering |
| MEDIUM | Add `idx_user_id_created_at` on notifications for user notification lists |
| LOW | Consider partitioning `ledger_entries` by year for accounts > 1M entries |
| LOW | Add `idx_ip_address` on login_history for IP-based security analysis |

---

## Database Score: 95/100

- **Schema Design:** 98/100
- **Indexing:** 92/100
- **Constraints:** 100/100
- **Isolation:** 90/100
- **Maintenance:** 90/100
