Sophisticated patterns.
Accessible to everyone.
Advanced software patterns—CQRS, event sourcing, vector search—are locked behind walls of complexity and configuration. We believe these patterns should be learnable by building, not by reading academic papers. Our tools take your hand and show you the path.
The Pattern Paradox
Enterprise patterns solve real problems. CQRS prevents read/write bottlenecks. Event sourcing enables audit trails and time-travel debugging. Vector search powers semantic understanding. These patterns exist because they work. But they're trapped behind walls of complexity.
Want to try CQRS? First, spend 3 days understanding MediatR, handlers, pipelines, validation, and dependency injection. Then do it again for the next project. Advanced patterns are locked behind walls of boilerplate—you can't experiment until you've invested weeks learning the setup. Every new capability means new services to configure, new connection strings, new docker-compose files. Want semantic search? Great, here are 12 decisions to make first.
Complex patterns should reveal themselves through simple code. Write Todo.Save()
and discover repositories, validation, event publishing. See it work, then understand why.
Add one package, write one entity, get CQRS, multi-provider data access, auto-generated APIs,
and event-driven architecture. Experiment freely, learn as you build.
Want vector search? Add [Embedding] to a property.
Need Postgres? Reference the connector. Your intent is clear, infrastructure disappears.
Take your hand and show you the path. That's Sylin.
Six pillars, zero configuration
Entity-first development, multi-provider data access, native AI, auto-generated APIs, event-driven messaging, and container orchestration. Add a package reference, write an entity, watch it work. That's Koan.
Entity-First Development
1Define your entity
public class Todo : Entity<Todo>
{
public string Title { get; set; } = "";
public bool IsCompleted { get; set; }
}
2It just works
// Auto-generated GUID v7 IDs
var todo = new Todo { Title = "Try Koan Framework" };
await todo.Save();
// Query naturally
var all = await Todo.Query();
var pending = await Todo.Where(t => !t.IsCompleted);
Multi-Provider Transparency
1Add any database
# Reference = Intent (zero configuration)
dotnet add package Koan.Data.Connector.Postgres
# Or: Sqlite, Mongo, Redis, SqlServer, Couchbase...
2Same code, different stores
// Routes to Postgres automatically
await product.Save();
// Switch providers via config - code unchanged
// appsettings.json: "Data:Provider": "Mongo"
// Even mix providers in the same app
[StoreIn("Redis")]
public class Session : Entity<Session> { }
Native AI Integration
1Enable semantic search
public class Document : Entity<Document>
{
public string Title { get; set; } = "";
public string Content { get; set; } = "";
[Embedding] // Auto-generates embeddings
public string SearchableText => $"{Title} {Content}";
}
2Search semantically
// Natural language queries
var results = await Document.SemanticSearch(
"kubernetes deployment strategies",
limit: 10
);
// Vector operations feel like LINQ
var similar = await doc.FindSimilar(threshold: 0.8);
Auto-Generated APIs
1Define controller
[ApiController, Route("api/[controller]")]
public class ProductsController : EntityController<Product>
{
// That's it. Full REST API generated.
}
2Full CRUD out of the box
GET /api/products # List with filtering
GET /api/products/{id} # Get by ID
POST /api/products # Create
PUT /api/products/{id} # Update
PATCH /api/products/{id} # Partial update
DELETE /api/products/{id} # Delete
// Plus: filtering, sorting, pagination, field selection
Event-Driven Architecture
1Send events
// Same Entity<T> pattern extends to messaging
await new OrderCompleted
{
OrderId = order.Id,
Total = order.Total
}.Send();
2Handle events
public class OrderCompletedHandler : IMessageHandler<OrderCompleted>
{
public async Task Handle(OrderCompleted evt)
{
// Auto-discovered via Reference = Intent
await SendConfirmationEmail(evt.OrderId);
}
}
Container Management
1Define dependencies
// appsettings.json
{
"Orchestration": {
"Services": ["postgres", "redis", "rabbitmq"]
}
}
2Auto-generated compose files
# Koan discovers dependencies and generates docker-compose.yml
dotnet run
# Health checks, networking, volumes - all handled
# Or use the CLI:
koan compose generate
koan compose up
Built to feel natural
Every feature designed to eliminate boilerplate, reduce cognitive load, and let you focus on what matters—your domain logic.
Zero-Code REST APIs
Inherit from EntityController<T> and get full CRUD + Query + Patch API automatically.
public class MembersController :
EntityController<Member> { }
✓ Eliminates 100+ lines of boilerplate per controller
No Repository Injection
CRUD operations as static methods directly on entities. No DI, no interfaces, no service layers.
var member = await Member.Get(id);
await member.Save();
✓ Just call the method
One-Line Framework Setup
Single method call auto-discovers and registers everything.
builder.Services.AddKoan();
// That's it.
✓ Zero manual registration
Lifecycle Events
BeforeUpsert/AfterUpsert hooks with declarative field protection.
Reading.Events
.Setup(ctx => ctx.ProtectAll())
.BeforeUpsert(async ctx => { })
✓ Type-safe, co-located with entity
Instant Relationships
Declare FK relationships with a single attribute.
[Parent(typeof(Member))]
public string? MemberId { get; set; }
✓ No Fluent API needed
Built-in File Handling
MediaEntity<T> with automatic storage integration.
var stream = await photo.OpenRead();
return File(stream, photo.ContentType);
✓ Entity IS the file handle
Automatic Vector Detection
float[] properties automatically become embeddings—no attribute needed.
public float[]? Embedding { get; set; }
// Vector search enabled automatically
✓ Convention over configuration
Reference = Intent
Add package reference, get functionality—no registration needed.
<PackageReference
Include="Koan.Data.Connector.Postgres" />
✓ Auto-discovered and registered
Smart Property Detection
No [NotMapped] attributes—framework detects computed properties automatically.
public string FullName =>
$"{FirstName} {LastName}";
✓ Just write C# naturally
Built-In Batch Operations
Get multiple entities with single call, optimized by provider.
var photos = await Photo.Get(ids);
var all = await Photo.All();
✓ No N+1 queries
Custom ID Types
Use any type as primary key with Entity<T, TKey>.
public class Sensor :
Entity<Sensor, string> { }
✓ Natural keys without extra fields
Prior State Access
Compare current vs previous entity state in lifecycle hooks.
var prior = await ctx.Prior.Get();
if (prior?.PlotId != ctx.Current.PlotId)
// Handle change
✓ No manual state tracking