Skip to content

๐Ÿ“ก Domain Events in Fintech

Event-driven architecture: decoupling business logic with domain events

๐Ÿ Overview

Domain events are immutable messages that represent significant business occurrences within the system. In this fintech project, domain events are the backbone of the event-driven architecture, enabling modular, decoupled, and extensible workflows for payments, transfers, deposits, withdrawals, and account operations.

๐Ÿ”‘ Key Principles

  1. Immutable: Events are records of things that happened in the past
  2. Self-Contained: Each event carries all necessary data
  3. Named in Past Tense: Events represent something that has already occurred
  4. Causality: Events form a directed acyclic graph (DAG)
  5. Idempotency: Event handling must be idempotent

๐Ÿ—๏ธ Event Structure

All domain events implement the following interface:

type Event interface {

    Type() string // Event type

๐ŸŒ Event Bus

Events are published to an event bus that routes them to registered handlers:

type EventBus interface {
    Emit(ctx context.Context, event Event) error
    Register(eventType string, handler EventHandler) error
}

๐Ÿ“Š Event Store

All events are persisted in an event store for audit and replay:

type EventStore interface {
    Save(events []Event) error
    Load(aggregateID string) ([]Event, error)
    Subscribe(handler EventHandler) error
}

๐Ÿงฉ Key Domain Events

Events are defined in pkg/domain/events/:

Deposit Flow

  • Deposit.Requested - Initial deposit request
  • Deposit.CurrencyConverted - Input validation completed
  • Deposit.Validated - Deposit record created in database
  • Payment.Initiated - Payment processing started with provider

Withdraw Flow

  • Withdraw.Requested - Initial withdraw request
  • Withdraw.CurrencyConverted - Input validation completed
  • Withdraw.Validated - Withdraw record created in database
  • Payment.Initiated - Payment processing started with provider

Transfer Flow

  • Transfer.Requested - Initial transfer request
  • Transfer.CurrencyConverted - Input validation completed
  • Transfer.Validated - Transfer record created in database
  • Transfer.Completed - Transfer fully completed

Payment Events

  • Payment.Initiated - Payment processing started with provider
  • Payment.Processed - Payment processed by webhook
  • Payment.Completed - Payment confirmed by provider
  • Payment.Failed - Payment processing failed

Common Events

  • AccountBalanceUpdatedEvent - Account balance was updated
  • TransactionCreatedEvent - New transaction was created

๐Ÿ–ผ๏ธ Event Flow Relationships

```mermaid flowchart TD subgraph Deposit DR[Deposit.Requested] DC[Deposit.CurrencyConverted] DV[Deposit.Validated]

    DR --> DC
    DC --> DV
    DV --> PI
end

subgraph Withdraw
    WR[Withdraw.Requested]
    WC[Withdraw.CurrencyConverted]
    WV[Withdraw.Validated]

    WR --> WC
    WC --> WV
    WV --> PI
end

subgraph Transfer
    TR[Transfer.Requested]
    TV[Transfer.Validated]
    TCC[Transfer.CurrencyConverted]
    TC[Transfer.Completed]

    TR --> TV
    TV --> TCC
    TCC --> TC
end

subgraph Payment
    PI[Payment.Initiated]
    PP[Payment.Processed]
    PC[Payment.Completed]
    PF[Payment.Failed]

    PI --> PP
    PP --> PC
    PC --> PF
end

```

Common FlowEvent

type FlowEvent struct {
    FlowType      string    // "deposit", "withdraw", "transfer", "payment", "payment"
    UserID        uuid.UUID
    AccountID     uuid.UUID
    CorrelationID uuid.UUID
    Timestamp     time.Time
}

Event Interface

type Event interface {
    Type() string
}

๐Ÿ› ๏ธ Best Practices

  • Immutability: Events should never be mutated after creation.
  • Explicit Event Types: Use clear, descriptive event type names (see Type() methods).
  • Decoupling: Business logic should be implemented in event handlers, not in the event emitters.
  • Extensibility: Add new events for new business flows; subscribe handlers as needed.
  • Correlation IDs: All events include correlation IDs for tracing across the entire flow.
  • Structured Logging: All handlers use emoji-rich structured logging for clarity.

๐Ÿงช Testing

  • E2E Event Flow Tests: Verify complete event chains for each business flow
  • Unit Tests: Test individual event handlers in isolation
  • Mock Integration: Use testify mocks for repository and external service interactions
  • Static Analysis: Automated cycle detection prevents infinite event loops

๐Ÿ“š References