qbil-software/openexchangeratesbundle
Symfony bundle for integrating the Open Exchange Rates API, providing configurable services to fetch currency exchange rates and use them in PHP applications. Simplifies API access and rate handling.
Architecture fit — poor, and this is the headline finding. The package is a Symfony bundle, not a Laravel package. Its value is almost entirely in bundle plumbing: a Bundle class, a DependencyInjection/Extension that builds a Configuration tree, and service definitions that wire an HTTP client into a rate-fetching service. Laravel has no bundle registry, no Kernel::registerBundles(), and no config-tree compiler, so none of that machinery executes. The only genuinely reusable asset is the small service class that calls the Open Exchange Rates /latest.json endpoint — realistically 30–80 lines of logic that we would rewrite in an afternoon. The 0.045 score and 3 stars reflect that near-zero marginal value.
Integration feasibility — technically possible, practically inadvisable. We could composer require it and bootstrap a mini Symfony container inside a service provider, but that buys us a container-inside-a-container, an unfamiliar config format, and a dependency tree we don't want. The far cheaper path is to read the source as a reference implementation (confirm the endpoint, query parameters, and response shape) and reimplement the call using Laravel's Http facade, config(), and Cache.
Technical risk — high, driven by staleness and abandonment. Last release was 2019-11-13, roughly six years ago. That vintage implies:
symfony/http-kernel / symfony/dependency-injection constraint in the 2.8–4.x range, which will collide with the symfony/* ^6|^7 components that Laravel 10 and 11 ship. Composer resolution will likely fail outright without --ignore-platform-reqs, which we should never ship.There is also a product-level risk independent of the code: Open Exchange Rates' free plan is USD-base-only and requires client-side triangulation for other base currencies, with roughly a 1,000-request/month quota. If the bundle predates or ignores that restriction, it can produce silently wrong conversions rather than loud failures.
Key questions for the spike:
Http:: against the API directly) that removes this dependency entirely?Stack fit. Treat the package as documentation, not as a dependency. Laravel already provides every ingredient the bundle exists to supply: an HTTP client (Illuminate\Support\Facades\Http), configuration (config/services.php + env), caching, and an IoC container. The bundle's entire contribution — service registration and config plumbing — is dead weight in our stack.
Recommended path (no package install):
openexchangerates block to config/services.php reading OPEN_EXCHANGE_RATES_APP_ID from .env.ExchangeRateService in app/Services against Http:: with an explicit timeout, a single retry with backoff, and Http::throw() for non-2xx.Cache::remember() keyed on base currency and value date; expose a "last known good" fallback.ExchangeRateProvider interface in a service provider so pricing code depends on the contract, not the implementation — this is what makes it testable and swappable.Http::fake() tests against recorded OXR fixtures, including a 429 and a malformed-payload case.Migration path. There is no migration — there is nothing in production depending on this bundle (0 dependents). If a team has already prototyped with it, the migration is a like-for-like swap of one injected class behind an interface, which is a half-day of work at most given the interface is introduced first.
Compatibility. Blocking issues are PHP floor, Symfony component constraints, and Guzzle pin. None are negotiable in a modern Laravel app; all disappear under the reimplementation path. If the consuming application is in fact Symfony (not Laravel), the analysis inverts: register the bundle, set the app id parameter, inject the service — but even then the 2019 vintage means a Symfony 5.4+/6.x app will hit the same constraint wall.
Sequencing. (1) Validate the OXR API and our plan tier with a throwaway script — confirm base-currency restriction, quota, and current response schema. (2) Confirm expected call volume against the 1,000/month free-tier ceiling; if we exceed it, budget for a paid tier before any code is written. (3) Build the service behind an interface with caching and fallback. (4) Contract tests. (5) Only then wire into pricing/domain code, with a finance stakeholder reviewing rounding and triangulation behavior.
Maintenance. Effectively zero upstream maintenance. Six years without a release, three stars, no dependents: we would be adopting code we must own outright. Copying the ~50 lines of relevant logic into our own service is strictly cheaper than owning a fossilised dependency that dependency scanners will flag on every audit.
Support. No vendor support, no SLA, no security response process. The only real SLA in the chain comes from Open Exchange Rates, so subscribe to their status feed and treat their incident as our incident. Community support is a single maintainer's issue tracker.
Scaling. The binding constraint is not compute — it is the API quota. Design for: cache keys per base currency per value date; a hot-path read that never blocks on the network; single-flight refresh so a cold cache doesn't stampede the provider; and an internal rate limit if the rate lookup is ever exposed as an endpoint.
Failure modes to design against:
Ramp-up. Engineering ramp-up on the code is trivial — it is a few dozen lines. The real ramp-up is domain and compliance: understanding USD-base triangulation and rounding policy, agreeing with Finance on how rates are used in pricing and invoicing, and honouring OXR's attribution requirements on the free tier. Budget for a finance review, not an architecture review.
How can I help you explore Laravel packages today?