README.md
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.