# 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/ │ ├── commands/ # Command handlers │ │ ├── accounts/ # Account management commands │ │ │ ├── mod.rs # Account commands dispatch │ │ │ ├── link.rs # Account linking logic │ │ │ ├── list.rs # Account listing functionality │ │ │ └── status.rs # Account status functionality │ │ ├── transactions/ # Transaction management commands │ │ ├── list.rs # Source/destination listing │ │ └── sync.rs # Sync command handler │ ├── cli/ # CLI utilities and formatting │ ├── core/ # Domain logic and models │ ├── adapters/ # External service integrations │ └── main.rs # CLI entry point and command dispatch ├── 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. Command Handlers (`banks2ff/src/commands/`) The CLI commands are organized into focused modules: - **sync.rs**: Handles transaction synchronization between sources and destinations - **accounts/**: Account management including linking, listing, and status - **transactions/**: Transaction inspection, caching, and cache management - **list.rs**: Simple listing of available sources and destinations ### 4. 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 with robust error handling: 1. **Account Discovery**: Fetch active accounts from GoCardless (filtered by End User Agreement (EUA) validity) 2. **Agreement Validation**: Check EUA expiry status for each account's requisition 3. **Account Matching**: Match GoCardless accounts to Firefly asset accounts by IBAN 4. **Error-Aware Processing**: Continue with valid accounts when some have expired agreements 5. **Date Window**: Calculate sync range (Last Firefly transaction + 1 to Yesterday) 6. **Transaction Processing** (with error recovery): - **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 - **Error Handling**: Log issues but continue with other transactions/accounts ## 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 ### Agreement Expiry Handling - **Proactive Validation**: Checks End User Agreement (EUA) expiry before making API calls to avoid unnecessary requests - **Reactive Recovery**: Detects expired agreements from API 401 errors and skips affected accounts - **Continued Operation**: Maintains partial sync success even when some accounts are inaccessible - **User Feedback**: Provides detailed reporting on account status and re-authorization needs - **Multiple Requisitions**: Supports accounts linked to multiple requisitions, using the most recent valid one ### 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 including End User Agreement (EUA) expiry (`SyncError::AgreementExpired`) - **Propagation**: `anyhow` for error context across async boundaries - **Graceful Degradation**: Rate limits, network issues, and expired agreements don't crash entire sync - **Partial Success**: Continues processing available accounts when some fail - **Structured Logging**: `tracing` for observability and debugging with account-level context ## Configuration Management - Environment variables loaded via `dotenvy` - Workspace-level dependency management - Feature flags for optional functionality - Secure credential handling (no hardcoded secrets)