|
| 1 | +# DynamoDB Mapping Library |
| 2 | + |
| 3 | +Annotation-driven DynamoDB mapping library with automatic dependency resolution and compile-time code generation for type-safe bidirectional conversion between domain objects and DynamoDB AttributeValue format. |
| 4 | + |
| 5 | +## Features |
| 6 | + |
| 7 | +- **Single annotation approach**: Only `@DynamoMappable` needed to mark types |
| 8 | +- **Automatic dependency resolution**: Analyzes object relationships and generates proper CDI injection |
| 9 | +- **Compile-time code generation**: No runtime reflection, full Quarkus/GraalVM compatibility |
| 10 | +- **Type-safe mapping**: Compile-time validation and generation |
| 11 | +- **CDI integration**: Generated mappers are `@ApplicationScoped` beans with dependency injection |
| 12 | + |
| 13 | +## Usage |
| 14 | + |
| 15 | +### 1. Mark your domain classes with `@DynamoMappable` |
| 16 | + |
| 17 | +```java |
| 18 | +@DynamoMappable |
| 19 | +@Table(name = "routes") |
| 20 | +public class Route { |
| 21 | + private String userId; |
| 22 | + private String routeId; |
| 23 | + private String name; |
| 24 | + private List<Waypoint> waypoints; // Waypoint must also be @DynamoMappable |
| 25 | + private RouteGeometry geometry; // RouteGeometry must also be @DynamoMappable |
| 26 | + private Instant createdAt; |
| 27 | +} |
| 28 | + |
| 29 | +@DynamoMappable |
| 30 | +public class Waypoint { |
| 31 | + private double lat; |
| 32 | + private double lng; |
| 33 | + private String name; |
| 34 | +} |
| 35 | + |
| 36 | +@DynamoMappable |
| 37 | +public class RouteGeometry { |
| 38 | + private GeometryType type; |
| 39 | + private List<List<Double>> coordinates; |
| 40 | +} |
| 41 | +``` |
| 42 | + |
| 43 | +### 2. Generated mappers are automatically injected |
| 44 | + |
| 45 | +The annotation processor generates mapper classes with proper CDI dependency injection: |
| 46 | + |
| 47 | +```java |
| 48 | +@ApplicationScoped |
| 49 | +public class RouteMapper { |
| 50 | + private final WaypointMapper waypointMapper; |
| 51 | + private final RouteGeometryMapper routeGeometryMapper; |
| 52 | + |
| 53 | + public RouteMapper(WaypointMapper waypointMapper, RouteGeometryMapper routeGeometryMapper) { |
| 54 | + this.waypointMapper = waypointMapper; |
| 55 | + this.routeGeometryMapper = routeGeometryMapper; |
| 56 | + } |
| 57 | + |
| 58 | + public AttributeValue toDynamoDbAttributeValue(Route route) { /* generated */ } |
| 59 | + public Route fromDynamoDbAttributeValue(AttributeValue attributeValue) { /* generated */ } |
| 60 | +} |
| 61 | +``` |
| 62 | + |
| 63 | +### 3. Use in your repository classes |
| 64 | + |
| 65 | +```java |
| 66 | +@ApplicationScoped |
| 67 | +public class DynamoDbRoutesRepository implements RoutesRepository { |
| 68 | + |
| 69 | + private final DynamoDbClient dynamoDb; |
| 70 | + private final RouteMapper routeMapper; // Automatically injected |
| 71 | + |
| 72 | + public Route save(Route route) { |
| 73 | + Map<String, AttributeValue> item = routeMapper.toDynamoDbAttributeValue(route).m(); |
| 74 | + // ... save to DynamoDB |
| 75 | + } |
| 76 | + |
| 77 | + public Route findById(String id) { |
| 78 | + // ... get from DynamoDB |
| 79 | + return routeMapper.fromDynamoDbAttributeValue(AttributeValue.builder().m(item).build()); |
| 80 | + } |
| 81 | +} |
| 82 | +``` |
| 83 | + |
| 84 | +## Supported Field Types |
| 85 | + |
| 86 | +| Type | Strategy | Description | |
| 87 | +|------|----------|-------------| |
| 88 | +| `String` | STRING | Direct string mapping | |
| 89 | +| `Double`, `Integer`, etc. | NUMBER | Number mapping with type conversion | |
| 90 | +| `Boolean` | BOOLEAN | Boolean mapping | |
| 91 | +| `Instant` | INSTANT | ISO-8601 string representation | |
| 92 | +| `Enum` | ENUM | Enum name mapping | |
| 93 | +| `List<String>` | STRING_LIST | Native DynamoDB string set | |
| 94 | +| `List<@DynamoMappable>` | COMPLEX_LIST | List of mapped complex objects | |
| 95 | +| `@DynamoMappable` objects | COMPLEX_OBJECT | Nested object mapping via dependency injection | |
| 96 | + |
| 97 | +## Build Integration |
| 98 | + |
| 99 | +Add the annotation processor to your Maven build: |
| 100 | + |
| 101 | +```xml |
| 102 | +<dependency> |
| 103 | + <groupId>com.tourino</groupId> |
| 104 | + <artifactId>dynamodb-mapping-lib</artifactId> |
| 105 | + <version>1.0.0-SNAPSHOT</version> |
| 106 | +</dependency> |
| 107 | +``` |
| 108 | + |
| 109 | +The annotation processor runs automatically during compilation and generates mapper classes in the same package as your domain classes. |
| 110 | + |
| 111 | +## Benefits over Manual Mapping |
| 112 | + |
| 113 | +- **Reduced Code**: 256 lines of manual mapping → ~10 lines of annotations |
| 114 | +- **Type Safety**: Compile-time validation vs runtime parsing errors |
| 115 | +- **Maintainability**: New entities require only `@DynamoMappable` annotation |
| 116 | +- **Consistency**: Generated code follows same patterns |
| 117 | +- **Performance**: No reflection at runtime, optimal native compilation |
| 118 | +- **Dependency Management**: Automatic resolution of mapper dependencies |
| 119 | + |
| 120 | +## Generated Code Structure |
| 121 | + |
| 122 | +For each `@DynamoMappable` class, the processor generates: |
| 123 | + |
| 124 | +- **Mapper class**: `{ClassName}Mapper` with CDI annotations |
| 125 | +- **Bidirectional methods**: `toDynamoDbAttributeValue()` and `fromDynamoDbAttributeValue()` |
| 126 | +- **Dependency injection**: Constructor injection for required mappers |
| 127 | +- **Null safety**: Proper null handling throughout mapping logic |
| 128 | +- **Error handling**: Safe parsing with fallback to null values |
0 commit comments