con un clic
koan-data-modeling
Aggregate boundaries, relationships, lifecycle hooks, value objects
Instalar con Codex o Claude Copia este prompt, pégalo en Codex, Claude u otro asistente, y deja que revise la página de la skill y la instale por ti.
Menú
Aggregate boundaries, relationships, lifecycle hooks, value objects
Instalar con Codex o Claude Copia este prompt, pégalo en Codex, Claude u otro asistente, y deja que revise la página de la skill y la instale por ti.
Basado en la clasificación ocupacional SOC
Auto-registration via KoanAutoRegistrar, minimal Program.cs, "Reference = Intent" pattern
Chat endpoints, embeddings, RAG workflows, vector search
Entity<T> patterns, GUID v7 auto-generation, static methods vs manual repositories
Transparent L1/L2 caching for Entity<T>, [Cacheable] attribute, cross-node coherence, per-request opt-out
Run a mandatory pre-implementation exploration workflow before writing production code in Koan (.NET/C#). Use when a task requires code changes and Codex must first map concerns/layers, read relevant files and docs, check existing constants and types, identify the closest existing pattern, plan exact code placement, and confirm architectural guardrails.
EntityController<T>, custom routes, payload transformers, auth policies
| name | koan-data-modeling |
| description | Aggregate boundaries, relationships, lifecycle hooks, value objects |
Entities are aggregates that encapsulate business logic and define clear boundaries. Use lifecycle hooks for invariants, value objects for cohesive data, and navigation helpers for relationships.
public class Order : Entity<Order>
{
public string CustomerId { get; set; } = "";
public Money Total { get; private set; } = new(0m, "USD");
public OrderStatus Status { get; private set; } = OrderStatus.Draft;
// Business methods
public void MarkShipped() => Status = OrderStatus.Shipped;
// Navigation helper
public Task<Customer?> GetCustomer(CancellationToken ct = default) =>
Customer.Get(CustomerId, ct);
// Domain query
public static Task<List<Order>> RecentOrders(int days = 7, CancellationToken ct = default) =>
Query(o => o.Created > DateTimeOffset.UtcNow.AddDays(-days), ct);
}
public record Money(decimal Amount, string Currency);
public record Address(string Street, string City, string State, string Zip);
public class Invoice : Entity<Invoice>
{
public Money Total { get; set; } = new(0m, "USD");
public Address ShippingAddress { get; set; } = new("", "", "", "");
}
public static class ProductLifecycle
{
public static void Configure(EntityLifecycleBuilder<Product> pipeline)
{
pipeline.ProtectAll()
.Allow(p => p.Price, p => p.Description)
.BeforeUpsert(async (ctx, next) =>
{
if (ctx.Entity.Price < 0)
throw new InvalidOperationException("Price cannot be negative");
await next();
})
.AfterLoad(ctx => ctx.Entity.FormattedPrice = $"${ctx.Entity.Price:F2}");
}
}
public class Todo : Entity<Todo>
{
public string UserId { get; set; } = "";
public string? CategoryId { get; set; }
// Navigation helpers
public Task<User?> GetUser(CancellationToken ct = default) =>
User.Get(UserId, ct);
public Task<Category?> GetCategory(CancellationToken ct = default) =>
string.IsNullOrEmpty(CategoryId) ? Task.FromResult<Category?>(null)
: Category.Get(CategoryId, ct);
public Task<List<TodoItem>> GetItems(CancellationToken ct = default) =>
TodoItem.Query(i => i.TodoId == Id, ct);
}
Use [Timestamp] to auto-manage creation and modification times:
public class Order : Entity<Order>
{
public string CustomerId { get; set; } = "";
public decimal Total { get; set; }
[Timestamp] // Set once on first save
public DateTimeOffset CreatedAt { get; set; }
[Timestamp(OnSave = true)] // Updated on every save
public DateTimeOffset UpdatedAt { get; set; }
}
No manual assignment needed — the framework's lifecycle hooks handle it via compiled expression trees for zero-overhead hot-path performance.
docs/guides/data-modeling.mddocs/examples/entity-pattern-recipes.mdsamples/S1.Web/ (Relationship patterns)