A brief overview of how FluentDynamoDB grew from early prototypes into its current form. FluentDynamoDB didn't start out as a framework. It began as a small internal utility - something to reduce the friction of working directly with the AWS SDK for DynamoDB. Instead of writing raw request objects everywhere, we wrapped them in a "fluent" interface and built some helper methods around queries, paging, and indexes.
It worked… but it was painfully verbose.
A typical query felt like writing a tiny novel:
var result = await table.Query()
.Where("pk = :pk AND begins_with(sk, :prefix)")
.WithAttribute("#pk", "pk")
.WithValue(":pk", id)
.WithValue(":prefix", prefix)
.WithFilter("#status = :status")
.WithAttribute("#status", "status")
.WithValue(":status", "pending")
.ExecuteAsync();
There was no hydration. No mapping. No guardrails.
Just a nicer way to build the underlying DynamoDB request - and that was it.
Phase 1: Source Generation - Goodbye Boilerplate
The first major breakthrough was using a C# source generator. Instead of hand-writing:
- Table classes
- Index definitions
- Field names
- Mapping methods
- Repository-style wrappers
we added attributes and interfaces to our entities and let the generator produce all of it automatically.
Suddenly, entities gained ToDynamoDb() and FromDynamoDb() methods for free.
Repositories disappeared.
Tables and indexes were strongly typed.
Half of our codebase simply evaporated.
The library still used string-based expressions and attribute collections, but the scaffolding around them became much cleaner.
Phase 2: Parameterized Expressions - Cutting the Verbosity
Next, we simplified the way expressions were written.
Instead of pairing every expression with a dozen .WithValue() and .WithAttribute() calls, we introduced C#-style formatting:
.Where("id = {0} AND sk < {1}", id, limit);
Need ISO-8601 formatting?
.Where("timestamp > {0:o}", date);
This change alone made day-to-day usage much more readable.
Phase 3: Lambda Expression Translation
The biggest leap forward came with lambda expressions.
Instead of strings:
.Where("id = {0}", id)
You could simply write:
.Where(x => x.Id == id && x.Sk < limit);
The system translates the expression tree into DynamoDB query and filter expressions, even for updates:
.Update(orderId)
.Set(x => new OrderUpdateModel {
Status = "shipped",
Version = x.Version + 1
})
.UpdateAsync();
Under the hood, this required:
- Expression tree parsing
- AoT-safe translation logic
- Recognizing DynamoDB function calls
- Generated metadata for every entity
This is where FluentDynamoDB stopped being a helper library and started becoming a true DynamoDB abstraction layer.
Phase 4: Convenience APIs & Strongly Typed Accessors
Once tables and metadata were generated, we added intuitive, strongly typed entry points:
await table.Orders.Query(x => x.Id == id).ToListAsync();
await table.Orders.DeleteAsync(orderId);
await table.Orders.PutAsync(order);
Developers can still drop back into:
- Raw expressions
- Parameterized expressions
- Full fluent builders
…but most of the time, the simplest version is enough.
Transactions and Batches Built Right In
Because the fluent builders aren't tied directly to execution, they integrate naturally into DynamoDB transactions:
await DynamoDbTransactions.Write
.Add(table.Orders.Put(item1))
.Add(table.Orders.Put(item2))
.ExecuteAsync();
The request builders transform into the correct transactional payloads automatically.
Where FluentDynamoDB Is Today
The library has transformed through necessity, iteration, and the constraints of real-world use:
- Manual tables → source-generated models
- Verbose string builders → formatted expressions
- Formatted expressions → lambda expression translation
- Raw operations → strongly typed convenience APIs
- Single requests → full transactional support
What started as a small helper has grown into a capable, expressive DynamoDB framework - one that still preserves escape hatches for power users.
But the direction is clear:
A DynamoDB experience that feels natural for C# developers - without sacrificing power, safety, or flexibility.
The library keeps evolving.