koan-multi-provider
Provider transparency, capability detection, context routing (partition/source/adapter)
- Category: discovered
- Tags: github, community
- Version: 1.0.0
- Author: walid
- Downloads: 0
- GitHub stars: 2
- Updated: 2025-11-21
Download the latest version (v1.0.0) from this page.
SKILL.md
---
name: koan-multi-provider
description: Provider transparency, capability detection, context routing (partition/source/adapter)
---
# Koan Multi-Provider Transparency
## Core Principle
**Same entity code works across SQL, NoSQL, Vector, JSON stores.** Koan Framework provides complete provider transparency with automatic capability detection and graceful fallbacks.
## Revolutionary Approach
Write code once, run on any data provider:
- **PostgreSQL** - Relational with JSON support
- **MongoDB** - Document database
- **SQLite** - Embedded relational
- **Redis** - Key-value cache
- **JSON** - File-based development
- **Weaviate/Milvus** - Vector databases
- **InMemory** - Testing
No code changes needed to switch providers. Just change configuration.
## Provider Capability Detection
```csharp
// Check what current provider supports
var capabilities = Data<Todo, string>.QueryCaps;
if (capabilities.Capabilities.HasFlag(QueryCapabilities.LinqQueries))
{
// Provider supports server-side LINQ (Postgres, Mongo, SQL Server)
var filtered = await Todo.Query(t => t.Priority > 3 && !t.Completed);
}
else
{
// Provider requires client-side filtering (JSON, InMemory)
var all = await Todo.All();
var filtered = all.Where(t => t.Priority > 3 && !t.Completed).ToList();
}
// Check fast removal support
if (Todo.SupportsFastRemove)
{
// Provider supports TRUNCATE/DROP (Postgres, SQL Server, Mongo)
await Todo.RemoveAll(RemoveStrategy.Fast);
}
```
## Context Routing
### Partition (Logical Suffix)
Partitions provide logical data isolation within same provider:
```csharp
// Default partition
var todo = new Todo { Title = "Active task" };
await todo.Save(); // Stored as Todo
// Archive partition
using (EntityContext.Partition("archive"))
{
var archived = new Todo { Title = "Archived task" };
await archived.Save(); // Stored as Todo#archive
}
// Tenant isolation
using (EntityContext.Partition(quot;tenant-{tenantId}"))
{
var tenantTodos = await Todo.All(); // Only this tenant's data
}
```
**Use Cases:**
- Multi-tenant isolation
- Archival storage
- Test data separation
- Environment segmentation
### Source (Named Configuration)
Sources route to different provider configurations:
```csharp
// appsettings.json
{
"Koan": {
"Data": {
"Sources": {
"Default": {
"Adapter": "postgres",
"ConnectionString": "Host=localhost;Database=main"
},
"Analytics": {
"Adapter": "postgres",
"ConnectionString": "Host=readonly-replica;Database=analytics"
},
"Cache": {
"Adapter": "redis",
"ConnectionString": "localhost:6379"
}
}
}
}
}
// Route to analytics read-replica
using (EntityContext.Source("analytics"))
{
var stats = await Todo.Count; // Reads from replica
}
// Route to cache
using (EntityContext.Source("cache"))
{
var frequent = await FrequentQuery.Get(id); // From Redis
}
```
**Use Cases:**
- Read replicas
- Analytics databases
- Cache layers
- Regional data centers
### Adapter (Explicit Provider Override)
Adapters explicitly select provider regardless of configuration:
```csharp
// Force MongoDB
using (EntityContext.Adapter("mongodb"))
{
var todos = await Todo.All(); // Always uses MongoDB
}
// Force JSON for testing
using (EntityContext.Adapter("json"))
{
await todo.Save(); // Writes to JSON file
}
```
**Use Cases:**
- Provider-specific features
- Migration testing
- Development overrides
- Provider comparison
### CRITICAL RULE: Source XOR Adapter
**NEVER combine Source and Adapter** (ADR DATA-0077):
```csharp
// ❌ WRONG: Conflicting context
using (EntityContext.Source("analytics"))
using (EntityContext.Adapter("mongodb"))
{
// Which wins? Undefined behavior!
}
// ✅ CORRECT: Use one or the other
using (EntityContext.Source("analytics"))
{
// Routes via named source configuration
}
// OR
using (EntityContext.Adapter("mongodb"))
{
// Explicit provider override
}
```
## Context Nesting
Contexts nest and replace previous values:
```csharp
// Outer context
using (EntityContext.Source("analytics"))
{
var count1 = await Todo.Count; // analytics source
// Inner context replaces outer
using (EntityContext.Partition("archive"))
{
var count2 = await Todo.Count; // analytics + archive partition
}
var count3 = await Todo.Count; // back to analytics (no partition)
}
```
## Provider-Specific Configuration
### Forcing Specific Provider
Use `[DataAdapter]` attribute to pin entity to provider:
```csharp
// Always use MongoDB for this entity
[DataAdapter("mongodb")]
public class FlexibleDocument : Entity<FlexibleDocument>
{
public Dictionary<string, object> Properties { get; set; } = new();
}
// Always use vector database
[DataAdapter("weaviate")]
public class MediaEmbedding : Entity<MediaEmbedding>
{
[VectorField]
public float[] Embedding { get; set; } = Array.Empty<float>();
}
```
## Capability Fallback Patterns
```csharp
public async Task<List<Todo>> SearchWithFallback(string searchTerm)
{
var caps = Data<Todo, string>.QueryCaps;
if (caps.Capabilities.HasFlag(QueryCapabilities.FullTextSearch))
{
// Provider supports full-text search
return await Todo.Query(quot;CONTAINS(Title, '{searchTerm}')");
}
else
{
// Fallback to client-side filtering
var all = await Todo.All();
return all.Where(t => t.Title.Contains(searchTerm,
StringComparison.OrdinalIgnoreCase)).ToList();
}
}
```
## Provider Comparison Matrix
| Provider | LINQ Queries | Transactions | Fast Remove | Vector Search | JSON Fields |
|----------|--------------|--------------|-------------|---------------|-------------|
| **PostgreSQL** | ✅ | ✅ | ✅ | ✅ (pgvector) | ✅ |
| **SQL Server** | ✅ | ✅ | ✅ | ❌ | ✅ (limited) |
| **MongoDB** | ✅ | ✅ | ✅ | ❌ | ✅ (native) |
| **SQLite** | ✅ | ✅ | ✅ | ❌ | ✅ (json1) |
| **Redis** | ❌ | ❌ | ✅ | ❌ | ✅ |
| **JSON** | ❌ | ❌ | ❌ | ❌ | ✅ (native) |
| **InMemory** | ❌ | ❌ | ❌ | ❌ | ✅ |
| **Weaviate** | ❌ | ❌ | ❌ | ✅ | ✅ |
| **Milvus** | ❌ | ❌ | ❌ | ✅ | ✅ |
## When This Skill Applies
Invoke this skill when:
- ✅ Working with multiple data providers
- ✅ Switching between providers
- ✅ Implementing multi-tenant isolation
- ✅ Routing to read replicas
- ✅ Writing capability-aware code
- ✅ Debugging provider-specific issues
## Reference Documentation
- **Full Guide:** `docs/guides/entity-capabilities-howto.md` § Context Routing
- **ADR:** DATA-0077 (Source XOR Adapter rule)
- **Sample:** `samples/S10.DevPortal/` (Multi-provider showcase)
- **Sample:** `samples/S14.AdapterBench/` (Provider performance comparison)
## Framework Compliance
Multi-provider patterns are fundamental to Koan Framework:
- ✅ Write provider-agnostic code
- ✅ Use capability detection
- ✅ Implement graceful fallbacks
- ✅ Follow Source XOR Adapter rule
- ❌ Never write provider-specific code without capability checks
- ❌ Never hard-code provider assumptions