chore: sync submodule state (parent ref update)
Made-with: Cursor
This commit is contained in:
215
IMPLEMENTATION_CHECKLIST.md
Normal file
215
IMPLEMENTATION_CHECKLIST.md
Normal file
@@ -0,0 +1,215 @@
|
||||
# Ledger Correctness Boundaries - Implementation Checklist
|
||||
|
||||
## ✅ Completed
|
||||
|
||||
- [x] SQL migration files created
|
||||
- [x] `001_ledger_idempotency.sql` - Unique constraint
|
||||
- [x] `002_dual_ledger_outbox.sql` - Outbox table
|
||||
- [x] `003_outbox_state_machine.sql` - State transitions
|
||||
- [x] `004_balance_constraints.sql` - Balance integrity
|
||||
- [x] `005_post_ledger_entry.sql` - Atomic posting function
|
||||
|
||||
- [x] Prisma schema updated
|
||||
- [x] `dual_ledger_outbox` model added with correct mappings
|
||||
|
||||
- [x] Worker service created
|
||||
- [x] `DualLedgerOutboxWorker` with retry/backoff
|
||||
- [x] `run-dual-ledger-outbox.ts` runner
|
||||
|
||||
- [x] GSS Master Ledger service refactored
|
||||
- [x] DBIS-first posting
|
||||
- [x] Outbox pattern integration
|
||||
- [x] Transactional guarantees
|
||||
|
||||
- [x] Ledger posting module created
|
||||
- [x] Guarded access enforcement
|
||||
- [x] SQL function wrapper
|
||||
|
||||
## 🔄 Next Steps (Deployment)
|
||||
|
||||
### 1. Verify Database Column Names
|
||||
|
||||
**CRITICAL**: Before running migrations, verify your database uses snake_case or camelCase:
|
||||
|
||||
```sql
|
||||
-- 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:
|
||||
|
||||
```sql
|
||||
-- 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
|
||||
|
||||
```bash
|
||||
# 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
|
||||
|
||||
```bash
|
||||
npx prisma generate
|
||||
```
|
||||
|
||||
### 5. Deploy Worker
|
||||
|
||||
```bash
|
||||
# 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:
|
||||
|
||||
```typescript
|
||||
// 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()`:
|
||||
|
||||
```typescript
|
||||
// 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:
|
||||
|
||||
```sql
|
||||
-- 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
|
||||
|
||||
```typescript
|
||||
// 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
|
||||
|
||||
```typescript
|
||||
// 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
|
||||
Reference in New Issue
Block a user