Skip to content

Commit 45647e6

Browse files
authored
feat: rename correspondent to counterparty in internal-account service (#1212)
* feat: rename correspondent terminology to counterparty in internal-account service Renames all 'correspondent' terminology to 'counterparty' throughout the internal-account service to use generic, asset-agnostic language: - Proto: CorrespondentBankDetails -> CounterpartyDetails, CorrespondentType -> CounterpartyType, field names updated (bank_id -> counterparty_id, etc.), swift_code moved to attributes map<string,string> - Domain: correspondent.go -> counterparty.go, CorrespondentDetails -> CounterpartyDetails with updated accessor methods - Errors: ErrCorrespondentRequired/NotAllowed -> ErrCounterpartyRequired/NotAllowed - DB: Atlas migration to rename correspondent_bank_id/name/external_ref -> counterparty_id/name/external_ref, drop swift_code column - All service, persistence, client, test, and doc files updated accordingly Intentional breaking change: part of the asset-agnostic accounts initiative (GitHub Issue asset-agnostic-accounts, Task 7) to remove bank-specific terminology. * fix: capture error return from NewCounterpartyDetails in integration test golangci-lint checks integration-tagged files that are excluded from normal compilation, catching that NewCounterpartyDetails returns (T, error) not T. Also fix gofumpt formatting in mappers_test.go and repository.go. * fix: apply gofumpt formatting to internal-account service files Fix alignment whitespace issues flagged by golangci-lint gofmt checker in domain/internal_account.go, e2e tests, and account_lifecycle example. * fix: address CodeRabbit review comments on counterparty rename - Fix VARCHAR widths in test schemas to match migration (counterparty_id VARCHAR(50), counterparty_external_ref VARCHAR(100)) in repository_test.go - Add counterparty_attributes mapping in starlark.go addCounterpartyDetails so metadata (e.g. swift_code) is not silently dropped; use sentinel errors to satisfy err113 linter - Fix e2e test 'CLEARING account rejects counterparty details' to actually send CounterpartyDetails and assert the error response - Fix mismatched product type codes in counterparty_account.go example (NOSTRO_EUR/VOSTRO_JPY instead of NOSTRO_USD/VOSTRO_USD) * fix: guard CounterpartyDetails nil dereferences in example file Use GetCounterpartyDetails() accessor with nil checks before accessing fields, preventing panics on unexpected server responses. Applies to all log sites in counterparty_account.go. --------- Co-authored-by: Ben Coombs <bjcoombs@users.noreply.github.com>
1 parent 1bcbbc5 commit 45647e6

41 files changed

Lines changed: 1541 additions & 1588 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

api/proto/meridian/internal_account/v1/internal_account.proto

Lines changed: 27 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -130,49 +130,44 @@ enum ControlAction {
130130
CONTROL_ACTION_CLOSE = 3;
131131
}
132132

133-
// CorrespondentType distinguishes between nostro and vostro relationships.
134-
enum CorrespondentType {
135-
// CORRESPONDENT_TYPE_UNSPECIFIED is the default value and should not be used.
136-
CORRESPONDENT_TYPE_UNSPECIFIED = 0;
137-
// CORRESPONDENT_TYPE_NOSTRO means our account at the correspondent bank.
138-
CORRESPONDENT_TYPE_NOSTRO = 1;
139-
// CORRESPONDENT_TYPE_VOSTRO means their account at our bank.
140-
CORRESPONDENT_TYPE_VOSTRO = 2;
133+
// CounterpartyType distinguishes between nostro and vostro relationships.
134+
enum CounterpartyType {
135+
// COUNTERPARTY_TYPE_UNSPECIFIED is the default value and should not be used.
136+
COUNTERPARTY_TYPE_UNSPECIFIED = 0;
137+
// COUNTERPARTY_TYPE_NOSTRO means our account at the counterparty.
138+
COUNTERPARTY_TYPE_NOSTRO = 1;
139+
// COUNTERPARTY_TYPE_VOSTRO means their account at our institution.
140+
COUNTERPARTY_TYPE_VOSTRO = 2;
141141
}
142142

143-
// CorrespondentBankDetails contains information about the correspondent bank
143+
// CounterpartyDetails contains information about the counterparty
144144
// for NOSTRO and VOSTRO account types.
145-
message CorrespondentBankDetails {
146-
// bank_id is the unique identifier for the correspondent bank.
147-
string bank_id = 1 [(buf.validate.field).string = {
145+
message CounterpartyDetails {
146+
// counterparty_id is the unique identifier for the counterparty.
147+
string counterparty_id = 1 [(buf.validate.field).string = {
148148
min_len: 1
149149
max_len: 100
150150
pattern: "^[a-zA-Z0-9_-]+$"
151151
}];
152152

153-
// bank_name is the display name of the correspondent bank.
154-
string bank_name = 2 [(buf.validate.field).string = {
153+
// counterparty_name is the display name of the counterparty.
154+
string counterparty_name = 2 [(buf.validate.field).string = {
155155
min_len: 1
156156
max_len: 255
157157
}];
158158

159-
// external_account_ref is the account reference at the correspondent bank.
160-
string external_account_ref = 3 [(buf.validate.field).string = {
159+
// counterparty_external_ref is the account reference at the counterparty.
160+
string counterparty_external_ref = 3 [(buf.validate.field).string = {
161161
min_len: 1
162162
max_len: 255
163163
}];
164164

165-
// swift_code is the SWIFT/BIC code of the correspondent bank (optional).
166-
// Pattern validates BIC8 (8 chars) or BIC11 (11 chars) format.
167-
// Empty string is allowed for correspondent banks without SWIFT codes
168-
// (e.g., domestic correspondents, emerging market banks).
169-
string swift_code = 4 [(buf.validate.field).string = {
170-
max_len: 11
171-
pattern: "^([A-Z]{6}[A-Z0-9]{2}([A-Z0-9]{3})?)?$"
172-
}];
165+
// attributes provides extensibility for counterparty-specific metadata.
166+
// Product-type-specific fields (e.g., swift_code for banking) should be stored here.
167+
map<string, string> attributes = 4;
173168

174-
// correspondent_type indicates whether this is a NOSTRO or VOSTRO relationship.
175-
CorrespondentType correspondent_type = 5 [(buf.validate.field).enum = {
169+
// counterparty_type indicates whether this is a NOSTRO or VOSTRO relationship.
170+
CounterpartyType counterparty_type = 5 [(buf.validate.field).enum = {
176171
defined_only: true
177172
not_in: [0]
178173
}];
@@ -220,10 +215,10 @@ message InternalAccountFacility {
220215
pattern: "^[A-Z][A-Z0-9_]*$"
221216
}];
222217

223-
// correspondent_details contains correspondent bank information for NOSTRO/VOSTRO accounts.
218+
// counterparty_details contains counterparty information for NOSTRO/VOSTRO accounts.
224219
// Required when account_type is NOSTRO or VOSTRO (enforced at service layer since
225220
// proto validation cannot express cross-field conditional constraints).
226-
CorrespondentBankDetails correspondent_details = 7;
221+
CounterpartyDetails counterparty_details = 7;
227222

228223
// description provides additional context about this account's purpose.
229224
string description = 8 [(buf.validate.field).string.max_len = 1000];
@@ -323,9 +318,9 @@ message InitiateInternalAccountRequest {
323318
pattern: "^[A-Z][A-Z0-9_]*$"
324319
}];
325320

326-
// correspondent_details is required for NOSTRO/VOSTRO account types.
321+
// counterparty_details is required for NOSTRO/VOSTRO account types.
327322
// Cross-field validation enforced at service layer.
328-
CorrespondentBankDetails correspondent_details = 5;
323+
CounterpartyDetails counterparty_details = 5;
329324

330325
// description provides additional context about this account's purpose.
331326
string description = 6 [(buf.validate.field).string.max_len = 1000];
@@ -394,8 +389,8 @@ message UpdateInternalAccountRequest {
394389
// description is the updated description (optional).
395390
string description = 3 [(buf.validate.field).string.max_len = 1000];
396391

397-
// correspondent_details updates correspondent information (for NOSTRO/VOSTRO accounts).
398-
CorrespondentBankDetails correspondent_details = 4;
392+
// counterparty_details updates counterparty information (for NOSTRO/VOSTRO accounts).
393+
CounterpartyDetails counterparty_details = 4;
399394

400395
// expected_version for optimistic locking. If provided, update fails if current version differs.
401396
int32 expected_version = 5 [(buf.validate.field).int32 = {gte: 0}];

services/internal-account/README.md

Lines changed: 41 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ triggers:
1111
- Balance queries for internal accounts
1212
instructions: |
1313
InternalAccount manages non-customer accounts used for internal accounting purposes:
14-
- Counterparty accounts (nostro/vostro for correspondent banking)
14+
- Counterparty accounts (nostro/vostro for counterparty banking)
1515
- Operational accounts (clearing, suspense, holding, revenue, expense)
1616
- Multi-asset support (fiat, energy, carbon credits, compute hours)
1717
@@ -42,11 +42,11 @@ BIAN-compliant internal account registry microservice for managing counterparty
4242
## Purpose
4343

4444
The Internal Account service manages accounts that are not customer-facing but are essential
45-
for internal accounting and correspondent banking operations:
45+
for internal accounting and counterparty banking operations:
4646

4747
- **Clearing Accounts**: Settlement and clearing operations during transaction processing
48-
- **Nostro Accounts**: "Our account at your bank" - accounts held at correspondent banks
49-
- **Vostro Accounts**: "Your account at our bank" - accounts held by correspondent banks at us
48+
- **Nostro Accounts**: "Our account at your bank" - accounts held at counterparty banks
49+
- **Vostro Accounts**: "Your account at our bank" - accounts held by counterparty banks at us
5050
- **Holding Accounts**: Temporary holding of funds during multi-step processes
5151
- **Suspense Accounts**: Unidentified or pending transactions awaiting resolution
5252
- **Revenue Accounts**: Income and revenue tracking for GL integration
@@ -78,12 +78,11 @@ req := &iba.InitiateInternalAccountRequest{
7878
Name: "USD Nostro at HSBC London",
7979
AccountType: iba.INTERNAL_ACCOUNT_TYPE_NOSTRO,
8080
InstrumentCode: "USD",
81-
CorrespondentDetails: &iba.CorrespondentBankDetails{
82-
BankId: "hsbc-london",
83-
BankName: "HSBC London",
84-
ExternalAccountRef: "GB12HSBC12345678901234",
85-
SwiftCode: "HSBCGB2L",
86-
CorrespondentType: iba.CORRESPONDENT_TYPE_NOSTRO,
81+
CounterpartyDetails: &iba.CounterpartyDetails{
82+
CounterpartyId: "hsbc-london",
83+
CounterpartyName: "HSBC London",
84+
CounterpartyExternalRef: "GB12HSBC12345678901234",
85+
CounterpartyType: iba.COUNTERPARTY_TYPE_NOSTRO,
8786
},
8887
Description: "Primary USD clearing account at HSBC London",
8988
IdempotencyKey: &common.IdempotencyKey{Key: "create-nostro-001"},
@@ -197,19 +196,19 @@ classDiagram
197196
+ClearingPurpose ClearingPurpose
198197
+InternalAccountStatus AccountStatus
199198
+string InstrumentCode
200-
+CorrespondentBankDetails CorrespondentDetails
199+
+CounterpartyDetails CounterpartyDetails
201200
+string Description
202201
+int32 Version
203202
+Timestamp CreatedAt
204203
+Timestamp UpdatedAt
205204
}
206205
207-
class CorrespondentBankDetails {
208-
+string BankID
209-
+string BankName
210-
+string ExternalAccountRef
211-
+string SwiftCode
212-
+CorrespondentType CorrespondentType
206+
class CounterpartyDetails {
207+
+string CounterpartyID
208+
+string CounterpartyName
209+
+string CounterpartyExternalRef
210+
+map Attributes
211+
+CounterpartyType CounterpartyType
213212
}
214213
215214
class InternalAccountType {
@@ -247,7 +246,7 @@ classDiagram
247246
CLOSE
248247
}
249248
250-
class CorrespondentType {
249+
class CounterpartyType {
251250
<<enumeration>>
252251
NOSTRO
253252
VOSTRO
@@ -256,24 +255,24 @@ classDiagram
256255
InternalAccountFacility --> InternalAccountType
257256
InternalAccountFacility --> ClearingPurpose
258257
InternalAccountFacility --> InternalAccountStatus
259-
InternalAccountFacility --> CorrespondentBankDetails
260-
CorrespondentBankDetails --> CorrespondentType
258+
InternalAccountFacility --> CounterpartyDetails
259+
CounterpartyDetails --> CounterpartyType
261260
```
262261

263262
**Field Notes:**
264263

265264
- `AccountID`: System-generated KSUID (e.g., `2rPxMVkj3tNmqPwT5Wk8Lc4M9xZ`)
266265
- `AccountCode`: Business-friendly code (e.g., `NOSTRO-USD-HSBC`, `CLR-001`)
267266
- `InstrumentCode`: References instrument from Reference Data service (e.g., `USD`, `KWH`, `GPU_HOUR`)
268-
- `CorrespondentDetails`: Required for NOSTRO/VOSTRO accounts, null for others
267+
- `CounterpartyDetails`: Required for NOSTRO/VOSTRO accounts, null for others
269268

270269
## Account Types
271270

272271
| Type | Description | Typical Use |
273272
|------|-------------|-------------|
274273
| `CLEARING` | Settlement and clearing operations | Interbank settlement, payment clearing |
275-
| `NOSTRO` | Our account at another bank | Foreign currency holdings, correspondent banking |
276-
| `VOSTRO` | Their account at our bank | Correspondent accounts for partner banks |
274+
| `NOSTRO` | Our account at another bank | Foreign currency holdings, counterparty banking |
275+
| `VOSTRO` | Their account at our bank | Counterparty accounts for partner banks |
277276
| `HOLDING` | Temporary fund holding | Escrow, pending transfers, batch processing |
278277
| `SUSPENSE` | Unmatched/pending transactions | Reconciliation, error correction |
279278
| `REVENUE` | Income tracking | Fee collection, interest income |
@@ -505,7 +504,7 @@ sequenceDiagram
505504
| `account_type` | Must not be UNSPECIFIED | "account_type must be specified" |
506505
| `instrument_code` | Pattern: `^[A-Z][A-Z0-9_]*$`, max 32 chars | "instrument_code must match pattern" |
507506
| `control_action.reason` | Min 10 chars for audit completeness | "reason must be at least 10 characters" |
508-
| `correspondent_details` | Required for NOSTRO/VOSTRO types | "correspondent_details required for nostro/vostro" |
507+
| `counterparty_details` | Required for NOSTRO/VOSTRO types | "counterparty details required for nostro/vostro" |
509508

510509
## Database Schema
511510

@@ -526,17 +525,14 @@ erDiagram
526525
timestamp updated_at
527526
}
528527
529-
correspondent_bank_details {
530-
uuid id PK
531-
uuid internal_account_id FK
532-
string bank_id
533-
string bank_name
534-
string external_account_ref
535-
string swift_code "nullable"
536-
string correspondent_type "NOSTRO|VOSTRO"
528+
counterparty_details {
529+
string counterparty_id "nullable"
530+
string counterparty_name "nullable"
531+
string counterparty_external_ref "nullable"
532+
string counterparty_type "NOSTRO|VOSTRO"
537533
}
538534
539-
internal_account ||--o| correspondent_bank_details : "has (nostro/vostro only)"
535+
internal_account ||--o| counterparty_details : "has (nostro/vostro only)"
540536
```
541537

542538
## Service Dependencies
@@ -639,22 +635,21 @@ client.ControlInternalAccount(ctx, &iba.ControlInternalAccountRequest{
639635
})
640636
```
641637

642-
### Correspondent Banking
638+
### Counterparty Banking
643639

644-
Nostro and vostro accounts require correspondent bank details:
640+
Nostro and vostro accounts require counterparty details:
645641

646642
```go
647643
// Nostro: Our account at their bank
648644
nostro := &iba.InitiateInternalAccountRequest{
649645
AccountCode: "NOSTRO-EUR-DEUTSCHE",
650646
AccountType: iba.INTERNAL_ACCOUNT_TYPE_NOSTRO,
651647
InstrumentCode: "EUR",
652-
CorrespondentDetails: &iba.CorrespondentBankDetails{
653-
BankId: "deutsche-frankfurt",
654-
BankName: "Deutsche Bank Frankfurt",
655-
ExternalAccountRef: "DE89370400440532013000",
656-
SwiftCode: "DEUTDEFF",
657-
CorrespondentType: iba.CORRESPONDENT_TYPE_NOSTRO,
648+
CounterpartyDetails: &iba.CounterpartyDetails{
649+
CounterpartyId: "deutsche-frankfurt",
650+
CounterpartyName: "Deutsche Bank Frankfurt",
651+
CounterpartyExternalRef: "DE89370400440532013000",
652+
CounterpartyType: iba.COUNTERPARTY_TYPE_NOSTRO,
658653
},
659654
}
660655

@@ -663,12 +658,11 @@ vostro := &iba.InitiateInternalAccountRequest{
663658
AccountCode: "VOSTRO-JPY-MUFG",
664659
AccountType: iba.INTERNAL_ACCOUNT_TYPE_VOSTRO,
665660
InstrumentCode: "JPY",
666-
CorrespondentDetails: &iba.CorrespondentBankDetails{
667-
BankId: "mufg-tokyo",
668-
BankName: "MUFG Bank Tokyo",
669-
ExternalAccountRef: "VOSTRO-MUFG-001",
670-
SwiftCode: "BOTKJPJT",
671-
CorrespondentType: iba.CORRESPONDENT_TYPE_VOSTRO,
661+
CounterpartyDetails: &iba.CounterpartyDetails{
662+
CounterpartyId: "mufg-tokyo",
663+
CounterpartyName: "MUFG Bank Tokyo",
664+
CounterpartyExternalRef: "VOSTRO-MUFG-001",
665+
CounterpartyType: iba.COUNTERPARTY_TYPE_VOSTRO,
672666
},
673667
}
674668
```

services/internal-account/adapters/persistence/account_entity.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -51,10 +51,10 @@ type InternalAccountEntity struct {
5151
ProductTypeCode *string `gorm:"column:product_type_code;type:varchar(100)"`
5252
ProductTypeVersion *int `gorm:"column:product_type_version;type:integer"`
5353

54-
// Correspondent bank details (nullable for non-nostro/vostro accounts)
55-
CorrespondentBankID *string `gorm:"column:correspondent_bank_id;type:varchar(50)"`
56-
CorrespondentBankName *string `gorm:"column:correspondent_bank_name;type:varchar(255)"`
57-
CorrespondentExternalRef *string `gorm:"column:correspondent_external_ref;type:varchar(100)"`
54+
// Counterparty details (nullable for non-nostro/vostro accounts)
55+
CounterpartyID *string `gorm:"column:counterparty_id;type:varchar(100)"`
56+
CounterpartyName *string `gorm:"column:counterparty_name;type:varchar(255)"`
57+
CounterpartyExternalRef *string `gorm:"column:counterparty_external_ref;type:varchar(255)"`
5858

5959
// Extensible attributes
6060
Attributes AttributesJSON `gorm:"column:attributes;type:jsonb;not null;default:'{}'"`

services/internal-account/adapters/persistence/doc.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@
4242
//
4343
// - UUID primary key
4444
// - Tenant-scoped unique constraint on account_code
45-
// - JSONB storage for correspondent details
45+
// - JSONB storage for counterparty details
4646
// - Timestamp tracking (created_at, updated_at)
4747
// - Version column for optimistic locking
4848
//

0 commit comments

Comments
 (0)