Bean & Bark: your model is the schema
In Part 1 you shaped keys by hand — CUSTOMER#c-014, begins_with(sk, 'ORDER#') — and held the whole design in your head. That works for anyone with a DynamoDB table. This is Part 2: you're not just querying Bean & Bark anymore, you're building it on the Kanject platform — where you write the model once as annotated C#, and the keys, the schema DynoStudio reads, and (next chapter) your data-access code all flow from that one place.
- Scaffold a Kanject service and its dev / stage / prod stages with the
kanjectCLI - Declare keys and a uniqueness constraint as attributes on a plain C# class
- See DynoStudio read that same model — schema, markers, and access patterns — with nothing defined twice
One command, one model
kanject new scaffolds the service from a template; kanject init writes its manifest and one stage file per environment — the same dev / stage / prod you learned to respect in Part 1, now created for you:
# scaffold the service from a template, then wire its stageskanject new kbs-lambda-netcore-api --name bean-and-barkkanject init --stages dev,stage,prod --region eu-west-1 Now the model. Instead of remembering that a customer's key is CUSTOMER#{id}, you declare it — and you mark the email unique, right on the property:
using Kanject.Core.NoSqlDatabase.Provider.DynamoDb.Annotations.Attributes;[Table("BeanAndBark")]public class Customer{ [KeyTemplate("CUSTOMER#{CustomerId}")] // the partition key, declared public string Pk { get; set; } [KeyTemplate("PROFILE")] // one profile row per customer public string Sk { get; set; } public string CustomerId { get; set; } public string Name { get; set; } [Unique("Email")] // one account per email — enforced public string Email { get; set; } public string Region { get; set; }} 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.
// generated from [Unique("Email")] — the email is the argument, no query to writeCustomer? ada = await customers.FindCustomerByEmailAsync("ada@beanandbark.co"); - On Kanject, the model is the source:
kanject initscaffolds the service and its dev/stage/prod stages, and keys are attributes ([KeyTemplate]), not strings you remember. - 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 generatedFindCustomerByEmailAsyncand a table-enforced uniqueness constraint (a$$Unique#…reserved row).
[Unique("Email")] on a property give you?Product entity to the same BeanAndBark table: its partition key is PRODUCT#{Sku}, and the SKU must be unique.Customer: a [Table("BeanAndBark")] class, a [KeyTemplate] on the key property, and [Unique(...)] on the SKU.Show solution
[Table("BeanAndBark")] class with [KeyTemplate("PRODUCT#{Sku}")] on the key and [Unique("Sku")] on the SKU — which generates FindProductBySkuAsync and guarantees no two products share a SKU.[Table("BeanAndBark")]public class Product{ [KeyTemplate("PRODUCT#{Sku}")] public string Pk { get; set; } [KeyTemplate("PROFILE")] public string Sk { get; set; } [Unique("Sku")] public string Sku { get; set; } public string Name { get; set; }}