1**Design:** New Feature, **Status:** 2[In Development](../../../../../README.md) 3 4## Tenets (unless you know better ones) 5 61. Meeting customers in their problem space allows them to deliver value 7 quickly. 82. Meeting customer expectations drives usability. 93. Discoverability drives usage. 104. Providing a Java-focused experience for DynamoDB reduces the coding 11 effort required to integrate with DynamoDB. 125. Reusing the same nouns and verbs as the generated DynamoDB client 13 meets customer expectations. 146. Optimizing for cold-start performance allows customers the 15 convenience of using object mapping in a Lambda environment. 16 17## Problem 18 19Customers on the AWS SDK for Java 2.x currently use the `DynamoDbClient` 20to communicate with DynamoDB. This client is generated from the model 21provided by the DynamoDB team. 22 23Because this client is generated, it does not provide an idiomatic Java 24experience. For example: (1) the client represents numbers as `String` 25instead of the more idiomatic `Number`, (2) customers must manually 26convert common Java data types like `Instant` into types supported by 27DynamoDB, (3) customers that represent their DynamoDB objects using Java 28objects must manually convert these objects into the item representation 29supported by DynamoDB. 30 31## Existing Solutions 32 33This problem is not currently addressed directly in the AWS SDK for Java 342.x by any known third-party tool. In 1.11.x, several solutions exist, 35including AWS's own Document and Mapper Clients. 36 37## Proposed Solution 38 39The AWS SDK for Java will add a new "enhanced DynamoDB client" that 40provides an alternative to the data-access portion of the generated 41DynamoDB APIs. Control-plane operations like "create table" will not be 42supported at launch, but may be added at a later time. 43 44This enhanced client will make DynamoDB easier to use for Java customers 45by: 461. Supporting conversions between Java objects and DynamoDB items 472. Supporting conversions between Java built-in types (eg. `Instant`) 48 and DynamoDB attribute value types 493. Directly supporting every data-plane operation of DynamoDB 504. Using the same verbs and nouns of DynamoDB 51 52## Implementation Overview 53 54**New Clients** 55 56Two new client classes will be added: 57`DynamoDbEnhancedClient` and `DynamoDbEnhancedAsyncClient`. These 58classes act as a wrapper around the generated `DynamoDbClient` and 59`DynamoDbAsyncClient` classes, to provide additional functionality on 60top of that which can be provided by the generated clients. 61 62```java 63DynamoDbEnhancedClient enhancedClient = 64 DynamoDbEnhancedClient.builder() 65 .dynamoDbClient(DynamoDbClient.create()) 66 .build(); 67``` 68 69**Table Abstraction** 70 71`DynamoDbEnhancedClient` provides access to `Table` and `MappedTable`, 72and `DynamoDbEnhancedAsyncClient`provides access to `AsyncTable`, and 73`AsyncMappedTable` abstractions. 74 75The operations on these "tables" match the data-plane operations in the 76low-level DynamoDB client. For example, because `DynamoDbClient.putItem` 77exists, `Table.putItem` will also exist. 78 79`Table` and `AsyncTable` work with "items", described below. 80`MappedTable` and `AsyncMappedTable` work with "objects", described 81below. `Table` and `MappedTable` returning results synchronously, and 82`AsyncTable` and `AsyncMappedTable` returning results asynchronously. 83 84```java 85Table booksTable = enhancedClient.table("books"); 86booksTable.putItem(...); 87 88MappedTable mappedBooksTable = enhancedClient.mappedTable("books"); 89mappedBooksTable.putItem(...); 90``` 91 92**Item Abstraction** 93 94The operations on `Table` and `AsyncTable` work on `Item`s. An `Item` is 95a user-friendly representation of the generated `Map<String, 96AttributeValue>`. `Item`s support automatic type conversion between Java 97built-in types and DynamoDB-specific `AttributeValue` types. 98 99```java 100booksTable.putItem(Item.builder() 101 .putAttribute("isbn", "0-330-25864-8") 102 .putAttribute("title", "The Hitchhiker's Guide to the Galaxy") 103 .putAttribute("creationDate", Instant.now()) 104 .build()); 105``` 106 107The `Table` and `AsyncTable` abstractions can be seen as a replacement 108for the 1.11.x DynamoDB Document client. 109 110**Object Abstraction** 111 112The operations on `MappedTable` and `AsyncMappedTable` work on Java 113objects (at launch, Java beans). These objects are automatically 114converted by the enhanced client to the generated `Map<String, 115AttributeValue>`. It's likely that the `MappedTable` and 116`AsyncMappedTable` will use the `Table` and `AsyncTable` as an 117implementation detail. 118 119```java 120Book book = new Book(); 121book.setIsbn("0-330-25864-8"); 122book.setTitle("The Hitchhiker's Guide to the Galaxy"); 123book.setCreationDate(Instant.now()); 124mappedBooksTable.putItem(book); 125``` 126 127The `MappedTable` and `AsyncMappedTable` abstractions can be seen as a 128replacement for the 1.11.x DynamoDB Mapper client. 129 130**Type Conversion** 131 132The core feature of the mapper is the ability to convert common Java 133structures (e.g. Java beans) and types (e.g. `Instant`, `Number`) into 134DynamoDB attribute values. 135 136These conversions are performed based on the types specified by the 137customer. For example, the SDK will automatically convert any `Number` 138types specified by the customer (as an Item attribute) into a DynamoDB 139number. 140 141The customer has the ability to configure the type converters used at 142the `Item` or `DynamoDbEnhanced[Async]Client`-level. This allows the 143customer to add support for unsupported types, change the DynamoDB type 144associated with a Java type (e.g. storing an `Instant` as a DynamoDB 145string instead of a number), or to add support for custom POJO 146conversion logic (i.e. other than Java beans). This also allows the 147customer to provide a hard-coded converter for a specific object type 148that performs more efficiently than the built-in reflection-based object 149converter. 150 151## Features 152 153**Launch Features** 154 155These features are intended for inclusion at launch of the library. 156 1571. Support for all existing data plane operations: get, put, query, 158 update, scan, delete, batch get, batch put, transaction get, and 159 transaction put. 1602. Support for `[Async]Table` and `[Async]MappedTable`, as described 161 above. 1623. Support for bean-based representations in `[Async]MappedTable`. 1634. Type converters for all Java built-in types that are currently 164 supported by [Joda Convert](https://www.joda.org/joda-convert/). 165 166| API | Feature | Development | Usability Study | 167| --- | --- | --- | --- | 168| Item | Get | Done | | 169| | Put | Done | | 170| | Query | | | 171| | Update | | | 172| | Scan | | | 173| | Delete | | | 174| | Batch Get | | | 175| | Batch Put | | | 176| | Transaction Get | | | 177| | Transaction Put | | | 178| Object | Get | | | 179| | Put | | | 180| | Query | | | 181| | Update | | | 182| | Scan | | | 183| | Delete | | | 184| | Batch Get | | | 185| | Batch Put | | | 186| | Transaction Get | | | 187| | Transaction Put | | | 188| All | Type Support | In Progress | | 189 190**Post-Launch Features** 191 1921. Support for inheritance in `[Async]MappedTable`. 1932. Support for immutable objects in `[Async]MappedTable`. 1943. Support for projection statements in `[Async]Table` and 195 `[Async]MappedTable`. 1964. Support for DynamoDB-provided API metrics (e.g. consumed capacity). 1975. A `software.amazon.aws:dynamodb-all` module that automatically 198 includes all AWS DynamoDB artifacts, to enhance client 199 discoverability. 200 201**Missing Features** 202 203These features are not intended for inclusion at launch of the library 204(but may be added at a future time). 205 2061. Support for control-plane operations, like create or delete table. 207 *Justification for exclusion:* For testing purposes, this can be done 208 through the AWS console or low-level SDK. For production purposes, 209 this should be done through the AWS CDK or cloud formation. 2102. Versioning and UUID annotations. *Justification for exclusion:* This 211 is a higher-level concern than the "type converter" goal that the 212 enhanced client is attempting to deliver on. This is a piece of 213 functionality that will be built on-top of the enhanced client, not 214 in it. 215 216**Requested Features** 217 218* [Immutable classes](https://github.com/aws/aws-sdk-java-v2/issues/35#issuecomment-315049138) 219* [Getter/setter-less fields](https://github.com/aws/aws-sdk-java/issues/547) 220* [Replace `PaginatedList` with `Stream`](https://github.com/aws/aws-sdk-java-v2/issues/35#issuecomment-318051305) 221* [Allow 'setters' and 'getters' to support different types](https://github.com/aws/aws-sdk-java-v2/issues/35#issuecomment-318792534) 222* [Have 'scan' respect the table's read throughput](https://github.com/aws/aws-sdk-java-v2/issues/35#issuecomment-329007523) 223* [Allow creating a table with an LSI that projects all attributes](https://github.com/aws/aws-sdk-java/issues/214#issue-31304615) 224* [Projection expressions in 'load' and 'batchLoad'](https://github.com/aws/aws-sdk-java/issues/527) 225* [New condition expressions](https://github.com/aws/aws-sdk-java/issues/534) 226* [Accessing un-modeled/dynamic attributes in a POJO](https://github.com/aws/aws-sdk-java/issues/674) 227* [Inheritance](https://github.com/aws/aws-sdk-java/issues/832) 228* [Service-side metrics](https://github.com/aws/aws-sdk-java/issues/953) 229 ([1](https://github.com/aws/aws-sdk-java/issues/1170), 230 [2](https://github.com/aws/aws-sdk-java-v2/issues/703), 231 [3](https://github.com/aws/aws-sdk-java-v2/issues/35#issuecomment-417656448)) 232* [Merging DynamoDB mapper configurations](https://github.com/aws/aws-sdk-java/issues/1201) 233* [Cache merged DynamoDB mapper configurations](https://github.com/aws/aws-sdk-java/issues/1235) 234* [Create one single type converter interface](https://github.com/aws/aws-sdk-java-v2/issues/35#issuecomment-330616648) 235* [Support `@DynamoDBGeneratedUuid` in objects nested within lists](https://github.com/aws/aws-sdk-java-v2/issues/35#issuecomment-332958299) 236* [Allow annotating fields in addition to methods](https://github.com/aws/aws-sdk-java-v2/issues/35#issuecomment-332968651) 237* [Non-string keys in maps](https://github.com/aws/aws-sdk-java-v2/issues/35#issuecomment-332974427) 238* [Multiple conditions on the same attribute, for save/delete](https://github.com/aws/aws-sdk-java-v2/issues/35#issuecomment-342586344) 239* [Persisting public getters from package-private classes](https://github.com/aws/aws-sdk-java-v2/issues/35#issuecomment-343006566) 240* [Return modified attributes when doing a save](https://github.com/aws/aws-sdk-java-v2/issues/35#issuecomment-417656448) 241* [More direct exposure of scan or filter expressions](https://github.com/aws/aws-sdk-java-v2/issues/35#issuecomment-430993224) 242* [Transactions support](https://github.com/aws/aws-sdk-java-v2/issues/35#issuecomment-443308198) 243* [Creating an Item from JSON (and vice-versa)](https://github.com/aws/aws-sdk-java-v2/issues/1240) 244* Straight-forward support for multiple classes in a single table (as 245 per 246 [here](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/bp-general-nosql-design.html)) 247 (from email) 248* Support for `Optional` (from email) 249* Support for `Publisher` for async paginated responses (from email) 250* Create a table with partial projections (from email) 251* Better integration with DynamoDB streams (from email) 252* Configuring table auto-scaling when a table is created (from email) 253* Request-level credentials (from email) 254* Wrappers for transactional isolation (from email) 255* Dynamic attributes - ones with different types depending on the value 256 of other attributes, or attributes with names that are generated at 257 runtime (from email) 258* Structure versioning (from email) 259 260## Appendix A: Alternative Solutions 261 262### Alternative Solution 1: Level 3 Storage Library 263 264A "Level 2" high-level library is a service-specific library built on 265top of the "Level 1" generated client. The solution proposed above is a 266Level 2 high-level library for DynamoDB. 267 268A "Level 3" high-level library focuses on a specific customer problem 269instead of a specific AWS service. For example, customers frequently use 270DynamoDB to store time series data. An alternate to the proposed 271solution above, would be to build multiple Level 3 libraries, each 272focusing on a specific customer problem: a document database library, a 273time series database library, etc. These libraries would support 274DynamoDB as one of many backing data stores. 275 276Instead of using traditional DynamoDB nouns and verbs (e.g. Item), a 277Level 3 library would use words more aligned to the problem domain (e.g. 278Document for document databases or Entry for time-series data). They 279would also expose operations more constrained to the problem domain they 280were trying to solve, instead of trying to expose every piece of 281DynamoDB functionality. 282 283This solution would be better for customers that are more familiar with 284the problem they are trying to solve and less familiar with DynamoDB. 285This solution would be worse for customers that are familiar with 286DynamoDB and want to be "closer" to the service. 287 288**Customer Feedback** 289 290The Java SDK team collected customer feedback internally and 291[externally](https://github.com/aws/aws-sdk-java-v2/issues/35#issuecomment-468435660), 292comparing this alternate solution against the proposed solution. 293Customers were presented with the following option comparison: 294 295> Option 1: A DynamoDB-specific client that combines the functionality 296> of 1.11.x's Documents APIs and DynamoDB Mapper APIs in a 297> straight-forward manner. 298 299> Option 2: A generic document database client that creates an 300> abstraction over all document databases, like DynamoDB and MongoDB. 301> This would simplify using multiple document databases in the same 302> application, and make it easier to migrate between the two. 303> Unfortunately as a result, it also wouldn't be a direct DynamoDB 304> experience. 305 306We requested that customers review these two options as well as a 307[prototype of option 1](prototype/option-1/sync/Prototype.java) and a 308[prototype of option 2](prototype/option-2/sync/Prototype.java), to let 309us know which they prefer. 310 311The following anecdotes are from this customer feedback: 312 313> If \[Amazon] can make something like https://serverless.com/ or 314> https://onnx.ai/ which free customers from vendor lock-in, that would 315> be a great Think Big & Customer Obsession idea. If \[Amazon] cannot, 316> I feel that somebody who is more vendor-neutral can make a better 317> mapper than \[Amazon]. 318 319> Have you thought about contributing to projects which already exist, 320> like Spring Data? https://github.com/derjust/spring-data-dynamodb 321 322> Both options would work well for us. 323 324> I think \[doing option 1 and then creating a Spring Data plugin] might 325> get adoption from a broader audience than option 2. It could be used 326> as a stepping stone to move to DynamoDB. 327 328> I believe Option 2 does not make much sense. It would make sense to me 329> to go for Option 1 and start a bounty program to implement a module to 330> popular data access abstraction libraries such as spring-data 331> mentioned above or GORM. 332 333> Maybe you could implement/support JNOSQL spec http://www.jnosql.org/ 334 335**Decision** 336 337Based on customer feedback, it was decided to temporarily reject 338alternative solution 1, and build the proposed solution. At a later 339time, the SDK may build a Level 3 abstraction for DynamoDB or integrate 340with existing Java Level 3 abstractions like Spring Data, Hibernate OGM, 341and/or JNoSQL. This Level 3 abstraction will possibly leverage the Level 3422 solution "under the hood". 343 344## Links 345 346**[Features](features.md)** - The features intended for inclusion during 347and after the launch of the enhanced DynamoDB client. 348 349**Prototypes** 350 351During the design of the project, two prototype interfaces were created 352to solicit feedback from customers on potential design directions. 353 354* [Prototype 1](prototype/option-1/sync/Prototype.java) - A DynamoDB 355 specific API that focuses on making DynamoDB easy to use from Java. 356* [Prototype 2](prototype/option-2/sync/Prototype.java) - A 357 DynamoDB-agnostic API that focuses on creating a generic document 358 database abstraction, that could be backed by DynamoDB or other 359 document databases. 360 361**Feedback** 362 363* [DynamoDB Mapper Feature Request](https://github.com/aws/aws-sdk-java-v2/issues/35) 364 \- A github issue for tracking customer feature requests and feedback 365 for DynamoDB mapper-equivalent functionality in 2.x. 366* [DynamoDB Document API Feature Request](https://github.com/aws/aws-sdk-java-v2/issues/36) 367 \- A github issue for tracking customer feature requests and feedback 368 for DynamoDB document API-equivalent functionality in 2.x.