Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
| Total | n/a |
0 / 0 |
n/a |
0 / 0 |
CRAP | n/a |
0 / 0 |
|||
| 1 | <?php |
| 2 | |
| 3 | declare(strict_types=1); |
| 4 | |
| 5 | namespace App\Currency\Application; |
| 6 | |
| 7 | use App\Shared\Domain\ValueObject\Currency; |
| 8 | use DateTimeImmutable; |
| 9 | |
| 10 | /** |
| 11 | * Port: the exchange rate between two currencies as of a given date. Conversion |
| 12 | * stays a view-time concern (never stored on an Expense), but it is anchored to |
| 13 | * the expense's spend date — not "today" — so a past expense always shows the |
| 14 | * rate of the day it was spent and never drifts with the market. Past rates are |
| 15 | * immutable, so the result is stable and safe to cache for a long time. The |
| 16 | * Reporting context reuses this port. |
| 17 | * |
| 18 | * DECISION (2026-06-22) — view-time historical lookup (B), NOT a persisted rate |
| 19 | * snapshot (A). The drift the user flagged comes from using *today's* rate, not |
| 20 | * from a lack of persistence; looking the rate up by the spend date fixes it with |
| 21 | * zero schema/aggregate/write-path change, and survives a user changing their |
| 22 | * preferred currency (it re-converts to the *current* preferred at the historical |
| 23 | * rate). A snapshot was rejected because recording a back-dated expense would |
| 24 | * still need a history-capable provider at write time, so it would NOT remove the |
| 25 | * provider-history dependency — it would only freeze already-recorded rows. |
| 26 | * |
| 27 | * FUTURE — switch to persistence (or a dedicated daily rate-history store) ONLY |
| 28 | * when one of these becomes real: |
| 29 | * 1. we leave a provider that serves historical dates (Frankfurter `/v1/{date}`) |
| 30 | * for one that serves only "latest" → then own the rates (snapshot at write + |
| 31 | * back-fill, or an `exchange_rates(date, base, quote, rate)` table); |
| 32 | * 2. we need an immutable audit trail frozen at write time, independent of any |
| 33 | * provider. |
| 34 | * A per-user "change preferred currency" feature alone does NOT force this. Full |
| 35 | * rationale + the rejected option in docs/PROGRESS.md. |
| 36 | */ |
| 37 | interface ExchangeRateProvider |
| 38 | { |
| 39 | /** |
| 40 | * Multiplier to take an amount from $from into $to as of $on (e.g. EUR→USD ≈ 1.08). |
| 41 | */ |
| 42 | public function rate(Currency $from, Currency $to, DateTimeImmutable $on): float; |
| 43 | } |