Data sources
Adapter architecture, service providers, and API integration. DataSourceFactory is the component that selects the active data source and exposes service implementations to BFF modules.
DataSourceFactory
In apps/bff, the factory injects the active data source and each provider, then exposes a single getServices() API used by BFF modules:
TypeScript
@Injectable()
export class DataSourceFactory {
constructor(
@Inject('COMMERCETOOLS_SERVICE_PROVIDER')
private readonly commercetoolsServiceProvider: CommercetoolsServiceProvider,
@Inject('MOCK_SERVICE_PROVIDER')
private readonly mockServiceProvider: MockServiceProvider,
@Inject(DATA_SOURCE) private readonly dataSource: DataSource
) {}
getServices(): {
productService: ProductService
navigationService: NavigationService
} {
switch (this.dataSource) {
case 'commercetools-set':
return this.commercetoolsServiceProvider.getServices()
case 'mock-set':
return this.mockServiceProvider.getServices()
default:
throw new Error(`Unknown data source: ${this.dataSource}`)
}
}
}
Customisation options
1. Single data source
If you only need one data source:
- Remove unused providers from DataSourceModule
- Update the factory to return only that provider’s services
- Remove the
switchand use the provider directly
TypeScript
getServices() {
return this.commercetoolsServiceProvider.getServices()
}
2. Multiple data sources
The factory can switch between data sources via:
- Environment variables — Set the
DATA_SOURCEenvironment variable - Request headers — Use the
x-data-sourceheader for per-request switching (the optionaldemo/package can drive this; the storefront does not ship a global picker by default) - Runtime configuration — Inject different data sources based on business logic
3. Mixed data sources
You can combine services from different providers:
TypeScript
getServices() {
const commercetoolsServices = this.commercetoolsServiceProvider.getServices()
const mockServices = this.mockServiceProvider.getServices()
const contentfulServices = this.contentfulServiceProvider.getServices()
return {
productService: commercetoolsServices.productService,
navigationService: mockServices.navigationService,
contentService: contentfulServices.contentService
}
}
Use this pattern when, for example, products come from Commercetools, navigation from mock during development, and page content from Contentful.