Priority: P1 - Critical
Tier: 2 - Single-Table Support
Effort: Large (12-18 hours)
Already In Codebase
- No hook discovery or invocation is generated today.
src/LayeredCraft.DynamoMapper.Generators/Templates/Mapper.scriban currently generates:
ToItem: expression-bodied returning new Dictionary<string, AttributeValue>(...).SetXxx(...).SetXxx(...)
FromItem: expression-bodied returning new T { Prop = ..., ... }
Goal
Implement lifecycle hooks for both ToItem and FromItem so users can inject single-table logic (pk/sk composition, discriminator, TTL, attribute bags) without polluting domain models.
Hooks must be optional and compile away when not implemented.
Hook signatures (final)
Hooks are declared on the mapper class:
ToItem hooks
static partial void BeforeToItem(TSource source, Dictionary<string, AttributeValue> item);
static partial void AfterToItem(TSource source, Dictionary<string, AttributeValue> item);
FromItem hooks
static partial void BeforeFromItem(Dictionary<string, AttributeValue> item);
static partial void AfterFromItem(Dictionary<string, AttributeValue> item, ref TTarget entity);
Implementation notes
Tests (must be part of this story)
Add verify tests demonstrating:
- Hook calls are emitted in correct order.
- Generated code compiles with:
- no hook implementations
- hook implementations present
Acceptance criteria
- ToItem emits Before/After calls at correct points.
- FromItem emits Before/After calls at correct points (After uses
ref).
- No hooks implemented -> no runtime overhead.
- Snapshot tests cover both directions.
Priority: P1 - Critical
Tier: 2 - Single-Table Support
Effort: Large (12-18 hours)
Already In Codebase
src/LayeredCraft.DynamoMapper.Generators/Templates/Mapper.scribancurrently generates:ToItem: expression-bodied returningnew Dictionary<string, AttributeValue>(...).SetXxx(...).SetXxx(...)FromItem: expression-bodied returningnew T { Prop = ..., ... }Goal
Implement lifecycle hooks for both
ToItemandFromItemso users can inject single-table logic (pk/sk composition, discriminator, TTL, attribute bags) without polluting domain models.Hooks must be optional and compile away when not implemented.
Hook signatures (final)
Hooks are declared on the mapper class:
ToItem hooks
static partial void BeforeToItem(TSource source, Dictionary<string, AttributeValue> item);static partial void AfterToItem(TSource source, Dictionary<string, AttributeValue> item);FromItem hooks
static partial void BeforeFromItem(Dictionary<string, AttributeValue> item);static partial void AfterFromItem(Dictionary<string, AttributeValue> item, ref TTarget entity);Implementation notes
Update
src/LayeredCraft.DynamoMapper.Generators/Templates/Mapper.scribanto block-bodied methods:ToItemshould createvar item = new Dictionary<string, AttributeValue>(capacity);Invoke
BeforeToItem(source, item)Perform all
.SetXxx(...)work (either as statements or chained intoitem = item.Set...)Invoke
AfterToItem(source, item)return item;FromItemshould:BeforeFromItem(item)AfterFromItem(item, ref entity)return entity;Decide hook emission strategy:
Tests (must be part of this story)
Add verify tests demonstrating:
Acceptance criteria
ref).