# Repository Ownership Map and Data Model Gap Analysis

Date: 2026-03-30
Status: approved
Reference: docs/multi-wallet/deliverables/capability-matrix.md

## 1. Capability-to-App Ownership Map

### 1.1 Wallet Product Lifecycle (WP-01 through WP-10)

| Capability | Primary App | Supporting Apps | Reason |
|---|---|---|---|
| WP-01 Create wallet product | wallet_accounts | wallet_database | Account lifecycle is wallet_accounts domain. OpenWalletAccount already here. |
| WP-02 List wallet products | wallet_accounts | wallet_database | Query within account domain. |
| WP-03 Get wallet product details | wallet_accounts | wallet_ledger (balance aggregation) | Cross-app via DI (ledger_balance_fn). |
| WP-04 Update metadata | wallet_accounts | wallet_database | Mutation within account domain. |
| WP-05 Freeze (cascade) | wallet_accounts | wallet_database | Existing FreezeWalletAccount pattern extended. |
| WP-06 Unfreeze (cascade) | wallet_accounts | wallet_database | Existing UnfreezeWalletAccount pattern extended. |
| WP-07 Suspend | wallet_accounts | wallet_database | Existing SuspendAccount pattern extended. |
| WP-08 Resume | wallet_accounts | wallet_database | Existing ResumeAccount pattern extended. |
| WP-09 Close | wallet_accounts | wallet_ledger (balance check) | Balance check via DI. |
| WP-10 Same-type per user | wallet_accounts | wallet_database | No uniqueness constraint on (user_id, product_type). |

### 1.2 Sub-wallet Lifecycle (SW-01 through SW-10)

| Capability | Primary App | Supporting Apps | Reason |
|---|---|---|---|
| SW-01 Create sub-wallet | wallet_accounts | wallet_database | Sub-wallet is child of wallet product in account domain. |
| SW-02 List sub-wallets | wallet_accounts | wallet_ledger (balances) | Balance via DI. |
| SW-03 Get sub-wallet details | wallet_accounts | wallet_ledger | Balance via DI. |
| SW-04 Update metadata | wallet_accounts | wallet_database | Mutation within account domain. |
| SW-05 Freeze sub-wallet | wallet_accounts | wallet_database | Independent freeze within account domain. |
| SW-06 Unfreeze sub-wallet | wallet_accounts | wallet_database | Checks parent status. |
| SW-07 Close sub-wallet | wallet_accounts | wallet_ledger (balance check) | Balance check via DI. |
| SW-08 Transfer within product | wallet_transfers | wallet_accounts, wallet_ledger | Transfer orchestration is wallet_transfers domain. Account resolution via DI. |
| SW-09 Transfer across products | wallet_transfers | wallet_accounts, wallet_ledger, wallet_integrations (FX) | May need FX if different currencies. |
| SW-10 Transfer cross-user | wallet_transfers | wallet_accounts, wallet_ledger | Uses existing transfer state machine. |

### 1.3 Multi-Currency (MC-01 through MC-05)

| Capability | Primary App | Supporting Apps | Reason |
|---|---|---|---|
| MC-01 Set primary currency | wallet_accounts | wallet_database | Currency config is part of wallet product domain. |
| MC-02 Add display currency | wallet_accounts | wallet_integrations (FX rates) | Rate fetching via DI. |
| MC-03 Update display rate | wallet_accounts | wallet_integrations | Rate source integration. |
| MC-04 Disable/enable | wallet_accounts | wallet_database | Toggle within account domain. |
| MC-05 One-primary invariant | wallet_accounts | wallet_database | Store + persistence constraint. |

### 1.4 Ledger Integration (LT-01 through LT-08)

| Capability | Primary App | Supporting Apps | Reason |
|---|---|---|---|
| LT-01 Credit to sub-wallet | wallet_ledger | wallet_accounts | Ledger is posting engine. Sub-wallet ref added to entry. |
| LT-02 Debit from sub-wallet | wallet_ledger | wallet_accounts | Per-sub-wallet balance check. |
| LT-03 Transfer posting | wallet_ledger | wallet_transfers | Linked entries for transfer legs. |
| LT-04 Freeze-state guard | wallet_ledger | wallet_accounts (status check via DI) | Posting pipeline checks sub-wallet status. |
| LT-05 Balance by sub-wallet | wallet_ledger | — | New query in LedgerStore. |
| LT-06 Aggregate by product | wallet_ledger | wallet_accounts (sub-wallet list) | Sum across sub-wallets. |
| LT-07 FX conversion records | wallet_ledger | wallet_integrations | FX rate lookup. |
| LT-08 Idempotency | wallet_state | wallet_ledger | Existing idempotency contract. |

### 1.5 API/Channel (API-01 through API-07)

| Capability | Primary App | Supporting Apps | Reason |
|---|---|---|---|
| API-01 Wallet product endpoints | wallet_web | wallet_accounts | API layer delegates to domain. |
| API-02 Sub-wallet endpoints | wallet_web | wallet_accounts | API layer delegates to domain. |
| API-03 Transfer endpoint | wallet_web | wallet_transfers | API layer delegates to transfer domain. |
| API-04 Currency config endpoints | wallet_web | wallet_accounts | API layer. |
| API-05 Customer LiveView | wallet_web | wallet_accounts | UI within wallet_web. |
| API-06 Admin LiveView | wallet_web | wallet_accounts | UI within wallet_web. |
| API-07 Backward compat | wallet_web | wallet_accounts | Resolver in API layer. |

### 1.6 Eventing/Compliance (EC-01 through EC-06)

| Capability | Primary App | Supporting Apps | Reason |
|---|---|---|---|
| EC-01 Product lifecycle events | wallet_events + wallet_accounts | — | Events defined in wallet_accounts, behaviour from wallet_events. |
| EC-02 Sub-wallet lifecycle events | wallet_events + wallet_accounts | — | Same pattern. |
| EC-03 Transfer events | wallet_events + wallet_transfers | — | Transfer events in wallet_transfers. |
| EC-04 Audit events | wallet_observability | wallet_accounts, wallet_transfers | AuditEvent.build in each command. |
| EC-05 Telemetry spans | wallet_observability | wallet_accounts, wallet_transfers | :telemetry in each command. |
| EC-06 Compliance controls | wallet_compliance | wallet_accounts | ControlCatalog checks. |

## 2. Data Model Gap Analysis

### 2.1 New Tables Required

| Table | App | Description | Migration Phase |
|---|---|---|---|
| `wallet_products` | wallet_database | Wallet product aggregate | 3 |
| `sub_wallets` | wallet_database | Sub-wallet entities | 3 |
| `currency_configs` | wallet_database | Multi-currency settings | 3 |

### 2.2 Existing Table Modifications

| Table | Change | Compatibility | Migration Phase | Risk |
|---|---|---|---|---|
| `accounts` | Add `wallet_product_id` column (nullable, FK → wallet_products) | **Compatible** — nullable column addition, no lock | 3 | Low |
| `ledger_entries` | Add `sub_wallet_id` column (nullable) | **Compatible** — nullable column addition | 3 | Low |
| `ledger_balances` | Add `sub_wallet_id` column (nullable), add composite index (sub_wallet_id, currency) | **Compatible** — nullable + index | 3 | Low |
| `wallet_transfers` | Add `from_sub_wallet_id`, `to_sub_wallet_id` columns (nullable) | **Compatible** — nullable columns | 3 | Low |

### 2.3 Detailed Schema Gap Matrix

| Aspect | Current State | Target State | Gap Type | Resolution |
|---|---|---|---|---|
| **Account hierarchy** | Flat: Account has no parent | WalletProduct → Account (1:1), WalletProduct → SubWallet (1:N) | **New concept** | Add WalletProduct struct/store/schema. Add `wallet_product_id` FK to Account. |
| **Account per user** | One or more Accounts per user, each independent | Accounts linked to WalletProducts; multiple products per user | **Partial** | Existing multi-account works. Add WalletProduct grouping layer. |
| **Product type** | No `product_type` or `account_type` field | `product_type` enum on WalletProduct | **New field** | New struct + ETS store + table. No change to Account struct. |
| **Sub-wallet** | Does not exist | SubWallet struct with parent_id, purpose, balance tracking | **New concept** | New struct + store + table. Ledger entries tagged with sub_wallet_id. |
| **Currency model** | `Account.currency` (single string) | WalletProduct.primary_currency + CurrencyConfig[] | **Partial** | Account.currency → maps to primary. New CurrencyConfig table for display currencies. |
| **Balance tracking** | LedgerStore by (account_id, currency) | LedgerStore by (sub_wallet_id, currency) for sub-wallet; (account_id, currency) for aggregate | **Extension** | Add sub_wallet_id dimension to LedgerStore. Existing (account_id, currency) kept for aggregate. |
| **Transfer source/target** | from_account_id / to_account_id | from_sub_wallet_id / to_sub_wallet_id (+ account resolution) | **Extension** | Add sub_wallet reference fields. Existing account fields remain for backward compat. |
| **Freeze cascade** | Account only (no children) | WalletProduct freeze cascades to SubWallets | **New behavior** | WalletProduct freeze command iterates sub-wallets and freezes each. Track `frozen_by_parent` flag on sub-wallet. |
| **Close guard** | Account close: no balance check (logic gap) | WalletProduct close: requires zero balance across all sub-wallets | **New guard** | Balance check via DI function before state transition. |
| **Default sub-wallet** | Not applicable | Auto-created on WalletProduct creation | **New behavior** | CreateWalletProduct command creates default SubWallet atomically. |
| **Backward compat** | Endpoints use account_id directly | New endpoints use wallet_product_id/sub_wallet_id; old endpoints resolve via default | **New resolver** | WalletProductResolver: account_id → default wallet_product → default sub_wallet. |
| **ETS indexes** | AccountStore: no user_id index | WalletProductStore: needs user_id + product_type indexes; SubWalletStore: needs wallet_product_id index | **New stores** | New ETS GenServer stores with proper secondary indexes. |

### 2.4 Ecto Schema Impact

| Schema | File | Change |
|---|---|---|
| Account (existing) | `apps/wallet_database/lib/wallet_database/schemas/accounts/account.ex` | Add `belongs_to :wallet_product` + `wallet_product_id` field |
| WalletProduct (new) | `apps/wallet_database/lib/wallet_database/schemas/accounts/wallet_product.ex` | Full new schema |
| SubWallet (new) | `apps/wallet_database/lib/wallet_database/schemas/accounts/sub_wallet.ex` | Full new schema |
| CurrencyConfig (new) | `apps/wallet_database/lib/wallet_database/schemas/accounts/currency_config.ex` | Full new schema |
| Entry (existing) | `apps/wallet_database/lib/wallet_database/schemas/ledger/entry.ex` | Add `sub_wallet_id` field |
| Balance (existing) | `apps/wallet_database/lib/wallet_database/schemas/ledger/balance.ex` | Add `sub_wallet_id` field |
| Transfer (existing) | `apps/wallet_database/lib/wallet_database/schemas/transfers/transfer.ex` | Add `from_sub_wallet_id`, `to_sub_wallet_id` fields |

### 2.5 ETS Store Impact

| Store | Change |
|---|---|
| AccountStore (existing) | Add `wallet_product_id` to stored data. No API change needed. |
| WalletProductStore (new) | New GenServer. Tables: @table, @user_idx, @product_type_idx. API: store/get/update/list_by_user/list_by_user_and_type/reset. |
| SubWalletStore (new) | New GenServer. Tables: @table, @product_idx, @user_idx. API: store/get/update/list_by_product/list_by_user/reset. |
| CurrencyConfigStore (new) | New GenServer. Tables: @table, @product_idx. API: store/get/update/list_by_product/reset. |
| LedgerStore (existing) | Add sub_wallet balance tracking dimension. New: get_sub_wallet_balance/2, get_entries_by_sub_wallet/1. |

### 2.6 Known Database Schema Bugs (Pre-existing)

| Bug | Location | Impact |
|---|---|---|
| `close_reason` and `closed_by` exist in Account struct but not in DB migration/schema | Account struct vs migration | Close reason data is lost on persistence. Should fix in Phase 3 migration. |
| Event payload key inconsistency: older events use `payload`, newer use `data` | Various event modules | No data loss but inconsistent. Should standardize in Phase 7. |

## 3. Cross-App Dependency Map

```
wallet_web ──────→ wallet_accounts (wallet product + sub-wallet commands)
    │                    │
    │                    ├──→ wallet_database (persistence)
    │                    │
    │                    └──→ wallet_events (DomainEvent behaviour)
    │
    ├──────→ wallet_transfers (sub-wallet transfer orchestration)
    │              │
    │              ├──→ wallet_accounts (sub-wallet resolution via DI)
    │              ├──→ wallet_ledger (posting)
    │              └──→ wallet_integrations (FX, if cross-currency)
    │
    └──────→ wallet_ledger (balance queries)
                   │
                   └──→ wallet_state (idempotency)
```

All cross-app calls use the established DI pattern (injected function args) to avoid compile-time coupling.
