skills/microservice/cosmos/cosmos-entity/SKILL.md
Use when designing Cosmos DB document entities with partition keys and discriminators.
npx skillsauth add faysilalshareef/dotnet-ai-kit cosmos-entityInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
IContainerDocument interfacePartitionKeyBuilderETag for optimistic concurrency controlid property is required by Cosmos DB (lowercase)namespace {Company}.{Domain}.Cosmos.Domain;
public interface IContainerDocument
{
string ContainerName { get; }
PartitionKey PartitionKeys { get; }
string Discriminator { get; }
string? ETag { get; set; }
}
namespace {Company}.{Domain}.Cosmos.Domain;
public sealed class SaleInvoice : IContainerDocument
{
public string id { get; set; } = string.Empty;
public string MerchantId { get; private set; } = string.Empty;
public string InvoiceNumber { get; private set; } = string.Empty;
public decimal TotalAmount { get; private set; }
public DateTime CreatedAt { get; private set; }
public List<SoldItem> Items { get; private set; } = [];
public int Sequence { get; private set; }
// IContainerDocument
public string ContainerName => "invoices";
public string Discriminator => nameof(SaleInvoice);
public string? ETag { get; set; }
public PartitionKey PartitionKeys => new PartitionKeyBuilder()
.Add(MerchantId)
.Add(CreatedAt.ToString("yyyy-MM"))
.Add(Discriminator)
.Build();
// Factory method from event
public static SaleInvoice FromInvoiceCreated(Event<InvoiceCreatedData> @event)
{
return new SaleInvoice
{
id = @event.AggregateId.ToString(),
MerchantId = @event.Data.MerchantId,
InvoiceNumber = @event.Data.InvoiceNumber,
TotalAmount = @event.Data.TotalAmount,
CreatedAt = @event.DateTime,
Items = @event.Data.Items.Select(i => new SoldItem
{
ProductId = i.ProductId,
ProductName = i.ProductName,
Quantity = i.Quantity,
UnitPrice = i.UnitPrice
}).ToList(),
Sequence = @event.Sequence
};
}
// Apply update event
public void Apply(Event<InvoiceUpdatedData> @event)
{
TotalAmount = @event.Data.TotalAmount;
Sequence = @event.Sequence;
}
}
namespace {Company}.{Domain}.Cosmos.Domain;
public sealed class SoldItem
{
public string ProductId { get; set; } = string.Empty;
public string ProductName { get; set; } = string.Empty;
public int Quantity { get; set; }
public decimal UnitPrice { get; set; }
public decimal LineTotal => Quantity * UnitPrice;
}
namespace {Company}.{Domain}.Cosmos.Domain;
public sealed class MerchantSalesReport : IContainerDocument
{
public string id { get; set; } = string.Empty;
public string MerchantId { get; private set; } = string.Empty;
public string ReportMonth { get; private set; } = string.Empty;
public decimal TotalSales { get; private set; }
public int InvoiceCount { get; private set; }
public bool IsReport => true;
public string ContainerName => "reports";
public string Discriminator => nameof(MerchantSalesReport);
public string? ETag { get; set; }
public PartitionKey PartitionKeys => new PartitionKeyBuilder()
.Add(MerchantId)
.Add(ReportMonth)
.Add(Discriminator)
.Build();
public void AddInvoice(decimal amount)
{
TotalSales += amount;
InvoiceCount++;
}
}
| Anti-Pattern | Correct Approach |
|---|---|
| Missing id property (lowercase) | Cosmos requires lowercase id |
| Single partition key | Use hierarchical partition keys (up to 3 levels) |
| Missing Discriminator | Required for polymorphic container queries |
| Public setters everywhere | Private setters with factory methods and Apply |
| Large nested arrays (unbounded) | Keep nested arrays bounded; split if > 100 items |
# Find IContainerDocument implementations
grep -r "IContainerDocument" --include="*.cs" src/
# Find PartitionKeyBuilder usage
grep -r "PartitionKeyBuilder" --include="*.cs" src/
# Find Discriminator properties
grep -r "Discriminator =>" --include="*.cs" src/
# Find factory methods
grep -r "public static.*From" --include="*.cs" src/Cosmos/Domain/
IContainerDocument interface — match the contract exactlyid lowercase — Cosmos DB requirementdata-ai
Use when about to claim work is complete, fixed, passing, or ready — before committing, creating PRs, or moving to the next task. Requires running verification commands and confirming output before making any success claims.
development
Use when encountering any bug, test failure, build error, or unexpected behavior — before proposing fixes or making changes.
development
Use when checkpointing, wrapping up, or handing off an AI-assisted development session.
development
Use when following the Specification-Driven Development lifecycle from plan through ship.