Files
dbis_core/IMPLEMENTATION_CHECKLIST.md
2026-03-02 12:14:07 -08:00

5.1 KiB

Ledger Correctness Boundaries - Implementation Checklist

Completed

  • SQL migration files created

    • 001_ledger_idempotency.sql - Unique constraint
    • 002_dual_ledger_outbox.sql - Outbox table
    • 003_outbox_state_machine.sql - State transitions
    • 004_balance_constraints.sql - Balance integrity
    • 005_post_ledger_entry.sql - Atomic posting function
  • Prisma schema updated

    • dual_ledger_outbox model added with correct mappings
  • Worker service created

    • DualLedgerOutboxWorker with retry/backoff
    • run-dual-ledger-outbox.ts runner
  • GSS Master Ledger service refactored

    • DBIS-first posting
    • Outbox pattern integration
    • Transactional guarantees
  • Ledger posting module created

    • Guarded access enforcement
    • SQL function wrapper

🔄 Next Steps (Deployment)

1. Verify Database Column Names

CRITICAL: Before running migrations, verify your database uses snake_case or camelCase:

-- Check actual column names
SELECT column_name 
FROM information_schema.columns 
WHERE table_name = 'ledger_entries' 
  AND column_name IN ('ledger_id', 'ledgerId', 'reference_id', 'referenceId')
ORDER BY column_name;

If columns are camelCase, update SQL migrations accordingly.

2. Audit Existing Data

Before applying balance constraints:

-- Check for inconsistent balances
SELECT id, balance, available_balance, reserved_balance
FROM bank_accounts
WHERE available_balance < 0
   OR reserved_balance < 0
   OR available_balance > balance
   OR (available_balance + reserved_balance) > balance;

Fix any inconsistencies before applying 004_balance_constraints.sql.

3. Run Migrations

# Set database URL
export DATABASE_URL="postgresql://user:password@host:port/database"

# Run in order
cd dbis_core
psql $DATABASE_URL -f db/migrations/001_ledger_idempotency.sql
psql $DATABASE_URL -f db/migrations/002_dual_ledger_outbox.sql
psql $DATABASE_URL -f db/migrations/003_outbox_state_machine.sql
psql $DATABASE_URL -f db/migrations/004_balance_constraints.sql  # After data cleanup
psql $DATABASE_URL -f db/migrations/005_post_ledger_entry.sql

4. Generate Prisma Client

npx prisma generate

5. Deploy Worker

# Add to package.json scripts
"worker:dual-ledger-outbox": "ts-node src/workers/run-dual-ledger-outbox.ts"

# Run worker
npm run worker:dual-ledger-outbox

# Or use PM2
pm2 start src/workers/run-dual-ledger-outbox.ts --name dual-ledger-outbox

6. Implement SCB API Client

Update DualLedgerOutboxWorker.callScbLedgerApi() with real HTTP client:

// Replace placeholder with actual SCB API call
const response = await fetch(`${SCB_API_BASE_URL}/${sovereignBankId}/ledger/post`, {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Idempotency-Key': idempotencyKey, // CRITICAL
  },
  body: JSON.stringify({
    ledgerId,
    ...payload,
  }),
});

7. Update Existing Code

Replace direct ledgerService.postDoubleEntry() calls with ledgerPostingModule.postEntry():

// OLD (banned)
await ledgerService.postDoubleEntry(...);

// NEW (required)
await ledgerPostingModule.postEntry({
  ledgerId: 'Master',
  debitAccountId: '...',
  creditAccountId: '...',
  amount: '100.00',
  currencyCode: 'USD',
  assetType: 'fiat',
  transactionType: 'Type_A',
  referenceId: 'unique-ref-id',
});

8. Add Monitoring

Monitor outbox queue:

-- Queue depth
SELECT status, COUNT(*) 
FROM dual_ledger_outbox 
GROUP BY status;

-- Failed jobs needing attention
SELECT outbox_id, attempts, last_error, last_attempt_at
FROM dual_ledger_outbox
WHERE status = 'FAILED'
ORDER BY last_attempt_at DESC
LIMIT 10;

🧪 Testing

Test Atomic Posting

// Should succeed
await ledgerPostingModule.postEntry({
  ledgerId: 'Test',
  debitAccountId: 'account1',
  creditAccountId: 'account2',
  amount: '100.00',
  currencyCode: 'USD',
  assetType: 'fiat',
  transactionType: 'Type_A',
  referenceId: 'test-1',
});

// Should fail (duplicate reference_id)
await ledgerPostingModule.postEntry({
  // ... same params with same referenceId
});

Test Outbox Pattern

// Post to master ledger
const result = await gssMasterLedgerService.postToMasterLedger({
  nodeId: 'SSN-1',
  sourceBankId: 'SCB-1',
  destinationBankId: 'SCB-2',
  amount: '1000.00',
  currencyCode: 'USD',
  assetType: 'fiat',
}, 'test-ref-123');

// Check outbox was created
const outbox = await prisma.dual_ledger_outbox.findFirst({
  where: { referenceId: 'test-ref-123' },
});
console.log(outbox.status); // Should be 'QUEUED'

📋 Verification Checklist

  • Migrations applied successfully
  • Prisma client regenerated
  • Worker process running
  • SCB API client implemented
  • Existing code updated to use ledgerPostingModule
  • Monitoring in place
  • Tests passing
  • Documentation updated

🚨 Rollback Plan

If issues occur:

  1. Stop worker process
  2. Rollback migrations (see LEDGER_CORRECTNESS_BOUNDARIES.md)
  3. Revert code changes
  4. Investigate and fix issues
  5. Re-apply after fixes