| name | adr-025-clearing-purpose-specialization | |||
|---|---|---|---|---|
| description | Distinguish clearing accounts by purpose (deposit/withdrawal/settlement/general) for precise account resolution | |||
| triggers |
|
|||
| instructions | Use the ClearingPurpose enum to distinguish clearing accounts by their operational purpose. Set clearing_purpose field only when account_type is CLEARING. Filter clearing accounts by purpose using clearing_purpose_filter in ListInternalAccounts RPC. |
Date: 2026-01-16
Accepted
The Internal Account service supports multiple clearing accounts per instrument (e.g., CLR-GBP-DEPOSIT, CLR-GBP-WITHDRAW). Initially, these accounts were distinguished only by naming convention. This created ambiguity when services needed to programmatically resolve the correct clearing account for a specific operation.
- No type-safe resolution: Services relied on string parsing of account codes to distinguish deposit vs withdrawal clearing accounts
- No database-level filtering: Cannot query "all deposit clearing accounts for GBP" without pattern matching on account_code
- Validation gaps: No enforcement that clearing accounts are used for their intended purpose
- Inconsistent conventions: Different teams might use different naming patterns (CLR-DEPOSIT-GBP vs CLR-GBP-DEPOSIT)
- Current Account Service: When processing a customer deposit, needs the deposit clearing account specifically
- Payment Service: Withdrawal operations need the withdrawal clearing account
- Settlement Service: Settlement operations need settlement clearing accounts
- Account Resolution: Services need to programmatically select the correct clearing account without parsing strings
- Type safety for clearing account resolution
- Database-queryable clearing account purposes
- Backward compatibility with existing clearing accounts
- BIAN compliance for internal account types
- Support for deposit/withdrawal/settlement/general patterns
- Clear validation rules for clearing_purpose usage
- ClearingPurpose enum field (chosen)
- Account subtype string field
- Account tags/labels system
- Separate tables per clearing purpose
Chosen option: "ClearingPurpose enum field", because it provides type safety, database filtering, and clear semantics while maintaining backward compatibility with existing accounts.
Proto definition:
enum ClearingPurpose {
CLEARING_PURPOSE_UNSPECIFIED = 0;
CLEARING_PURPOSE_DEPOSIT = 1;
CLEARING_PURPOSE_WITHDRAWAL = 2;
CLEARING_PURPOSE_SETTLEMENT = 3;
CLEARING_PURPOSE_GENERAL = 4;
}
message InternalAccountFacility {
// ... existing fields ...
ClearingPurpose clearing_purpose = 12;
}
message ListInternalAccountsRequest {
// ... existing fields ...
ClearingPurpose clearing_purpose_filter = 5;
}Database schema:
ALTER TABLE internal_accounts
ADD COLUMN clearing_purpose VARCHAR(32);
-- Constraint ensures clearing_purpose is set for CLEARING accounts
-- and UNSPECIFIED for non-CLEARING accounts
ALTER TABLE internal_accounts
ADD CONSTRAINT chk_clearing_purpose_consistency
CHECK (
(account_type = 'CLEARING' AND clearing_purpose IS NOT NULL AND clearing_purpose != 'UNSPECIFIED')
OR
(account_type != 'CLEARING' AND (clearing_purpose IS NULL OR clearing_purpose = 'UNSPECIFIED'))
);Validation rules:
- For CLEARING accounts:
clearing_purposemust be non-UNSPECIFIED (DEPOSIT, WITHDRAWAL, SETTLEMENT, or GENERAL) - For non-CLEARING accounts:
clearing_purposemust be UNSPECIFIED or omitted - Enforced at both proto validation layer and database constraint
- Type-safe resolution: Services can filter by ClearingPurpose enum instead of parsing account codes
- Database-level filtering:
clearing_purpose_filterin ListInternalAccounts enables efficient queries - Clear semantics: Unambiguous distinction between deposit/withdrawal/settlement operations
- Validation enforcement: Cannot accidentally use a deposit clearing account for withdrawals
- Backward compatible: Existing clearing accounts can be migrated with appropriate purpose assignment
- Future-proof: Additional purposes (if needed) can be added as new enum values
- Additional field complexity: One more field to manage in the proto and database
- Migration required: Existing clearing accounts need purpose backfilled via database migration
- Coupled constraint: Must maintain consistency between account_type and clearing_purpose
- Good, because provides compile-time type safety
- Good, because supports efficient database filtering via indexed column
- Good, because enum values are self-documenting
- Good, because validation rules are clear and enforceable
- Good, because backward compatible with null-to-enum migration
- Bad, because requires database migration for existing data
- Bad, because adds cross-field validation complexity
A generic subtype string field that could hold any value.
- Good, because more flexible (any string value)
- Good, because simpler schema (no enum)
- Bad, because no compile-time type safety
- Bad, because prone to typos ("DEPSOIT" vs "DEPOSIT")
- Bad, because difficult to validate at database level
- Bad, because unclear what values are valid
A many-to-many tags system where accounts can have multiple labels.
- Good, because highly flexible
- Good, because supports multiple tags per account
- Bad, because overly complex for this use case (only need one purpose per account)
- Bad, because difficult to query efficiently
- Bad, because no type safety
- Bad, because tags proliferation without governance
Create separate tables: deposit_clearing_accounts, withdrawal_clearing_accounts, etc.
- Good, because complete type separation
- Good, because no validation needed (table choice implies purpose)
- Bad, because data duplication across tables
- Bad, because complex queries spanning purposes
- Bad, because difficult to add new purposes
- Bad, because breaks Internal Account service domain model
- ADR-0024: Internal Bank Account Service
- Internal Account README
- Proto Definitions
- BIAN Internal Account Service Domain
The provisioning system creates purpose-specific clearing accounts per instrument when a tenant is initialized:
| Account Code | Purpose | Description |
|---|---|---|
CLR-{INSTRUMENT}-DEPOSIT |
DEPOSIT | For deposit operations |
CLR-{INSTRUMENT}-WITHDRAW |
WITHDRAWAL | For withdrawal operations |
CLR-{INSTRUMENT}-SETTLE |
SETTLEMENT | For settlement operations |
For existing clearing accounts without a purpose:
- Analyze account_code patterns to infer purpose (DEPOSIT, WITHDRAW, SETTLE keywords)
- Default remaining CLEARING accounts to GENERAL purpose
- Run database migration to backfill clearing_purpose column
- Enable constraint enforcement after migration completes
If clearing accounts need multiple purposes simultaneously (e.g., an account that handles both deposits AND settlements), the current design would need to evolve. Options include:
- Add MULTI_PURPOSE enum value
- Migrate to a tags-based approach
- Create composite clearing accounts
Current analysis suggests single-purpose accounts are sufficient for Meridian's operational model.
Reconsider this design if:
- More than 2 clearing accounts per instrument-purpose combination are needed
- Clearing accounts need to serve multiple purposes
- Purpose categories exceed 10+ distinct values