> ## Documentation Index
> Fetch the complete documentation index at: https://docs.vultlocal.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Architecture

> Internal architecture of the Wallet-Core financial engine

# Wallet-Core Architecture

This document covers the internal architecture of Wallet-Core, including the service layer, ledger, and data access patterns.

## Layer Overview

```mermaid theme={null}
graph TB
    subgraph "Entry"
        GRPC[gRPC Server]
    end

    subgraph "Service Layer"
        SS[Subscriber Service]
        CS[Card Service]
        AS[Agent Service]
        WS[Wallet Service]
        CPS[Compliance Service]
        POS[POS Service]
        PS[Processor Service]
        FS[Fee Service]
        VS[VULT Service]
    end

    subgraph "Core"
        LED[Ledger]
        TV[Transaction Validator]
        FC[Fee Calculator]
    end

    subgraph "Data Access"
        SR[Subscriber Repo]
        CR[Card Repo]
        AR[Account Repo]
        TR[Transaction Repo]
        ALR[Audit Log Repo]
    end

    subgraph "Storage"
        DB[(PostgreSQL)]
    end

    GRPC --> SS & CS & AS & WS & CPS & POS & PS & FS
    WS --> LED
    AS --> LED
    POS --> LED
    LED --> TV & FC
    LED --> AR & TR & ALR
    SS --> SR
    CS --> CR
    VS --> TR
    SR & CR & AR & TR & ALR --> DB
```

## Service Layer

### Services Overview

| Service             | File                    | Responsibility                         |
| ------------------- | ----------------------- | -------------------------------------- |
| `SubscriberService` | `subscriber_service.go` | User registration, KYC, PIN management |
| `CardService`       | `card_service.go`       | Card linking, blocking, child cards    |
| `AgentService`      | `agent_service.go`      | Agent registration, float, cashin      |
| `WalletService`     | `wallet_service.go`     | Payments, balances, transfers          |
| `ComplianceService` | `compliance_service.go` | Monitoring rules, alerts               |
| `POSService`        | `pos_service.go`        | Terminal payments, verification        |
| `ProcessorService`  | `processor_service.go`  | External processor integration         |
| `FeeService`        | `fee_calculator.go`     | Fee calculation                        |
| `AuditService`      | `audit_service.go`      | Audit log management                   |
| `APIKeyService`     | `apikey_service.go`     | API key CRUD                           |
| `PEPAccessService`  | `pep_access_service.go` | Privileged access flows                |

### Service Pattern

```go theme={null}
// Standard service structure
type SubscriberService struct {
    repo   *repository.SubscriberRepository
    ledger *ledger.Service
    audit  *AuditService
}

func NewSubscriberService(
    repo *repository.SubscriberRepository,
    ledger *ledger.Service,
    audit *AuditService,
) *SubscriberService {
    return &SubscriberService{
        repo:   repo,
        ledger: ledger,
        audit:  audit,
    }
}

func (s *SubscriberService) Register(ctx context.Context, req *RegisterRequest) (*Subscriber, error) {
    // Validate
    if err := validateRequest(req); err != nil {
        return nil, err
    }
    
    // Create subscriber
    subscriber, err := s.repo.Create(ctx, req)
    if err != nil {
        return nil, err
    }
    
    // Create account
    _, err = s.ledger.CreateAccount(ctx, subscriber.ID, "SLE")
    if err != nil {
        // Rollback or handle
    }
    
    // Audit
    s.audit.Log(ctx, "subscriber_registered", subscriber.ID)
    
    return subscriber, nil
}
```

## Ledger

The ledger is the core transaction engine, handling all financial operations.

### Core Operations

| Operation             | Description                          |
| --------------------- | ------------------------------------ |
| `ExecutePayment`      | Atomic payment between accounts      |
| `GetBalance`          | Retrieve account balance             |
| `CreateAccount`       | Create new account for user+currency |
| `GetTransaction`      | Retrieve transaction by ID           |
| `ListTransactions`    | Query transaction history            |
| `GetAccountStatement` | Generate account statements          |

### Payment Execution

```go theme={null}
// ledger/service.go
func (l *Service) ExecutePayment(ctx context.Context, req *PaymentRequest) (*Transaction, error) {
    // Check idempotency
    existing, err := l.txRepo.GetByRequestID(ctx, req.RequestID)
    if err == nil && existing != nil {
        return existing, nil  // Return existing for duplicate
    }
    
    // Start transaction
    tx, err := l.db.BeginTx(ctx, nil)
    if err != nil {
        return nil, err
    }
    defer tx.Rollback()
    
    // Get or create accounts
    senderAcc, err := l.getOrCreateAccount(ctx, tx, req.UserID, req.Currency)
    if err != nil {
        return nil, err
    }
    
    recipientAcc, err := l.getOrCreateAccount(ctx, tx, req.RecipientID, req.Currency)
    if err != nil {
        return nil, err
    }
    
    // Validate balance
    if senderAcc.Balance < req.Amount {
        return nil, ErrInsufficientBalance
    }
    
    // Debit sender
    if err := l.debit(ctx, tx, senderAcc.ID, req.Amount); err != nil {
        return nil, err
    }
    
    // Credit recipient
    if err := l.credit(ctx, tx, recipientAcc.ID, req.Amount); err != nil {
        return nil, err
    }
    
    // Create transaction record
    txn := &Transaction{
        ID:          generateID(),
        RequestID:   req.RequestID,
        UserID:      req.UserID,
        RecipientID: req.RecipientID,
        Amount:      req.Amount,
        Currency:    req.Currency,
        Status:      "COMPLETED",
        CreatedAt:   time.Now(),
        CompletedAt: time.Now(),
    }
    
    if err := l.txRepo.Create(ctx, tx, txn); err != nil {
        return nil, err
    }
    
    // Audit log
    if err := l.auditLog(ctx, tx, txn); err != nil {
        return nil, err
    }
    
    // Commit
    if err := tx.Commit(); err != nil {
        return nil, err
    }
    
    return txn, nil
}
```

## Repository Layer

Repositories provide data access and encapsulate SQL queries.

### Repository Pattern

```go theme={null}
// repository/account_repository.go
type AccountRepository struct {
    db *sql.DB
}

func (r *AccountRepository) GetByUserAndCurrency(
    ctx context.Context,
    userID, currency string,
) (*models.Account, error) {
    query := `
        SELECT id, user_id, currency, balance, created_at, updated_at
        FROM accounts
        WHERE user_id = $1 AND currency = $2
    `
    
    var acc models.Account
    err := r.db.QueryRowContext(ctx, query, userID, currency).Scan(
        &acc.ID, &acc.UserID, &acc.Currency, 
        &acc.Balance, &acc.CreatedAt, &acc.UpdatedAt,
    )
    
    if err == sql.ErrNoRows {
        return nil, ErrAccountNotFound
    }
    
    return &acc, err
}

func (r *AccountRepository) UpdateBalance(
    ctx context.Context,
    tx *sql.Tx,
    accountID string,
    delta int64,
) error {
    query := `
        UPDATE accounts 
        SET balance = balance + $1, updated_at = NOW()
        WHERE id = $2
    `
    
    _, err := tx.ExecContext(ctx, query, delta, accountID)
    return err
}
```

### Available Repositories

| Repository                  | Table               | Operations             |
| --------------------------- | ------------------- | ---------------------- |
| `AccountRepository`         | `accounts`          | CRUD, balance updates  |
| `TransactionRepository`     | `transactions`      | Create, query, list    |
| `SubscriberRepository`      | `subscribers`       | CRUD, lookup by phone  |
| `CardRepository`            | `nfc_cards`         | Link, block, list      |
| `AgentRepository`           | `agents`            | CRUD, float management |
| `AuditLogRepository`        | `audit_log`         | Create, query          |
| `APIKeyRepository`          | `api_keys`          | CRUD                   |
| `ComplianceAlertRepository` | `compliance_alerts` | Create, resolve        |
| `MonitoringRuleRepository`  | `monitoring_rules`  | CRUD                   |

## gRPC Server

The server layer maps gRPC requests to service calls:

```go theme={null}
// server/server.go
type WalletServer struct {
    wallet.UnimplementedWalletServiceServer
    ledger *ledger.Service
}

func (s *WalletServer) ExecutePayment(
    ctx context.Context,
    req *wallet.PaymentRequest,
) (*wallet.PaymentResponse, error) {
    // Map proto to domain
    payment := &ledger.PaymentRequest{
        RequestID:   req.RequestId,
        UserID:      req.UserId,
        RecipientID: req.RecipientId,
        Amount:      req.Amount,
        Currency:    req.Currency,
        Memo:        req.Memo,
    }
    
    // Execute
    txn, err := s.ledger.ExecutePayment(ctx, payment)
    if err != nil {
        return nil, mapToGRPCError(err)
    }
    
    // Map to proto response
    return &wallet.PaymentResponse{
        TransactionId: txn.ID,
        Status:        txn.Status,
        NewBalance:    txn.NewBalance,
    }, nil
}
```

## Database Migrations

Migrations are run at startup:

```go theme={null}
// database/database.go
func Migrate(db *sql.DB) error {
    migrations := []string{
        `CREATE TABLE IF NOT EXISTS accounts (...)`,
        `CREATE TABLE IF NOT EXISTS transactions (...)`,
        `CREATE TABLE IF NOT EXISTS subscribers (...)`,
        // ... more tables
    }
    
    for _, m := range migrations {
        if _, err := db.Exec(m); err != nil {
            return err
        }
    }
    
    return nil
}
```

## Error Handling

Domain errors are defined and mapped:

```go theme={null}
// errors.go
var (
    ErrAccountNotFound     = errors.New("account not found")
    ErrInsufficientBalance = errors.New("insufficient balance")
    ErrDuplicateRequest    = errors.New("duplicate request")
    ErrCardAlreadyLinked   = errors.New("card already linked")
)

// Map to gRPC status codes
func mapToGRPCError(err error) error {
    switch {
    case errors.Is(err, ErrAccountNotFound):
        return status.Error(codes.NotFound, err.Error())
    case errors.Is(err, ErrInsufficientBalance):
        return status.Error(codes.FailedPrecondition, err.Error())
    case errors.Is(err, ErrDuplicateRequest):
        return status.Error(codes.AlreadyExists, err.Error())
    default:
        return status.Error(codes.Internal, "internal error")
    }
}
```

## Next Steps

<CardGroup cols={2}>
  <Card title="Ledger" icon="coins" href="/wallet-core/ledger">
    Detailed ledger documentation
  </Card>

  <Card title="Configuration" icon="gear" href="/wallet-core/configuration">
    config.yaml reference
  </Card>
</CardGroup>
