원클릭으로
koan-relationships
Entity navigation, batch loading, relationship best practices
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
메뉴
Entity navigation, batch loading, relationship best practices
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
SOC 직업 분류 기준
Auto-registration via KoanAutoRegistrar, minimal Program.cs, "Reference = Intent" pattern
Chat endpoints, embeddings, RAG workflows, vector search
Aggregate boundaries, relationships, lifecycle hooks, value objects
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.
| name | koan-relationships |
| description | Entity navigation, batch loading, relationship best practices |
Relationships use foreign keys + navigation helpers. Batch load to prevent N+1 queries. No ORM mapping configuration needed.
public class User : Entity<User>
{
public string Name { get; set; } = "";
public Task<List<Todo>> GetTodos(CancellationToken ct = default) =>
Todo.Query(t => t.UserId == Id, ct);
}
public class Todo : Entity<Todo>
{
public string UserId { get; set; } = "";
public Task<User?> GetUser(CancellationToken ct = default) =>
User.Get(UserId, ct);
}
// ❌ WRONG: N+1 query problem
foreach (var todo in todos)
{
var user = await todo.GetUser(); // N queries!
}
// ✅ CORRECT: Batch load
var userIds = todos.Select(t => t.UserId).Distinct().ToArray();
var users = await User.Get(userIds);
var userDict = users.Where(u => u != null).ToDictionary(u => u!.Id);
foreach (var todo in todos)
{
var user = userDict[todo.UserId];
}
public class Todo : Entity<Todo>
{
public string? CategoryId { get; set; }
public Task<Category?> GetCategory(CancellationToken ct = default) =>
string.IsNullOrEmpty(CategoryId) ? Task.FromResult<Category?>(null)
: Category.Get(CategoryId, ct);
}
public class TodoItem : Entity<TodoItem>
{
public string TodoId { get; set; } = "";
public int SortOrder { get; set; }
public Task<Todo?> GetParentTodo(CancellationToken ct = default) =>
Todo.Get(TodoId, ct);
}
public class Todo : Entity<Todo>
{
public Task<List<TodoItem>> GetItems(CancellationToken ct = default) =>
TodoItem.Query(i => i.TodoId == Id, ct)
.ContinueWith(t => t.Result.OrderBy(i => i.SortOrder).ToList());
}
.claude/skills/entity-first/examples/entity-relationships.cssamples/S1.Web/README.md (Relationship demo)