Files
banks2ff/docs/architecture.md
2025-11-22 15:04:04 +00:00

4.5 KiB

Architecture Documentation

Overview

Banks2FF implements a Hexagonal (Ports & Adapters) Architecture to synchronize bank transactions from GoCardless to Firefly III. This architecture separates business logic from external concerns, making the system testable and maintainable.

Workspace Structure

banks2ff/
├── banks2ff/           # Main CLI application
│   └── src/
│       ├── core/       # Domain logic and models
│       ├── adapters/   # External service integrations
│       └── main.rs     # CLI entry point
├── firefly-client/     # Firefly III API client library
├── gocardless-client/  # GoCardless API client library
└── docs/              # Architecture documentation

Core Components

1. Domain Core (banks2ff/src/core/)

models.rs: Defines domain entities

  • BankTransaction: Core transaction model with multi-currency support
  • Account: Bank account representation
  • Supports foreign_amount and foreign_currency for international transactions

ports.rs: Defines abstraction traits

  • TransactionSource: Interface for fetching transactions (implemented by GoCardless adapter)
  • TransactionDestination: Interface for storing transactions (implemented by Firefly adapter)
  • Traits are mockable for isolated testing

sync.rs: Synchronization engine

  • run_sync(): Orchestrates the entire sync process
  • Implements "Healer" strategy for idempotency
  • Smart date range calculation (Last Transaction Date + 1 to Yesterday)

2. Adapters (banks2ff/src/adapters/)

gocardless/: GoCardless integration

  • client.rs: Wrapper for GoCardless client with token management
  • mapper.rs: Converts GoCardless API responses to domain models
  • cache.rs: Caches account mappings to reduce API calls
  • Correctly handles multi-currency via currencyExchange array parsing

firefly/: Firefly III integration

  • client.rs: Wrapper for Firefly client for transaction storage
  • Maps domain models to Firefly API format

3. API Clients

Both clients are hand-crafted using reqwest:

  • Strongly-typed DTOs for compile-time safety
  • Custom error handling with thiserror
  • Rate limit awareness and graceful degradation

Synchronization Process

The "Healer" strategy ensures idempotency:

  1. Account Discovery: Fetch active accounts from GoCardless
  2. Account Matching: Match GoCardless accounts to Firefly asset accounts by IBAN
  3. Date Window: Calculate sync range (Last Firefly transaction + 1 to Yesterday)
  4. Transaction Processing:
    • Search: Look for existing transaction using windowed heuristic (date ± 3 days, exact amount)
    • Heal: If found without external_id, update with GoCardless transaction ID
    • Skip: If found with matching external_id, ignore
    • Create: If not found, create new transaction in Firefly

Key Features

Multi-Currency Support

  • Parses currencyExchange array from GoCardless responses
  • Calculates foreign_amount = amount * exchange_rate
  • Maps to Firefly's foreign_amount and foreign_currency_code fields

Rate Limit Management

  • Caching: Stores AccountId -> IBAN mappings to reduce requisition calls
  • Token Reuse: Maintains tokens until expiry to minimize auth requests
  • Graceful Handling: Continues sync for other accounts when encountering 429 errors

Idempotency

  • GoCardless transactionId → Firefly external_id mapping
  • Windowed duplicate detection prevents double-creation
  • Historical transaction healing for pre-existing data

Data Flow

GoCardless API → GoCardlessAdapter → TransactionSource → SyncEngine → TransactionDestination → FireflyAdapter → Firefly API

Testing Strategy

  • Unit Tests: Core logic with mockall for trait mocking
  • Integration Tests: API clients with wiremock for HTTP mocking
  • Fixture Testing: Real JSON responses for adapter mapping validation
  • Isolation: Business logic tested without external dependencies

Error Handling

  • Custom Errors: thiserror for domain-specific error types
  • Propagation: anyhow for error context across async boundaries
  • Graceful Degradation: Rate limits and network issues don't crash entire sync
  • Structured Logging: tracing for observability and debugging

Configuration Management

  • Environment variables loaded via dotenvy
  • Workspace-level dependency management
  • Feature flags for optional functionality
  • Secure credential handling (no hardcoded secrets)