Implement logic
This commit is contained in:
114
docs/architecture.md
Normal file
114
docs/architecture.md
Normal file
@@ -0,0 +1,114 @@
|
||||
# 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)
|
||||
Reference in New Issue
Block a user