Bean & Bark: your model is the schema

View .md

In the DynoStudio series you met this table from the read side, shaping keys by hand — CUSTOMER#c-014, begins_with(sk, 'ORDER#') — and holding the design in your head. In the orders chapter the API started writing typed orders. This chapter closes the loop between them: you write the model once as annotated C#, and the keys, the constraints the table enforces, and the schema DynoStudio reads all flow from that one place.

You'll learn
  • Declare keys and a uniqueness constraint as attributes on a plain C# entity
  • Register the entity beside Order in the same [DbContext] — one physical table, many shapes
  • See DynoStudio read that same model — schema, markers, and access patterns — with nothing defined twice

Declare it, don't remember it

Instead of remembering that a customer's key is CUSTOMER#{id}, you declare it — and you mark the email unique, right on the property:

csharp
using Amazon.DynamoDBv2.DataModel;using Kanject.Core.NoSqlDatabase.Provider.DynamoDb.Abstractions.Interfaces;using Kanject.Core.NoSqlDatabase.Provider.DynamoDb.Annotations.Attributes;// The customer's profile row: pk = CUSTOMER#<id>, sk = "PROFILE" — the same// partition the customer's orders already live under.public sealed class Customer : IDynamoDbEntity{    [DynamoDBHashKey("pk")]    [KeyTemplate("CUSTOMER#{CustomerId}")]   // the partition key, declared    public string PartitionKey { get; set; } = string.Empty;    [DynamoDBRangeKey("sk")]    [KeyTemplate("PROFILE")]                 // one profile row per customer    public string SortKey { get; set; } = string.Empty;    [DynamoDBProperty] public string CustomerId { get; set; } = string.Empty;    [DynamoDBProperty] public string Name       { get; set; } = string.Empty;    [Unique("Email")]                        // one account per email — enforced    [DynamoDBProperty] public string Email      { get; set; } = string.Empty;    [DynamoDBProperty] public string Region     { get; set; } = string.Empty;}

Note what the entity does not carry: a table name. The physical table is named exactly once, in the [DbContext]Customer simply joins Order in the MapEntity call from the orders chapter, and a four-line [Repository] partial asks the generator for the data layer:

csharp
// The same context from the orders chapter — Customer joins Order in the one// physical table. This is where "BeanAndBark" is named; entities never carry// the table name themselves.[DbContext]public partial class BeanAndBarkDbContext{    protected override void OnModelCreating(EntityModelBuilder builder)        => builder.MapEntity("BeanAndBark", new Order(), new Customer());}// Four lines; the generator emits the CRUD, FindCustomerAsync — and, from the// [Unique] marker, FindCustomerByEmailAsync.[Repository(Name = "Customers", Version = 1, Entity = typeof(Customer))]public partial class CustomerRepository;

Open this in DynoStudio's Model Builder and it parses the very same file. The entity, its key templates, and the marker all appear — because the studio reads exactly the attributes you wrote. There is no second schema to define, and nothing to keep in sync.

csharp
// generated from [Unique("Email")] — the email is the argument, no query to writeCustomer? ada = await customers.FindCustomerByEmailAsync("ada@beanandbark.co");
Recap
  • On Kanject, the model is the source: keys are attributes ([KeyTemplate]), not strings you remember — and the physical table is named once, in the [DbContext]'s MapEntity, never on the entity.
  • DynoStudio reads the same annotated C# your app is built from — so its schema, markers, and access patterns are never defined twice.
  • [Unique("Email")] is one attribute that yields both a generated FindCustomerByEmailAsync and a table-enforced uniqueness constraint (a $$Unique#… reserved row).
Check yourself2
On the Kanject platform, where does DynoStudio get its understanding of your schema?
What does [Unique("Email")] on a property give you?
Try it yourself 1
1 Model the product
Add a Product entity to the same BeanAndBark table: its partition key is PRODUCT#{Sku}, and the SKU must be unique.
Same shape as Customer: an IDynamoDbEntity with [KeyTemplate]s on the key properties and [Unique(...)] on the SKU — then add new Product() to the context's MapEntity call.
Show solution
An IDynamoDbEntity with [KeyTemplate("PRODUCT#{Sku}")] on the partition key and [Unique("Sku")] on the SKU — which generates FindProductBySkuAsync and guarantees no two products share a SKU. It joins the table via the same MapEntity call: builder.MapEntity("BeanAndBark", new Order(), new Customer(), new Product()).
csharp
public sealed class Product : IDynamoDbEntity{    [DynamoDBHashKey("pk")]    [KeyTemplate("PRODUCT#{Sku}")] public string PartitionKey { get; set; } = string.Empty;    [DynamoDBRangeKey("sk")]    [KeyTemplate("PROFILE")]       public string SortKey { get; set; } = string.Empty;    [Unique("Sku")]    [DynamoDBProperty] public string Sku  { get; set; } = string.Empty;    [DynamoDBProperty] public string Name { get; set; } = string.Empty;}
Was this page helpful?