Skip to content

Commit 352b988

Browse files
authored
Feature/sample operations (#83)
* feat(operation-samples): Add comprehensive DynamoDB operation samples showcase - Add OperationSamples project with compilable code examples demonstrating FluentDynamoDb API usage - Create Order and OrderLine domain models with single-table design pattern - Implement sample files for all DynamoDB operations: Get, Put, Update, Delete, Query, Scan, BatchGet, BatchWrite, TransactionGet, TransactionWrite - Each operation sample includes four implementation patterns: Raw AWS SDK, FluentDynamoDb manual builder, formatted strings, and lambda expressions - Add design specification and requirements documentation for the operation samples showcase - Provide side-by-side comparisons showing verbosity reduction and API improvements over raw AWS SDK - Enable use of samples for screenshots and video presentations demonstrating FluentDynamoDb capabilities * feat(operation-samples): Refine samples for full equivalency with domain model conversions - Update RawSdk methods to manually convert AWS SDK responses to domain models - Add manual conversion from AttributeValue dictionaries in BatchGetSamples - Implement ReturnValues in DeleteSamples to return deleted Order entity - Add manual conversion from AttributeValue dictionaries in GetSamples - Implement ReturnValues in PutSamples to return created Order entity - Add manual conversion from AttributeValue dictionaries in QuerySamples - Add manual conversion from AttributeValue dictionaries in ScanSamples - Implement ReturnValues in TransactionGetSamples for domain model returns - Implement ReturnValues in TransactionWriteSamples for domain model returns - Create OrderUpdateModel class for lambda expression Set() operations - Update FluentLambdaUpdateAsync to use Set(x => new OrderUpdateModel { ... }) syntax - Update FluentLambdaTransactionWriteAsync to use proper lambda expressions with conditions - Verify express-route methods are used in FluentLambda samples - Mark task 7 and all subtasks as complete in operation-samples-showcase specification - Ensures full equivalency between RawSdk and Fluent approaches for demonstration purposes
1 parent 82b0934 commit 352b988

File tree

18 files changed

+1815
-0
lines changed

18 files changed

+1815
-0
lines changed
Lines changed: 278 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,278 @@
1+
# Design Document: Operation Samples Showcase
2+
3+
## Overview
4+
5+
This project provides a standalone .NET 8 class library containing compilable code samples that demonstrate FluentDynamoDb API usage compared to the raw AWS SDK for DynamoDB. The samples are designed for screenshots and video presentations, showing the verbosity reduction and API improvements offered by FluentDynamoDb.
6+
7+
The project uses a simple Order/OrderLine domain with a single-table design pattern. Each DynamoDB operation type has a dedicated sample file containing four methods demonstrating different coding styles:
8+
1. Raw AWS SDK with explicit AttributeValue dictionaries
9+
2. FluentDynamoDb manual builder with WithAttribute()/WithValue()
10+
3. FluentDynamoDb formatted string with {0}, {1} placeholders
11+
4. FluentDynamoDb lambda expressions with entity accessors
12+
13+
## Architecture
14+
15+
```
16+
examples/
17+
└── OperationSamples/
18+
├── OperationSamples.csproj
19+
├── Models/
20+
│ ├── Order.cs
21+
│ ├── OrderLine.cs
22+
│ └── OrdersTable.cs
23+
└── Samples/
24+
├── GetSamples.cs
25+
├── PutSamples.cs
26+
├── UpdateSamples.cs
27+
├── DeleteSamples.cs
28+
├── QuerySamples.cs
29+
├── ScanSamples.cs
30+
├── TransactionGetSamples.cs
31+
├── TransactionWriteSamples.cs
32+
├── BatchGetSamples.cs
33+
└── BatchWriteSamples.cs
34+
```
35+
36+
The project is located under the `examples/` folder alongside other example projects (InvoiceManager, StoreLocator, TodoList, TransactionDemo).
37+
38+
### DynamoDB Table Schema
39+
40+
Single-table design with the following key structure:
41+
- **Table Name**: `Orders`
42+
- **Partition Key (pk)**: `ORDER#{OrderId}`
43+
- **Sort Key (sk)**:
44+
- `META` for Order metadata
45+
- `LINE#{LineId}` for OrderLine items
46+
47+
### Sample Method Naming Convention
48+
49+
Each sample file contains a static class with four methods following this pattern:
50+
- `RawSdk{Operation}Async` - Direct AWS SDK usage
51+
- `FluentManual{Operation}Async` - FluentDynamoDb with manual expressions
52+
- `FluentFormatted{Operation}Async` - FluentDynamoDb with format strings
53+
- `FluentLambda{Operation}Async` - FluentDynamoDb with lambda expressions
54+
55+
## Components and Interfaces
56+
57+
### Models
58+
59+
#### Order Entity
60+
```csharp
61+
[DynamoDbEntity]
62+
public partial class Order : IDynamoDbEntity
63+
{
64+
[PartitionKey]
65+
public string Pk { get; set; } // "ORDER#{OrderId}"
66+
67+
[SortKey]
68+
public string Sk { get; set; } // "META"
69+
70+
public string OrderId { get; set; }
71+
public string CustomerId { get; set; }
72+
public DateTime OrderDate { get; set; }
73+
public string Status { get; set; } // "Pending", "Shipped", "Delivered"
74+
public decimal TotalAmount { get; set; }
75+
}
76+
```
77+
78+
#### OrderLine Entity
79+
```csharp
80+
[DynamoDbEntity]
81+
public partial class OrderLine : IDynamoDbEntity
82+
{
83+
[PartitionKey]
84+
public string Pk { get; set; } // "ORDER#{OrderId}"
85+
86+
[SortKey]
87+
public string Sk { get; set; } // "LINE#{LineId}"
88+
89+
public string LineId { get; set; }
90+
public string ProductId { get; set; }
91+
public string ProductName { get; set; }
92+
public int Quantity { get; set; }
93+
public decimal UnitPrice { get; set; }
94+
}
95+
```
96+
97+
#### OrdersTable
98+
```csharp
99+
public partial class OrdersTable : DynamoDbTableBase
100+
{
101+
public OrdersTable(IAmazonDynamoDB client) : base(client, "Orders") { }
102+
103+
// Generated entity accessors:
104+
// public OrderAccessor Orders { get; }
105+
// public OrderLineAccessor OrderLines { get; }
106+
}
107+
```
108+
109+
#### OrderUpdateModel (for lambda expression updates)
110+
```csharp
111+
/// <summary>
112+
/// Update model for Order entity used with lambda expression Set() operations.
113+
/// </summary>
114+
public class OrderUpdateModel
115+
{
116+
[DynamoDbAttribute("orderStatus")]
117+
public string? Status { get; set; }
118+
119+
[DynamoDbAttribute("modifiedAt")]
120+
public DateTime? ModifiedAt { get; set; }
121+
122+
[DynamoDbAttribute("totalAmount")]
123+
public decimal? TotalAmount { get; set; }
124+
}
125+
```
126+
127+
### Sample File Structure
128+
129+
Each sample file follows this structure:
130+
```csharp
131+
namespace FluentDynamoDb.OperationSamples.Samples;
132+
133+
public static class {Operation}Samples
134+
{
135+
// Returns AWS SDK response, then converts to domain model for equivalency
136+
public static async Task<TEntity> RawSdk{Operation}Async(IAmazonDynamoDB client, ...) { }
137+
public static async Task<TEntity> FluentManual{Operation}Async(OrdersTable table, ...) { }
138+
public static async Task<TEntity> FluentFormatted{Operation}Async(OrdersTable table, ...) { }
139+
public static async Task<TEntity> FluentLambda{Operation}Async(OrdersTable table, ...) { }
140+
}
141+
```
142+
143+
### Lambda Expression Patterns
144+
145+
FluentLambda methods should demonstrate the full power of lambda expressions:
146+
147+
#### Update Operations
148+
```csharp
149+
// Use Set(x => new UpdateModel { ... }) syntax
150+
await table.Orders.Update(pk, sk)
151+
.Set(x => new OrderUpdateModel
152+
{
153+
Status = newStatus,
154+
ModifiedAt = modifiedAt
155+
})
156+
.UpdateAsync();
157+
```
158+
159+
#### Condition Expressions
160+
```csharp
161+
// Use AttributeExists/AttributeNotExists via lambda
162+
await table.Orders.Put(order)
163+
.Where(x => x.Pk.AttributeNotExists())
164+
.PutAsync();
165+
166+
await table.Orders.Update(pk, sk)
167+
.Where(x => x.Pk.AttributeExists())
168+
.Set(x => new OrderUpdateModel { Status = newStatus })
169+
.UpdateAsync();
170+
```
171+
172+
#### Express-Route Methods
173+
```csharp
174+
// Use express-route methods instead of builder chains
175+
await table.Orders.PutAsync(order); // NOT: Put(order).PutAsync()
176+
var order = await table.Orders.GetAsync(pk, sk); // NOT: Get(pk, sk).GetItemAsync()
177+
await table.Orders.DeleteAsync(pk, sk); // NOT: Delete(pk, sk).DeleteAsync()
178+
```
179+
180+
### Response Handling Pattern
181+
182+
Raw SDK methods should demonstrate full equivalency by converting responses to domain models:
183+
184+
```csharp
185+
public static async Task<Order?> RawSdkGetAsync(IAmazonDynamoDB client, string orderId)
186+
{
187+
var response = await client.GetItemAsync(request);
188+
189+
if (response.Item == null || response.Item.Count == 0)
190+
return null;
191+
192+
// Manual conversion to show equivalency
193+
return new Order
194+
{
195+
Pk = response.Item["pk"].S,
196+
Sk = response.Item["sk"].S,
197+
OrderId = response.Item["orderId"].S,
198+
// ... other properties
199+
};
200+
}
201+
```
202+
203+
## Data Models
204+
205+
### Key Patterns
206+
207+
| Entity | Partition Key | Sort Key |
208+
|--------|--------------|----------|
209+
| Order | `ORDER#{OrderId}` | `META` |
210+
| OrderLine | `ORDER#{OrderId}` | `LINE#{LineId}` |
211+
212+
### Sample Data Values
213+
214+
For consistency across samples:
215+
- OrderId: `"ORD-001"`, `"ORD-002"`
216+
- LineId: `"LN-001"`, `"LN-002"`
217+
- CustomerId: `"CUST-123"`
218+
- Status values: `"Pending"`, `"Shipped"`, `"Delivered"`
219+
220+
## Correctness Properties
221+
222+
*A property is a characteristic or behavior that should hold true across all valid executions of a system-essentially, a formal statement about what the system should do. Properties serve as the bridge between human-readable specifications and machine-verifiable correctness guarantees.*
223+
224+
Based on the prework analysis, most requirements for this showcase project are structural/stylistic and not amenable to property-based testing. The primary testable property is:
225+
226+
### Property 1: Sample File Method Structure
227+
*For any* sample file in the Samples folder, the file SHALL contain exactly four public static async Task methods with names matching the patterns: `RawSdk*Async`, `FluentManual*Async`, `FluentFormatted*Async`, and `FluentLambda*Async`.
228+
229+
**Validates: Requirements 1.1**
230+
231+
### Verification Examples (Not Properties)
232+
233+
The following requirements are verified through specific examples rather than universal properties:
234+
235+
- **Build Verification**: Running `dotnet build` produces exit code 0 (Requirements 3.1)
236+
- **Project Configuration**: The csproj contains `<TargetFramework>net8.0</TargetFramework>` and `<Nullable>enable</Nullable>` (Requirements 3.2, 3.3)
237+
- **Model Files Exist**: `Order.cs` and `OrderLine.cs` exist in Models folder (Requirements 4.1)
238+
- **Namespace Consistency**: All files use `FluentDynamoDb.OperationSamples` namespace (Requirements 4.4)
239+
- **Sample Files Exist**: All 10 sample files exist (Requirements 5.1-5.10)
240+
241+
## Error Handling
242+
243+
Since this is a showcase project that doesn't execute against real AWS resources, error handling is minimal:
244+
245+
- Methods may throw `NotImplementedException` only if absolutely necessary for compilation (avoided per requirements)
246+
- No try-catch blocks needed as code is for demonstration only
247+
- AWS SDK exceptions are not handled as the code won't execute
248+
249+
## Testing Strategy
250+
251+
### Dual Testing Approach
252+
253+
Given the nature of this showcase project (compilable but non-executable code), testing focuses on:
254+
255+
#### Unit Tests
256+
- Verify project compiles successfully with `dotnet build`
257+
- Verify all expected files exist in the correct locations
258+
- Verify namespace consistency across all files
259+
260+
#### Property-Based Tests
261+
262+
**Property-Based Testing Library**: FsCheck with xUnit integration
263+
264+
**Property 1 Implementation**: Sample File Method Structure
265+
```csharp
266+
// Feature: operation-samples-showcase, Property 1: Sample File Method Structure
267+
// Validates: Requirements 1.1
268+
[Property]
269+
public Property AllSampleFilesHaveFourMethods()
270+
{
271+
// For each sample file, verify it contains exactly 4 methods
272+
// with the correct naming patterns
273+
}
274+
```
275+
276+
### Test Configuration
277+
- Property tests run minimum 100 iterations
278+
- Tests tagged with feature and property references per design requirements
Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
# Requirements Document
2+
3+
## Introduction
4+
5+
This document specifies requirements for a standalone .NET 8 example project that demonstrates FluentDynamoDb API usage compared to the raw AWS SDK for DynamoDB. The project's sole purpose is to provide clean, compilable code samples suitable for screenshots and video presentations. The code must compile but does not need to execute against real AWS resources.
6+
7+
## Glossary
8+
9+
- **FluentDynamoDb**: The Oproto.FluentDynamoDb library providing fluent-style API wrappers for DynamoDB operations
10+
- **Raw SDK**: Direct usage of AWSSDK.DynamoDBv2 without any abstraction layers
11+
- **Manual Builder**: FluentDynamoDb style using explicit expression strings with WithAttribute() and WithValue() methods
12+
- **Formatted String**: FluentDynamoDb style using C# string interpolation with positional placeholders like {0}, {1}
13+
- **Lambda Mode**: FluentDynamoDb style using strongly-typed lambda expressions and entity accessors
14+
- **Entity Accessor**: Generated type-safe property on a table class providing direct access to entity operations (e.g., table.Orders)
15+
- **Operation Sample**: A set of four methods demonstrating the same DynamoDB operation in different coding styles
16+
17+
## Requirements
18+
19+
### Requirement 1
20+
21+
**User Story:** As a developer evaluating FluentDynamoDb, I want to see side-by-side code comparisons for each DynamoDB operation type, so that I can understand the verbosity reduction and API improvements.
22+
23+
#### Acceptance Criteria
24+
25+
1. WHEN a sample file is created for an operation type THEN the System SHALL include exactly four public static async methods: RawSdk, FluentManual, FluentFormatted, and FluentLambda variants
26+
2. WHEN the Raw SDK method is implemented THEN the System SHALL use only AWSSDK.DynamoDBv2 types with explicit AttributeValue dictionaries and string-based expressions
27+
3. WHEN the FluentManual method is implemented THEN the System SHALL use FluentDynamoDb builders with WithAttribute() and WithValue() method calls
28+
4. WHEN the FluentFormatted method is implemented THEN the System SHALL use FluentDynamoDb builders with positional placeholder strings like {0}, {1}
29+
5. WHEN the FluentLambda method is implemented THEN the System SHALL use strongly-typed lambda expressions with entity accessor properties, including Set(x => new UpdateModel { ... }) for updates and Where(x => x.Property.AttributeNotExists()) for conditions
30+
31+
### Requirement 2
32+
33+
**User Story:** As a presenter creating screenshots, I want each sample method to be concise and focused, so that the code fits well in presentation slides.
34+
35+
#### Acceptance Criteria
36+
37+
1. WHEN a standard operation sample method is written THEN the System SHALL contain between 5 and 20 lines of code
38+
2. WHEN a transaction or batch operation sample is written THEN the System SHALL show the full verbose Raw SDK implementation without helper methods to maximize the contrast
39+
3. WHEN sample methods are written THEN the System SHALL avoid TODO comments and placeholder implementations
40+
4. WHEN sample methods are written THEN the System SHALL use minimal inline comments only where they clarify approach differences
41+
42+
### Requirement 8
43+
44+
**User Story:** As a developer comparing approaches, I want the Raw SDK methods to return actual AWS SDK responses and the Fluent methods to return equivalent domain models, so that I can see full equivalency between approaches.
45+
46+
#### Acceptance Criteria
47+
48+
1. WHEN a Raw SDK method returns a response THEN the System SHALL include manual conversion of the response back to the domain model to demonstrate full equivalency
49+
2. WHEN a Fluent method returns a result THEN the System SHALL return the same domain model type as the converted Raw SDK response
50+
51+
### Requirement 9
52+
53+
**User Story:** As a developer learning FluentDynamoDb, I want the lambda expression samples to demonstrate the full power of lambda expressions including AttributeExists and AttributeNotExists conditions, so that I understand the type-safe condition capabilities.
54+
55+
#### Acceptance Criteria
56+
57+
1. WHEN a FluentLambda Update method is implemented THEN the System SHALL use Set(x => new UpdateModel { ... }) syntax with lambda expressions
58+
2. WHEN a FluentLambda method includes a condition THEN the System SHALL use Where(x => x.Property.AttributeExists()) or Where(x => x.Property.AttributeNotExists()) syntax where appropriate
59+
3. WHEN a FluentLambda method uses entity accessors THEN the System SHALL use express-route methods like PutAsync(entity), GetAsync(key), and DeleteAsync(key) instead of builder chains like Put(entity).PutAsync()
60+
61+
### Requirement 3
62+
63+
**User Story:** As a developer, I want the sample project to compile successfully, so that I can verify the code examples are syntactically correct.
64+
65+
#### Acceptance Criteria
66+
67+
1. WHEN the project is built with `dotnet build` THEN the System SHALL compile without errors
68+
2. WHEN the project references FluentDynamoDb THEN the System SHALL use the local project reference from the solution
69+
3. WHEN the project is structured THEN the System SHALL target .NET 8.0 and use nullable reference types
70+
71+
### Requirement 4
72+
73+
**User Story:** As a documentation maintainer, I want a consistent domain model across all samples, so that viewers can follow the examples without confusion.
74+
75+
#### Acceptance Criteria
76+
77+
1. WHEN domain models are defined THEN the System SHALL include Order and OrderLine entities at minimum
78+
2. WHEN the DynamoDB table schema is represented THEN the System SHALL use a single-table design with pk="ORDER#{OrderId}" and sk="META" or "LINE#{LineId}"
79+
3. WHEN entity properties are defined THEN the System SHALL include realistic fields without excessive complexity
80+
4. WHEN the namespace is defined THEN the System SHALL use FluentDynamoDb.OperationSamples consistently
81+
82+
### Requirement 5
83+
84+
**User Story:** As a developer reviewing samples, I want all ten DynamoDB operation types covered, so that I have comprehensive reference material.
85+
86+
#### Acceptance Criteria
87+
88+
1. WHEN sample files are created THEN the System SHALL include GetSamples.cs for GetItem operations
89+
2. WHEN sample files are created THEN the System SHALL include PutSamples.cs for PutItem operations
90+
3. WHEN sample files are created THEN the System SHALL include UpdateSamples.cs for UpdateItem operations
91+
4. WHEN sample files are created THEN the System SHALL include DeleteSamples.cs for DeleteItem operations
92+
5. WHEN sample files are created THEN the System SHALL include QuerySamples.cs for Query operations
93+
6. WHEN sample files are created THEN the System SHALL include ScanSamples.cs for Scan operations
94+
7. WHEN sample files are created THEN the System SHALL include TransactionGetSamples.cs for TransactGetItems operations
95+
8. WHEN sample files are created THEN the System SHALL include TransactionWriteSamples.cs for TransactWriteItems operations
96+
9. WHEN sample files are created THEN the System SHALL include BatchGetSamples.cs for BatchGetItem operations
97+
10. WHEN sample files are created THEN the System SHALL include BatchWriteSamples.cs for BatchWriteItem operations
98+
99+
### Requirement 6
100+
101+
**User Story:** As a presenter, I want the formatted string examples to demonstrate date formatting capabilities, so that viewers understand the full power of the format string approach.
102+
103+
#### Acceptance Criteria
104+
105+
1. WHEN a formatted string sample involves date values THEN the System SHALL demonstrate the {0:o} ISO 8601 format specifier where appropriate
106+
2. WHEN formatted string samples are written THEN the System SHALL show how placeholders eliminate manual ExpressionAttributeValues management
107+
108+
### Requirement 7
109+
110+
**User Story:** As a developer, I want transaction and batch samples to show multi-entity operations, so that I understand how FluentDynamoDb handles complex scenarios.
111+
112+
#### Acceptance Criteria
113+
114+
1. WHEN transaction write samples are implemented THEN the System SHALL demonstrate operations across multiple entity types in a single transaction
115+
2. WHEN batch operation samples are implemented THEN the System SHALL demonstrate processing multiple items of different types
116+
3. WHEN the Raw SDK transaction/batch samples are written THEN the System SHALL avoid helper methods to show the full verbosity contrast

0 commit comments

Comments
 (0)