Successfully implemented a centralized datasource mapping mechanism that allows developers to configure which datasources (drivers) different parts of the application use, without modifying individual object definitions.
-
DatasourceMappingRuleSchema: Zod schema defining routing rules with support for:
namespace: Match objects by namespace (e.g., 'crm', 'auth')package: Match objects by package ID (e.g., 'com.example.crm')objectPattern: Match objects by name pattern with glob support (e.g., 'sys_', 'temp_')default: Fallback rule for unmatched objectsdatasource: Target datasource namepriority: Optional priority for rule ordering (lower = higher priority)
-
ObjectStackDefinitionSchema: Added
datasourceMappingfield to accept an array of routing rules
- ManifestSchema: Added
defaultDatasourcefield to allow package-level default datasource configuration
- Storage: Added private fields for
datasourceMappingrules andmanifestsregistry - setDatasourceMapping(): Public method to configure mapping rules
- getDriver(): Updated to implement 4-tier resolution priority:
- Object's explicit
datasourcefield (if not 'default') - DatasourceMapping rules (namespace/package/pattern matching)
- Package's
defaultDatasourcefrom manifest - Global default driver
- Object's explicit
- resolveDatasourceFromMapping(): Evaluates rules in priority order and returns matched datasource
- matchPattern(): Implements glob-style pattern matching supporting
*and?wildcards - registerApp(): Updated to store manifests for defaultDatasource lookup
- AppPlugin.start(): Calls
ql.setDatasourceMapping()whendatasourceMappingis present in the stack definition
Created comprehensive test suite covering:
- Namespace-based routing
- Pattern-based routing (glob wildcards)
- Priority ordering
- Default fallback rules
- Explicit object datasource overrides
- DATASOURCE_MAPPING.md: Complete feature documentation with examples and use cases
- IMPLEMENTATION_SUMMARY.md: This file
1. Object.datasource (explicit)
↓ if 'default' or undefined
2. datasourceMapping rules
↓ if no match
3. Manifest.defaultDatasource
↓ if not set
4. Global default driver
The glob-style pattern matcher supports:
*(matches any characters):sys_*→sys_user,sys_role, etc.?(matches single character):temp_?→temp_1,temp_a, etc.- Exact matches:
account→ onlyaccount
This implementation draws from proven patterns:
- Django's Database Router: Multi-database routing based on app labels
- Kubernetes StorageClass: Declarative storage backend selection
- Salesforce External Objects: Datasource routing by object suffix
M packages/spec/src/stack.zod.ts
M packages/spec/src/kernel/manifest.zod.ts
M packages/objectql/src/engine.ts
M packages/runtime/src/app-plugin.ts
A packages/objectql/src/datasource-mapping.test.ts
A DATASOURCE_MAPPING.md
A IMPLEMENTATION_SUMMARY.md
// apps/server/objectstack.config.ts
export default defineStack({
manifest: {
id: 'com.objectstack.server',
name: 'ObjectStack Server',
version: '1.0.0',
},
plugins: [
new ObjectQLPlugin(),
new DriverPlugin(new TursoDriver({ url: 'file:./data/system.db' }), 'turso'),
new DriverPlugin(new InMemoryDriver(), 'memory'),
new AppPlugin(CrmApp),
],
datasourceMapping: [
// System objects → Turso
{ objectPattern: 'sys_*', datasource: 'turso' },
{ namespace: 'auth', datasource: 'turso' },
// CRM → Memory
{ namespace: 'crm', datasource: 'memory' },
// Default → Turso
{ default: true, datasource: 'turso' },
],
});- Centralized Configuration: All datasource routing in one place
- No Object Modification: Change datasources without touching object definitions
- Environment Flexibility: Different datasources per environment (dev/test/prod)
- Pattern-Based Batch Config: Configure multiple objects with one rule
- Backward Compatible: Existing explicit
datasourcefields still work and take priority
The test suite validates:
- ✅ Namespace matching works correctly
- ✅ Pattern matching with wildcards works correctly
- ✅ Priority ordering is respected
- ✅ Default fallback rules are applied
- ✅ Explicit object datasource overrides mapping rules
- Add read/write operation filtering (like Django's
db_for_readvsdb_for_write) - Support for conditional rules based on environment variables
- Performance metrics for datasource routing decisions
- Admin UI for visualizing datasource routing
- Migration tools to help convert from explicit to centralized configuration
- ✅ Fully backward compatible with existing code
- ✅ Objects with explicit
datasourcefield continue to work - ✅ No breaking changes to existing APIs
- ✅ TypeScript types are properly exported