Data provider simplification, tests, and documentation #1997
Reference in New Issue
Block a user
Delete Branch "zachgoll/data-provider-concepts"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
This PR simplifies and consolidates 3rd party data access patterns throughout the app. It prioritizes:
Before / after
Providers are now much clearer and simpler to write.
Provider::Synthbefore (example)Provider::SynthafterData Providers
The Maybe app utilizes several 3rd party data services to calculate historical account balances, enrich data, and more. Since the app can be run in both "hosted" and "self hosted" mode, this means that data providers are optional for self hosted users and must be configured.
Because of this optionality, data providers must be configured at runtime through the @providers.rb module, utilizing @setting.rb for runtime parameters like API keys:
There are two types of 3rd party data in the Maybe app:
"Concept" data
Since the app is self hostable, users may prefer using different providers for generic data like exchange rates and security prices. When data is generic enough where we can easily swap out different providers, we call it a data "concept".
Each "concept" must have a
Provideableconcern that defines the methods that must be implemented along with the data shapes that are returned. For example, an "exchange rates concept" might look like this:Where the
Provideableand concrete provider implementations would be something like:Any provider that is a valid exchange rate provider must implement this interface:
One-off data
For data that does not fit neatly into a "concept", a
Provideableis not required and the concrete provider may implement ad-hoc methods called directly in code. For example, the @synth.rb provider has ausagemethod that is only applicable to this specific provider. This should be called directly without any abstractions:"Provided" Concerns
In general, domain models should not be calling
Providers.some_providerdirectly. When 3rd party data is required for a domain model, we use theProvidedconcern within that model's namespace. This concern is primarily responsible for:For example, @exchange_rate.rb has a @provided.rb concern with the following convenience methods:
This exposes a generic access pattern where the caller does not care which provider has been chosen for the concept of exchange rates and can get a predictable response:
Concrete provider implementations
Each 3rd party data provider should have a class under the
Provider::namespace that inherits fromProviderand returnsprovider_response, which will return aProvider::ProviderResponseobject:The
provider_responseautomatically catches provider errors, so concrete provider classes should raise when valid data is not possible: