Skip to content

Commit b948ab9

Browse files
committed
Updated DynamoDB enhanced client design document.
1 parent 45cf9f3 commit b948ab9

File tree

1 file changed

+331
-43
lines changed
  • docs/design/services/dynamodb/high-level-library

1 file changed

+331
-43
lines changed
Lines changed: 331 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,347 @@
11
**Design:** New Feature, **Status:**
22
[In Development](../../../README.md)
33

4-
# SDK Tenets (unless you know better ones)
4+
## Tenets (unless you know better ones)
55

66
1. Meeting customers in their problem space allows them to deliver value
77
quickly.
88
2. Meeting customer expectations drives usability.
99
3. Discoverability drives usage.
10+
4. Providing a Java-focused experience for DynamoDB reduces the coding
11+
effort required to integrate with DynamoDB.
12+
5. Reusing the same nouns and verbs as the generated DynamoDB client
13+
meets customer expectations.
14+
6. Optimizing for cold-start performance allows customers the
15+
convenience of using object mapping in a Lambda environment.
1016

11-
# Project Tenets (unless you know better ones)
17+
## Problem
1218

13-
1. Providing a Java-focused experience for DynamoDB reduces the coding
14-
effort required to integrate with DynamoDB.
15-
2. Reusing the same nouns and verbs as the generated DynamoDB client
16-
meets customer expectations.
17-
3. Optimizing for cold-start performance allows customers the
18-
convenience of using object mapping in a Lambda environment.
19+
Customers on the AWS SDK for Java 2.x currently use the `DynamoDbClient`
20+
to communicate with DynamoDB. This client is generated from the model
21+
provided by the DynamoDB team.
22+
23+
Because this client is generated, it does not provide an idiomatic Java
24+
experience. For example: (1) the client represents numbers as `String`
25+
instead of the more idiomatic `Number`, (2) customers must manually
26+
convert common Java data types like `Instant` into types supported by
27+
DynamoDB, (3) customers that represent their DynamoDB objects using Java
28+
objects must manually convert these objects into the item representation
29+
supported by DynamoDB.
30+
31+
## Existing Solutions
32+
33+
This problem is not currently addressed directly in the AWS SDK for Java
34+
2.x by any known third-party tool. In 1.11.x, several solutions exist,
35+
including AWS's own Document and Mapper Clients.
36+
37+
## Proposed Solution
38+
39+
The AWS SDK for Java will add a new "enhanced DynamoDB client" that
40+
provides an alternative to the data-access portion of the generated
41+
DynamoDB APIs. Control-plane operations like "create table" will not be
42+
supported at launch, but may be added at a later time.
43+
44+
This enhanced client will make DynamoDB easier to use for Java customers
45+
by:
46+
1. Supporting conversions between Java objects and DynamoDB items
47+
2. Supporting conversions between Java built-in types (eg. `Instant`)
48+
and DynamoDB attribute value types
49+
3. Directly supporting every data-plane operation of DynamoDB
50+
4. Using the same verbs and nouns of DynamoDB
51+
52+
## Implementation Overview
53+
54+
**New Clients**
55+
56+
Two new client classes will be added:
57+
`DynamoDbEnhancedClient` and `DynamoDbEnhancedAsyncClient`. These
58+
classes act as a wrapper around the generated `DynamoDbClient` and
59+
`DynamoDbAsyncClient` classes, to provide additional functionality on
60+
top of that which can be provided by the generated clients.
61+
62+
```java
63+
DynamoDbEnhancedClient enhancedClient =
64+
DynamoDbEnhancedClient.builder()
65+
.dynamoDbClient(DynamoDbClient.create())
66+
.build();
67+
```
68+
69+
**Table Abstraction**
70+
71+
`DynamoDbEnhancedClient` provides access to `Table` and `MappedTable`,
72+
and `DynamoDbEnhancedAsyncClient`provides access to `AsyncTable`, and
73+
`AsyncMappedTable` abstractions.
74+
75+
The operations on these "tables" match the data-plane operations in the
76+
low-level DynamoDB client. For example, because `DynamoDbClient.putItem`
77+
exists, `Table.putItem` will also exist.
78+
79+
`Table` and `AsyncTable` work with "items", described below.
80+
`MappedTable` and `AsyncMappedTable` work with "objects", described
81+
below. `Table` and `MappedTable` returning results synchronously, and
82+
`AsyncTable` and `AsyncMappedTable` returning results asynchronously.
83+
84+
```java
85+
Table booksTable = enhancedClient.table("books");
86+
booksTable.putItem(...);
87+
88+
MappedTable mappedBooksTable = enhancedClient.mappedTable("books");
89+
mappedBooksTable.putItem(...);
90+
```
91+
92+
**Item Abstraction**
93+
94+
The operations on `Table` and `AsyncTable` work on `Item`s. An `Item` is
95+
a user-friendly representation of the generated `Map<String,
96+
AttributeValue>`. `Item`s support automatic type conversion between Java
97+
built-in types and DynamoDB-specific `AttributeValue` types.
98+
99+
```java
100+
booksTable.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+
107+
The `Table` and `AsyncTable` abstractions can be seen as a replacement
108+
for the 1.11.x DynamoDB Document client.
109+
110+
**Object Abstraction**
111+
112+
The operations on `MappedTable` and `AsyncMappedTable` work on Java
113+
objects (at launch, Java beans). These objects are automatically
114+
converted by the enhanced client to the generated `Map<String,
115+
AttributeValue>`. It's likely that the `MappedTable` and
116+
`AsyncMappedTable` will use the `Table` and `AsyncTable` as an
117+
implementation detail.
118+
119+
```java
120+
Book book = new Book();
121+
book.setIsbn("0-330-25864-8");
122+
book.setTitle("The Hitchhiker's Guide to the Galaxy");
123+
book.setCreationDate(Instant.now());
124+
mappedBooksTable.putItem(book);
125+
```
126+
127+
The `MappedTable` and `AsyncMappedTable` abstractions can be seen as a
128+
replacement for the 1.11.x DynamoDB Mapper client.
129+
130+
**Type Conversion**
131+
132+
The core feature of the mapper is the ability to convert common Java
133+
structures (e.g. Java beans) and types (e.g. `Instant`, `Number`) into
134+
DynamoDB attribute values.
135+
136+
These conversions are performed based on the types specified by the
137+
customer. For example, the SDK will automatically convert any `Number`
138+
types specified by the customer (as an Item attribute) into a DynamoDB
139+
number.
140+
141+
The customer has the ability to configure the type converters used at
142+
the `Item` or `DynamoDbEnhanced[Async]Client`-level. This allows the
143+
customer to add support for unsupported types, change the DynamoDB type
144+
associated with a Java type (e.g. storing an `Instant` as a DynamoDB
145+
string instead of a number), or to add support for custom POJO
146+
conversion logic (i.e. other than Java beans). This also allows the
147+
customer to provide a hard-coded converter for a specific object type
148+
that performs more efficiently than the built-in reflection-based object
149+
converter.
150+
151+
## Features
152+
153+
**Launch Features**
19154

20-
# Project Introduction
155+
These features are intended for inclusion at launch of the library.
21156

22-
The enhanced DynamoDB client replaces the generated DynamoDB client with
23-
one that is easier for a Java customer to use. It does this by
24-
supporting conversions between Java objects and DynamoDB items, as well
25-
as converting between Java built-in types (eg. java.time.Instant) and
26-
DynamoDB attribute value types.
157+
1. Support for all existing data plane operations: get, put, query,
158+
update, scan, delete, batch get, batch put, transaction get, and
159+
transaction put.
160+
2. Support for `[Async]Table` and `[Async]MappedTable`, as described
161+
above.
162+
3. Support for bean-based representations in `[Async]MappedTable`.
163+
4. Type converters for all Java built-in types that are currently
164+
supported by [Joda Convert](https://www.joda.org/joda-convert/).
27165

28-
The enhanced DynamoDB client intentionally does not attempt to simplify
29-
specific data access patterns, like relational or time-series data. It
30-
is within the scope of future projects to provide data access
31-
pattern-specific abstractions on top of the enhanced DynamoDB client
32-
and/or other AWS services.
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 | |
33189

34-
# Links
190+
**Post-Launch Features**
191+
192+
1. Support for inheritance in `[Async]MappedTable`.
193+
2. Support for immutable objects in `[Async]MappedTable`.
194+
3. Support for projection statements in `[Async]Table` and
195+
`[Async]MappedTable`.
196+
4. Support for DynamoDB-provided API metrics (e.g. consumed capacity).
197+
5. 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+
203+
These features are not intended for inclusion at launch of the library
204+
(but may be added at a future time).
205+
206+
1. 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.
210+
2. 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+
264+
A "Level 2" high-level library is a service-specific library built on
265+
top of the "Level 1" generated client. The solution proposed above is a
266+
Level 2 high-level library for DynamoDB.
267+
268+
A "Level 3" high-level library focuses on a specific customer problem
269+
instead of a specific AWS service. For example, customers frequently use
270+
DynamoDB to store time series data. An alternate to the proposed
271+
solution above, would be to build multiple Level 3 libraries, each
272+
focusing on a specific customer problem: a document database library, a
273+
time series database library, etc. These libraries would support
274+
DynamoDB as one of many backing data stores.
275+
276+
Instead of using traditional DynamoDB nouns and verbs (e.g. Item), a
277+
Level 3 library would use words more aligned to the problem domain (e.g.
278+
Document for document databases or Entry for time-series data). They
279+
would also expose operations more constrained to the problem domain they
280+
were trying to solve, instead of trying to expose every piece of
281+
DynamoDB functionality.
282+
283+
This solution would be better for customers that are more familiar with
284+
the problem they are trying to solve and less familiar with DynamoDB.
285+
This solution would be worse for customers that are familiar with
286+
DynamoDB and want to be "closer" to the service.
287+
288+
**Customer Feedback**
289+
290+
The Java SDK team collected customer feedback internally and
291+
[externally](https://github.com/aws/aws-sdk-java-v2/issues/35#issuecomment-468435660),
292+
comparing this alternate solution against the proposed solution.
293+
Customers 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+
306+
We 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
309+
us know which they prefer.
310+
311+
The 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+
337+
Based on customer feedback, it was decided to temporarily reject
338+
alternative solution 1, and build the proposed solution. At a later
339+
time, the SDK may build a Level 3 abstraction for DynamoDB or integrate
340+
with existing Java Level 3 abstractions like Spring Data, Hibernate OGM,
341+
and/or JNoSQL. This Level 3 abstraction will possibly leverage the Level
342+
2 solution "under the hood".
343+
344+
## Links
35345

36346
**[Features](features.md)** - The features intended for inclusion during
37347
and after the launch of the enhanced DynamoDB client.
@@ -55,26 +365,4 @@ to solicit feedback from customers on potential design directions.
55365
for DynamoDB mapper-equivalent functionality in 2.x.
56366
* [DynamoDB Document API Feature Request](https://github.com/aws/aws-sdk-java-v2/issues/36)
57367
\- A github issue for tracking customer feature requests and feedback
58-
for DynamoDB document API-equivalent functionality in 2.x.
59-
60-
# FAQ
61-
62-
**Why not optimize for specific data access patterns, like relational
63-
data?**
64-
65-
Some customers prefer to think about the DynamoDB-specific concepts of
66-
tables, queries, conditions and global secondary indices. These
67-
customers currently have trouble interacting with DynamoDB, because they
68-
have to perform a large amount of conversion between their Java types
69-
and DynamoDB types.
70-
71-
If we were to optimize this specific project for a particular access
72-
pattern, customers that have other access patterns would either be
73-
forced to modify their access pattern into the one we've optimized for,
74-
or they would need to use the generated client and handle their own Java
75-
type conversion.
76-
77-
Instead, this project focuses on making integrating with DynamoDB easier
78-
to do in Java, and leaves the problems of "making storing time series
79-
data on AWS easy" or "making storing documents on AWS easy" to another
80-
project.
368+
for DynamoDB document API-equivalent functionality in 2.x.

0 commit comments

Comments
 (0)