graphddb 0.2.4 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,28 +1,27 @@
1
1
  # GraphDDB
2
2
 
3
- **Type-safe Graph Query Runtime for DynamoDB**
3
+ **DynamoDB のための型安全な Graph Query Runtime**
4
4
 
5
- GraphDDB maps DynamoDB's native access patterns to a GraphQL-like query model:
6
- PK, GSI, Projection, Relation, Cursor, and operation limits all remain visible.
5
+ GraphDDB DynamoDB のネイティブなアクセスパターンを GraphQL ライクなクエリモデルにマッピングする。
6
+ PKGSIProjectionRelationCursor、そして操作の上限(limit)はすべて隠さず可視に保たれる。
7
7
 
8
- GraphDDB is not an ORM.
9
- It is a runtime for defining, validating, and executing DynamoDB access patterns with TypeScript types.
8
+ GraphDDB ORM ではない。
9
+ TypeScript の型を使って DynamoDB のアクセスパターンを定義・検証・実行するための Runtime である。
10
10
 
11
- ## Why GraphDDB?
11
+ ## 🤔 Why GraphDDB?
12
12
 
13
- DynamoDB is fast and scalable, but easy to design incorrectly. Teams often treat it like an
14
- RDBMS one table per entity, joins in application code, ad-hoc access patterns hidden behind
15
- ORM abstractions and end up with unstable performance, unnecessary GSIs, and unclear service
16
- boundaries.
13
+ DynamoDB は高速でスケーラブルだが、誤った設計をしやすい。チームはしばしば DynamoDB
14
+ RDBMS のように扱い —— エンティティごとに1テーブル、アプリケーションコード内での join、
15
+ ORM 抽象の裏に隠れたその場しのぎのアクセスパターン —— その結果、不安定なパフォーマンス、
16
+ 不要な GSI、不明瞭なサービス境界に陥る。
17
17
 
18
- GraphDDB takes the opposite approach: it represents DynamoDB access patterns **directly in
19
- code**, keeping PK/GSI, projections, relations, cursors, and operation limits visible. Access
20
- patterns and relations become explicit, query plans become inspectable, and well-designed
21
- access patterns naturally align with service boundaries. GraphDDB does not teach DynamoDB
22
- design — it makes correct design the easiest way to write code.
18
+ GraphDDB は逆のアプローチを取る。DynamoDB のアクセスパターンを **コードに直接** 表現し、
19
+ PK/GSI、projection、relation、cursor、操作の上限を可視に保つ。アクセスパターンと relation
20
+ 明示的になり、クエリプランが検査可能になり、よく設計されたアクセスパターンが自然と
21
+ サービス境界に整合する。GraphDDB DynamoDB の設計を教えるのではない —— 正しい設計を、
22
+ コードを書く上で最も簡単な道にする。
23
23
 
24
- It does this through a natural correspondence between GraphQL's query model and DynamoDB's
25
- access patterns:
24
+ これは GraphQL のクエリモデルと DynamoDB のアクセスパターンの自然な対応関係を通じて実現される:
26
25
 
27
26
  ```text
28
27
  GraphQL Query Model DynamoDB
@@ -33,33 +32,33 @@ connection ----> Cursor pagination
33
32
  complexity ----> Operation limits
34
33
  ```
35
34
 
36
- A partition key is a graph entry point, a relation an explicit traversal path, a projection a
37
- selection set. GraphDDB does not invent a query language — it executes well-designed access
38
- patterns directly and makes ambiguity visible through plans, limits, and relation resolution.
35
+ Partition Key Graph のエントリポイントであり、relation は明示的な traversal パスであり、
36
+ projection は selection set である。GraphDDB はクエリ言語を発明しない —— よく設計された
37
+ アクセスパターンを直接実行し、plan・limit・relation 解決を通じて曖昧さを可視化する。
39
38
 
40
- ## Install
39
+ ## 📦 Install
41
40
 
42
41
  ```bash
43
42
  npm install graphddb
44
43
  ```
45
44
 
46
- The AWS SDK v3 is a peer dependency install the clients you use:
45
+ AWS SDK v3 peer dependency である —— 使用するクライアントをインストールすること:
47
46
 
48
47
  ```bash
49
48
  npm install @aws-sdk/client-dynamodb @aws-sdk/lib-dynamodb
50
49
  ```
51
50
 
52
- `tsx` is an optional peer dependency, needed only to run the code-generation CLI
53
- (`graphddb generate …`) directly against TypeScript definition files. Library
54
- consumers don't need it (so it never pulls esbuild into your runtime bundle):
51
+ `tsx` optional peer dependency で、コード生成 CLI(`graphddb generate …`)を
52
+ TypeScript の定義ファイルに対して直接実行する場合にのみ必要となる。ライブラリの利用者には
53
+ 不要である(そのため esbuild がランタイムバンドルに混入することはない):
55
54
 
56
55
  ```bash
57
56
  npm install -D tsx
58
57
  ```
59
58
 
60
- Requires Node.js ≥ 22.
59
+ Node.js ≥ 22 が必要。
61
60
 
62
- ## Quick Start
61
+ ## 🚀 Quick Start
63
62
 
64
63
  ```ts
65
64
  import { DDBModel, model, string, key, k } from 'graphddb';
@@ -77,74 +76,76 @@ class UserModel extends DDBModel {
77
76
  }
78
77
  const User = UserModel.asModel();
79
78
 
80
- // Wire up the DynamoDB client once. GraphDDB owns throttle/transient retry
81
- // (see "Retry & throttling" below), so set the client to `maxAttempts: 1` to
82
- // avoid retrying twice (SDK × library).
79
+ // DynamoDB クライアントは一度だけ接続する。GraphDDB throttle / transient retry
80
+ // 所有するため(後述の "Retry & throttling" を参照)、クライアントは `maxAttempts: 1` に設定し、
81
+ // 二重 retry(SDK × ライブラリ)を避けること。
83
82
  DDBModel.setClient(new DynamoDBClient({ maxAttempts: 1 }));
84
83
 
85
- // Read only the selected fields appear in the result type.
84
+ // Read —— 結果の型に現れるのは select したフィールドのみ。
86
85
  const user = await User.query({ userId: 'alice' }, { name: true });
87
86
 
88
- // Single-item write raw base op, named after the DynamoDB API.
87
+ // 単一アイテムの書き込み —— DynamoDB API にちなんで名付けられた raw base 操作。
89
88
  await User.putItem({ userId: 'alice', name: 'Alice' });
90
89
 
91
- // Multi-item atomic write the unified envelope (default `mode: 'transaction'`).
90
+ // 複数アイテムの atomic な書き込み —— 統一 envelope(デフォルト `mode: 'transaction'`)。
92
91
  await DDBModel.mutate({
93
92
  user: { update: User, key: { userId: 'alice' }, input: { name: 'Alice B.' } },
94
93
  });
95
94
  ```
96
95
 
97
- For cross-service contracts, the Python bridge, multi-fragment atomic mutations,
98
- and more, see the sections below and [`docs/spec.md`](./docs/spec.md).
96
+ クロスサービス contract、Python bridge、複数 fragment atomic mutation などについては、
97
+ 以下の各セクションおよび [`docs/spec.md`](./docs/spec.md) を参照。
99
98
 
100
- ## Retry & throttling
99
+ ## 🔁 Retry & throttling
101
100
 
102
- GraphDDB retries throttle / transient errors on **every** single-item op
103
- (`GetItem` / `Query` / `PutItem` / `UpdateItem` / `DeleteItem`), the relation
104
- read fan-out, and `TransactWriteItems` out of the box, with no configuration.
105
- The default policy is exponential backoff (`min(1000, 50·2^(n-1))` ms) with full
106
- jitter, capped at 10 attempts, retrying only throttle / 5xx / transient errors
107
- (never `ValidationException` or a conditional-check failure). A cancelled
108
- transaction is inspected per-reason: a `ConditionalCheckFailed` reason throws
109
- immediately, a purely throttle-induced cancellation is retried.
101
+ GraphDDB **すべての** 単一アイテム操作(`GetItem` / `Query` / `PutItem` / `UpdateItem` /
102
+ `DeleteItem`)、relation read fan-out、そして `TransactWriteItems` に対して、throttle /
103
+ transient エラーの retry を行う —— 設定不要で、すぐに使える。デフォルトのポリシーは
104
+ full jitter 付きの指数バックオフ(`min(1000, 50·2^(n-1))` ms)で、最大 10 回まで、
105
+ throttle / 5xx / transient エラーのみを retry する(`ValidationException` や
106
+ 条件チェック失敗は決して retry しない)。キャンセルされた transaction は理由ごとに
107
+ 検査される: `ConditionalCheckFailed` の理由は即座に throw し、純粋に throttle による
108
+ キャンセルは retry される。
110
109
 
111
- > This is the library's own **error retry**, distinct from the partial-batch
112
- > `UnprocessedKeys` / `UnprocessedItems` retry that `batchGet` / `batchWrite`
113
- > already perform. Both use the same backoff ramp but are separate mechanisms.
110
+ > これはライブラリ自身の **error retry** であり、`batchGet` / `batchWrite` が既に行う
111
+ > 部分バッチの `UnprocessedKeys` / `UnprocessedItems` retry とは別物である。両者は同じ
112
+ > バックオフのランプを使うが、独立したメカニズムである。
114
113
 
115
- **Set your client to `maxAttempts: 1`.** Now that the library owns retry, leaving
116
- the AWS SDK at its default (`maxAttempts: 3`) retries *multiplicatively*
117
- underneath GraphDDB. Make the library the single source of truth:
114
+ **クライアントは `maxAttempts: 1` に設定すること。** ライブラリが retry を所有する以上、
115
+ AWS SDK をデフォルト(`maxAttempts: 3`)のままにすると、GraphDDB の下層で
116
+ *乗算的に* retry してしまう。ライブラリを単一の真実の源とすること:
118
117
 
119
118
  ```ts
120
119
  DDBModel.setClient(new DynamoDBClient({ maxAttempts: 1 }));
121
120
  ```
122
121
 
123
- GraphDDB never mutates a client you pass in — this is on you to set.
122
+ GraphDDB は渡されたクライアントを決して変更しない —— この設定はあなたの責任である。
124
123
 
125
- Tune the policy globally, or per call:
124
+ ポリシーはグローバルに、あるいは呼び出しごとに調整できる:
126
125
 
127
126
  ```ts
128
127
  import { DDBModel } from 'graphddb';
129
128
 
130
- // Global override (mirrors setClient / setTableMapping). `null` restores the default.
129
+ // グローバルな上書き(setClient / setTableMapping と同様)。`null` でデフォルトに戻る。
131
130
  DDBModel.setRetryPolicy({
132
131
  maxAttempts: 5,
133
- jitter: 'full', // or 'none'
132
+ jitter: 'full', // または 'none'
134
133
  onRetry: ({ attempt, error, delayMs, operation }) => {
135
134
  console.warn(`retry #${attempt} of ${operation} after ${delayMs}ms`, error);
136
135
  },
137
136
  });
138
137
 
139
- // Per-call override on any op's options (`RetryPolicy | false`). `false` disables retry.
138
+ // 任意の操作の options での呼び出しごとの上書き(`RetryPolicy | false`)。`false` retry 無効。
140
139
  await User.putItem({ userId: 'alice', name: 'Alice' }, { retry: false });
141
140
  await User.query({ userId: 'alice' }, { name: true }, { retry: { maxAttempts: 3 } });
142
141
  ```
143
142
 
144
- ## User Permissions Example
143
+ ## 👥 User Permissions Example
145
144
 
146
- The [user-permissions](./examples/user-permissions/) example models users, groups, memberships, and permissions in a single DynamoDB table.
147
- This is where GraphDDB's design becomes easiest to see: the table is physical, while the query surface is graph-shaped.
145
+ [user-permissions](./examples/user-permissions/) の例は、ユーザー・グループ・メンバーシップ・
146
+ パーミッションを単一の DynamoDB テーブルでモデル化している。
147
+ ここで GraphDDB の設計が最も分かりやすく見えてくる: テーブルは物理的だが、クエリの表面は
148
+ Graph の形をしている。
148
149
 
149
150
  ```text
150
151
  UserPermissions table
@@ -156,12 +157,12 @@ GroupMembership GROUP#<groupId> USER#<userId> USER#<userId>
156
157
  Permission GROUP#<groupId> PERM#<resource>#<action>
157
158
  ```
158
159
 
159
- `GroupMembership` uses the adjacency list pattern:
160
+ `GroupMembership` は隣接リスト(adjacency list)パターンを使う:
160
161
 
161
- - `PK/SK` reads members of a group
162
- - `GSI1PK/GSI1SK` reads groups for a user
162
+ - `PK/SK` はグループのメンバーを読む
163
+ - `GSI1PK/GSI1SK` はユーザーが所属するグループを読む
163
164
 
164
- The same physical table supports both directions without hiding the keys.
165
+ 同じ物理テーブルが、キーを隠すことなく両方向をサポートする。
165
166
 
166
167
  ```ts
167
168
  const engGroup = await Group.query(
@@ -180,9 +181,9 @@ const engGroup = await Group.query(
180
181
  },
181
182
  );
182
183
 
183
- // Find a user by email (GSI), list their groups, and for each group fetch the
184
- // group and its permissions. The N memberships -> parent Group resolution
185
- // collapses into a single BatchGetItem (no N+1).
184
+ // emailGSI)でユーザーを探し、所属グループを一覧し、各グループについてグループ本体と
185
+ // そのパーミッションを取得する。N 件の membership -> Group の解決は単一の
186
+ // BatchGetItem に集約される(N+1 にならない)。
186
187
  const alice = await User.query(
187
188
  { email: 'alice@example.com' },
188
189
  {
@@ -209,24 +210,29 @@ const alice = await User.query(
209
210
  );
210
211
  ```
211
212
 
212
- The first query expands into a small, inspectable DynamoDB plan:
213
+ 最初のクエリは、小さく検査可能な DynamoDB plan に展開される:
213
214
 
214
- ```text
215
- 1. Query PK = GROUP#eng, SK = META -> Group
216
- 2. Query PK = GROUP#eng, SK begins_with USER# -> members ┐ dispatched
217
- 3. Query PK = GROUP#eng, SK begins_with PERM# -> permissions ┘ in parallel
215
+ ```mermaid
216
+ flowchart LR
217
+ Q(["Group.query<br/>groupId: eng"]) --> S1["1 Query<br/>PK=GROUP#eng, SK=META<br/>→ Group"]
218
+ Q --> S2["2 Query<br/>PK=GROUP#eng, SK begins_with USER#<br/>→ members"]
219
+ Q --> S3["3 Query<br/>PK=GROUP#eng, SK begins_with PERM#<br/>→ permissions"]
220
+ S2 -.並列ディスパッチ.- S3
218
221
  ```
219
222
 
220
- Independent relation sub-queries (here `members` and `permissions`) are dispatched concurrently; the `belongsTo` resolution in the second query batches N lookups into one `BatchGetItem`. See the [example](./examples/user-permissions/) for a timeline that proves the parallel dispatch.
223
+ 独立した relation のサブクエリ(ここでは `members` `permissions`)は並列にディスパッチされる。
224
+ 2番目のクエリの `belongsTo` 解決は N 件の lookup を1回の `BatchGetItem` にバッチ化する。
225
+ 並列ディスパッチを示すタイムラインは [example](./examples/user-permissions/) を参照。
221
226
 
222
- ## Usage
227
+ ## 📖 Usage
223
228
 
224
- The snippets below are compressed from the [user-permissions](./examples/user-permissions/) example.
229
+ 以下のスニペットは [user-permissions](./examples/user-permissions/) の例を圧縮したものである。
225
230
 
226
231
  ### Entity Definition
227
232
 
228
- A TypeScript class is the single source of truth. Keys and GSIs are built from `k` tagged-template
229
- **segments**, and the key builder's input type becomes the query parameter type.
233
+ TypeScript クラスが単一の真実の源(single source of truth)である。Key GSI `k`
234
+ タグ付きテンプレートの **segment** から構築され、key builder の入力型がそのまま
235
+ クエリパラメータの型になる。
230
236
 
231
237
  ```ts
232
238
  const TABLE = 'UserPermissions';
@@ -276,18 +282,18 @@ export const User = UserModel.asModel();
276
282
  export const GroupMembership = GroupMembershipModel.asModel();
277
283
  ```
278
284
 
279
- A multi-segment sort key is an array of segments (e.g. `sk: [k\`PERM#${c.resource}\`, k\`${c.action}\`]`);
280
- a partial key compiles to `begins_with` at the matching segment boundary. See the
281
- [specification](./docs/spec.md) for the full key model.
285
+ 複数 segment sort key segment の配列である(例: `sk: [k\`PERM#${c.resource}\`, k\`${c.action}\`]`)。
286
+ partial key はマッチする segment 境界で `begins_with` にコンパイルされる。完全なキーモデルは
287
+ [仕様書](./docs/spec.md) を参照。
282
288
 
283
289
  ### GraphQL-like Queries
284
290
 
285
- The first argument describes how to find data.
286
- The second argument describes what to return.
287
- The runtime resolves which index to use from the provided field names.
291
+ 第1引数は「どうやってデータを見つけるか」を記述する。
292
+ 第2引数は「何を返すか」を記述する。
293
+ Runtime は与えられたフィールド名から、どのインデックスを使うかを解決する。
288
294
 
289
295
  ```ts
290
- // Fetch a user by email and traverse to group memberships.
296
+ // email でユーザーを取得し、group membership traverse する。
291
297
  const alice = await User.query(
292
298
  { email: 'alice@example.com' },
293
299
  {
@@ -300,11 +306,11 @@ const alice = await User.query(
300
306
  },
301
307
  );
302
308
 
303
- // Unknown key field -> compile-time error
309
+ // 未定義のキーフィールド -> コンパイルエラー
304
310
  User.query({ foo: 'bar' }, { name: true });
305
311
  ```
306
312
 
307
- The return type is inferred from `select`:
313
+ 返り値の型は `select` から推論される:
308
314
 
309
315
  ```ts
310
316
  // alice: {
@@ -328,21 +334,21 @@ const result = await GroupMembership.list(
328
334
  // result.cursor: string | null
329
335
  ```
330
336
 
331
- ### Filtering (server-side `filter`)
337
+ ### Filtering(サーバーサイド `filter`)
332
338
 
333
- `filter` is a declarative, type-safe condition compiled to a DynamoDB **`FilterExpression`** and
334
- evaluated **server-side**. Modeled on AWS AppSync's `ModelFilterInput`: a bare value is an
335
- equality shorthand, an operator object expresses comparisons, and `and` / `or` / `not` build
336
- logical groups. It is typed against the **full entity**, so it can reference attributes that are
337
- **not** in `select`.
339
+ `filter` は宣言的で型安全な条件であり、DynamoDB **`FilterExpression`** にコンパイルされ、
340
+ **サーバーサイド** で評価される。AWS AppSync `ModelFilterInput` をモデルにしている:
341
+ 裸の値は等価の省略記法、operator オブジェクトは比較を表現し、`and` / `or` / `not`
342
+ 論理グループを構築する。**エンティティ全体** に対して型付けされるため、`select` **含まれない**
343
+ 属性も参照できる。
338
344
 
339
345
  ```ts
340
346
  const result = await Order.list(
341
347
  { userId: 'u001' },
342
- { orderId: true }, // amount / status need not be projected
348
+ { orderId: true }, // amount / status projection 不要
343
349
  {
344
350
  filter: {
345
- status: 'confirmed', // #status = :v (equality shorthand)
351
+ status: 'confirmed', // #status = :v (等価の省略記法)
346
352
  amount: { gt: 100 }, // #amount > :v
347
353
  title: { beginsWith: 'A' }, // begins_with(#title, :v)
348
354
  shippedAt: { attributeExists: true },
@@ -352,44 +358,44 @@ const result = await Order.list(
352
358
  );
353
359
  ```
354
360
 
355
- Relations take the same `filter` via `Model.relation(...)` of the target model. For logic a
356
- server-side `FilterExpression` cannot express, call `result.items.filter(...)` on the returned
357
- projection — it keeps each selected field's declared scalar type, so `o.amount > 100` needs no
358
- cast (there is no built-in post-load predicate).
361
+ Relation は対象モデルの `Model.relation(...)` を通じて同じ `filter` を取る。サーバーサイドの
362
+ `FilterExpression` で表現できないロジックには、返された projection に対して
363
+ `result.items.filter(...)` を呼ぶこと —— select フィールドの宣言済みスカラー型が保たれるため、
364
+ `o.amount > 100` はキャスト不要である(組み込みの post-load 述語は存在しない)。
359
365
 
360
- Available operators (per field type): `eq`, `ne`, `ge`, `le`, `gt`, `lt`,
361
- `between`, `in`, `beginsWith`, `contains`, `notContains`, `attributeExists`,
362
- `attributeType`, `size`. Operators are **constrained by the field's declared
363
- type** — e.g. `beginsWith` / `contains` on a numeric field, or `gt: 'x'` on a
364
- number, are compile errors.
366
+ 利用可能な operator(フィールド型ごと): `eq`、`ne`、`ge`、`le`、`gt`、`lt`、
367
+ `between`、`in`、`beginsWith`、`contains`、`notContains`、`attributeExists`、
368
+ `attributeType`、`size`。operator **フィールドの宣言型によって制約される** ——
369
+ 例えば数値フィールドへの `beginsWith` / `contains`、あるいは数値への `gt: 'x'`
370
+ コンパイルエラーになる。
365
371
 
366
- For rare conditions the operator objects cannot express, the `cond` raw escape
367
- hatch accepts an expression fragment. Column names must use refactor-safe
368
- `Model.col.<field>` references (never bare strings); values are parameterized:
372
+ operator オブジェクトでは表現できない稀な条件のために、`cond` という raw escape hatch
373
+ fragment を受け取る。カラム名は bare な文字列ではなく、リファクタ安全な
374
+ `Model.col.<field>` 参照を使わなければならない。値はパラメータ化される:
369
375
 
370
376
  ```ts
371
377
  filter: cond`${Order.col.amount} > ${100} AND attribute_exists(${Order.col.status})`
372
378
  ```
373
379
 
374
- All values are parameterized (`ExpressionAttributeValues`) and all column names
375
- are aliased (`ExpressionAttributeNames`) — there is no literal interpolation, so
376
- the compiled expression is injection-free, and it is attached independently of
377
- the `KeyConditionExpression` / `ProjectionExpression`.
380
+ すべての値はパラメータ化され(`ExpressionAttributeValues`)、すべてのカラム名は
381
+ エイリアス化される(`ExpressionAttributeNames`)—— リテラルの補間は存在しないため、
382
+ コンパイルされた式はインジェクション安全であり、`KeyConditionExpression` /
383
+ `ProjectionExpression` とは独立して付与される。
378
384
 
379
- The same `cond` fragment is also accepted as a write `condition` (on
380
- `putItem` / `updateItem` / `deleteItem`, transaction items, and public commands),
381
- not just as a read filter.
385
+ 同じ `cond` fragment read filter としてだけでなく、書き込みの `condition`
386
+ (`putItem` / `updateItem` / `deleteItem`、transaction item、public command に対して)
387
+ としても受け付けられる。
382
388
 
383
- > **RCU note:** A `FilterExpression` does **not** reduce read capacity
384
- > DynamoDB reads matching keys first, then filters. `limit` is applied
385
- > **before** the filter, so a single page may return **fewer** than `limit`
386
- > items (and, if the whole page is filtered out, an empty page with a non-null
387
- > cursor). Design keys for efficient narrowing; use `filter` for correctness.
389
+ > **RCU に関する注意:** `FilterExpression` read capacity を **削減しない** ——
390
+ > DynamoDB はまずマッチするキーを読み、その後に filter する。`limit` filter の
391
+ > **前** に適用されるため、1ページが `limit` **未満** のアイテムを返すことがある
392
+ > (そしてページ全体が filter で除外された場合、null でない cursor を持つ空ページになる)。
393
+ > 効率的な絞り込みのためにキーを設計し、`filter` は正しさのために使うこと。
388
394
 
389
395
  ### Writes
390
396
 
391
- Raw base-table writes are `putItem` / `updateItem` / `deleteItem` the primitive
392
- single-item operations with no lifecycle semantics:
397
+ raw base テーブルの書き込みは `putItem` / `updateItem` / `deleteItem` である ——
398
+ ライフサイクルのセマンティクスを持たないプリミティブな単一アイテム操作:
393
399
 
394
400
  ```ts
395
401
  await User.putItem({
@@ -408,19 +414,18 @@ await GroupMembership.putItem({
408
414
  await User.updateItem({ userId: 'alice' }, { status: 'disabled' });
409
415
  ```
410
416
 
411
- Lifecycle-aware single writes — conditional gates, read-back, referential
412
- effects go through `DDBModel.mutate`, not the raw primitives (see
413
- [The in-process unified envelope](#the-in-process-unified-envelope) below).
417
+ ライフサイクルを意識した単一書き込み —— 条件ゲート、read-back、参照整合的な副作用 ——
418
+ raw なプリミティブではなく `DDBModel.mutate` を経由する(後述の
419
+ [The in-process unified envelope](#the-in-process-unified-envelope) を参照)。
414
420
 
415
421
  ### The in-process unified envelope
416
422
 
417
- `DDBModel.query` and `DDBModel.mutate` take a single **alias-map envelope** that
418
- runs multiple routes in one call. This is the in-process analog of a GraphQL
419
- operation: each alias is a named route, and the result is keyed by the same
420
- aliases.
423
+ `DDBModel.query` `DDBModel.mutate` は、1回の呼び出しで複数の route を実行する単一の
424
+ **alias-map envelope** を取る。これは GraphQL operation in-process 版である:
425
+ alias は名前付きの route であり、結果は同じ alias でキー付けされる。
421
426
 
422
- `DDBModel.query(map)` runs each read route independently and in parallel — there
423
- is no cross-route consistency, exactly like GraphQL's parallel field resolution:
427
+ `DDBModel.query(map)` は各 read route を独立かつ並列に実行する —— route 間の
428
+ 一貫性はなく、GraphQL の並列フィールド解決とまったく同じである:
424
429
 
425
430
  ```ts
426
431
  const { user, members } = await DDBModel.query({
@@ -430,12 +435,12 @@ const { user, members } = await DDBModel.query({
430
435
  // user: Item | null ; members: { items, cursor }
431
436
  ```
432
437
 
433
- A read route descriptor is `{ query | list: Model, key, select, options? }`
434
- (exactly one of `query` / `list`). `options` for `query` is
435
- `{ consistentRead?, maxDepth? }`; for `list` it is `{ limit?, after?, order?, filter? }`.
438
+ read route の記述子は `{ query | list: Model, key, select, options? }` である
439
+ (`query` / `list` のいずれか一方)。`query` `options`
440
+ `{ consistentRead?, maxDepth? }`、`list` の場合は `{ limit?, after?, order?, filter? }` である。
436
441
 
437
- `DDBModel.mutate(map, { mode })` runs write routes. A write route descriptor is
438
- `{ create | update | remove: Model, key, input?, condition?, result? }`:
442
+ `DDBModel.mutate(map, { mode })` write route を実行する。write route の記述子は
443
+ `{ create | update | remove: Model, key, input?, condition?, result? }` である:
439
444
 
440
445
  ```ts
441
446
  const res = await DDBModel.mutate({
@@ -444,32 +449,30 @@ const res = await DDBModel.mutate({
444
449
  }, { mode: 'transaction' });
445
450
  ```
446
451
 
447
- - **`key`** is a single object or an **array of objects** (key-array bulk). A
448
- transaction compiles to `TransactWriteItems`; parallel mode compiles to
449
- `BatchWriteItem` (chunked, with `UnprocessedItems` retry).
450
- - **`condition`** is a `WriteCondition` write gate the same declarative
451
- operator subset as read filters (`eq`/`ne`/comparisons/`between`/`in`/string
452
- ops/`size`/`attributeType` + `and`/`or`/`not`), the legacy existence primitives
453
- (`notExists` / `attributeExists` / `attributeNotExists`), or a raw `cond`
454
- fragment.
455
- - **`result: { select, options? }`** reads the written item back; omit it and the
456
- route returns void.
457
- - **`mode: 'transaction'`** (DEFAULT) is one atomic `TransactWriteItems`:
458
- all-or-nothing rollback, throws on failure, and a map of more than 25 items
459
- errors (it is never split). This is GraphQL's all-or-nothing atomic contract.
460
- - **`mode: 'parallel'`** is non-atomic: each alias reports partial success in the
461
- result object as `{ ok }` | `{ error }`, mirroring GraphQL's per-field partial
462
- success. Independent ops run in parallel; dependencies are ordered by
463
- `$.alias.field` references.
464
-
465
- In GraphQL terms: a transaction is atomic all-or-nothing; parallel is non-atomic
466
- per-field partial success; and `query` routes are parallel independent reads with
467
- no cross-route consistency.
452
+ - **`key`** は単一のオブジェクト、または **オブジェクトの配列**(key-array bulk)である。
453
+ transaction `TransactWriteItems` にコンパイルされ、parallel モードは
454
+ `BatchWriteItem`(チャンク分割され、`UnprocessedItems` retry 付き)にコンパイルされる。
455
+ - **`condition`** `WriteCondition` の書き込みゲートである —— read filter と同じ宣言的
456
+ operator のサブセット(`eq`/`ne`/比較/`between`/`in`/文字列操作/`size`/`attributeType` +
457
+ `and`/`or`/`not`)、レガシーな存在プリミティブ(`notExists` / `attributeExists` /
458
+ `attributeNotExists`)、または raw `cond` fragment。
459
+ - **`result: { select, options? }`** は書き込んだアイテムを read-back する。省略すると
460
+ その route void を返す。
461
+ - **`mode: 'transaction'`**(デフォルト)は1回の atomic な `TransactWriteItems` である:
462
+ all-or-nothing のロールバック、失敗時に throw、25 件を超える map はエラーになる
463
+ (決して分割されない)。これは GraphQL の all-or-nothing atomic 契約である。
464
+ - **`mode: 'parallel'`** は非 atomic である: alias は結果オブジェクト内で
465
+ `{ ok }` | `{ error }` として部分的な成否を報告し、GraphQL のフィールドごとの
466
+ 部分成功を反映する。独立した操作は並列に実行され、依存関係は `$.alias.field`
467
+ 参照で順序付けられる。
468
+
469
+ GraphQL の用語で言えば: transaction は atomic な all-or-nothing、parallel は非 atomic な
470
+ フィールドごとの部分成功、そして `query` route route 間の一貫性を持たない並列独立 read である。
468
471
 
469
472
  ### Inspect Execution Plans
470
473
 
471
- `explain()` shows the DynamoDB operations before they are executed.
472
- `select` is converted into a `ProjectionExpression`, so only requested attributes are read.
474
+ `explain()` は、実行される前の DynamoDB 操作を表示する。
475
+ `select` `ProjectionExpression` に変換されるため、要求した属性のみが読まれる。
473
476
 
474
477
  ```ts
475
478
  const plan = GroupMembership.explain(
@@ -491,7 +494,7 @@ const plan = GroupMembership.explain(
491
494
  // }
492
495
  ```
493
496
 
494
- For partial GSI key matches, the SK condition is separated as a `rangeCondition`:
497
+ partial GSI キーマッチの場合、SK 条件は `rangeCondition` として分離される:
495
498
 
496
499
  ```ts
497
500
  const gsiPlan = GroupMembership.explain(
@@ -513,33 +516,33 @@ const gsiPlan = GroupMembership.explain(
513
516
  // }
514
517
  ```
515
518
 
516
- ## Core Concepts
519
+ ## 🧠 Core Concepts
517
520
 
518
521
  ### Entity = Access Pattern
519
522
 
520
- An Entity is not an ORM model.
521
- It is a bounded access surface for DynamoDB.
523
+ Entity ORM モデルではない。
524
+ DynamoDB に対する、境界付けられたアクセス表面(bounded access surface)である。
522
525
 
523
- An Entity defines every supported way to access data within a service boundary:
526
+ Entity は、サービス境界内でデータにアクセスする、サポートされたすべての方法を定義する:
524
527
 
525
- - physical keys
526
- - GSIs
527
- - relations
528
- - projections
529
- - traversal rules
528
+ - 物理キー
529
+ - GSI
530
+ - relation
531
+ - projection
532
+ - traversal ルール
530
533
 
531
- Physically, an Entity maps to a DynamoDB item.
532
- Architecturally, it defines the allowed access surface for that part of the service.
534
+ 物理的には、Entity DynamoDB item にマップされる。
535
+ アーキテクチャ的には、サービスのその部分に許可されたアクセス表面を定義する。
533
536
 
534
537
  ### Key / GSI = Structured Segments
535
538
 
536
- `key()` and `gsi()` build keys from `k` tagged-template **segments**. The builder's input type
537
- defines the fields required to resolve the key, and also becomes the query parameter type.
539
+ `key()` `gsi()` `k` タグ付きテンプレートの **segment** からキーを構築する。builder
540
+ 入力型がキー解決に必要なフィールドを定義し、同時にクエリパラメータの型にもなる。
538
541
 
539
542
  ```ts
540
- // One definition serves two roles:
541
- // 1. Write path: generate PK/SK when saving an Entity
542
- // 2. Query path: define the parameter type for User.query({ email: ... })
543
+ // 1つの定義が2つの役割を果たす:
544
+ // 1. 書き込みパス: Entity 保存時に PK/SK を生成
545
+ // 2. クエリパス: User.query({ email: ... }) のパラメータ型を定義
543
546
  static readonly emailIndex = gsi<{ email: string }>(
544
547
  'GSI1',
545
548
  (c) => ({ pk: k`EMAIL#${c.email}`, sk: k`PROFILE` }),
@@ -547,14 +550,14 @@ static readonly emailIndex = gsi<{ email: string }>(
547
550
  );
548
551
  ```
549
552
 
550
- Because keys are structured segments rather than opaque strings, a partial key one supplying
551
- only a leading prefix of the sort-key segments — compiles to `begins_with` at that segment
552
- boundary. This is how relations and prefix queries resolve.
553
+ キーは不透明な文字列ではなく構造化された segment であるため、partial key —— sort-key
554
+ segment の先頭プレフィックスのみを与えたもの —— はその segment 境界で `begins_with`
555
+ コンパイルされる。これが relation prefix クエリの解決方法である。
553
556
 
554
557
  ### Relation = Bound Query
555
558
 
556
- A Relation is a query bound to values from the parent Entity.
557
- The same resolution logic applies: the runtime matches field names against Key / GSI definitions.
559
+ Relation は、親 Entity の値にバインドされたクエリである。
560
+ 同じ解決ロジックが適用される: runtime はフィールド名を Key / GSI 定義に照合する。
558
561
 
559
562
  ```ts
560
563
  @hasMany(() => GroupMembershipModel, { userId: 'userId' }, {
@@ -565,154 +568,168 @@ groups!: GroupMembershipModel[];
565
568
 
566
569
  ### Field Decorators
567
570
 
568
- Type-safe semantic decorators define DynamoDB attribute types.
569
- Compile-time checks based on `ClassFieldDecoratorContext` detect mismatches between declared TypeScript types and DynamoDB types.
571
+ 型安全なセマンティック decorator DynamoDB の属性型を定義する。
572
+ `ClassFieldDecoratorContext` に基づくコンパイル時チェックが、宣言された TypeScript の型と
573
+ DynamoDB の型の不一致を検出する。
570
574
 
571
575
  ```ts
572
576
  @string userId!: string; // DynamoDB S
573
577
  @number amount!: number; // DynamoDB N
574
- @datetime createdAt!: Date; // stored as an ISO 8601 string
578
+ @datetime createdAt!: Date; // ISO 8601 文字列として保存される
575
579
  @boolean isActive!: boolean; // DynamoDB BOOL
576
580
  ```
577
581
 
578
582
  ### Select = Projection + Type Inference
579
583
 
580
- The `select` object controls both DynamoDB `ProjectionExpression` generation and TypeScript return type inference.
581
- Only requested fields are returned, and only those fields appear in the result type.
584
+ `select` オブジェクトは DynamoDB `ProjectionExpression` 生成と TypeScript の返り値型推論の
585
+ 両方を制御する。
586
+ 要求したフィールドだけが返り、結果の型にもそのフィールドだけが現れる。
582
587
 
583
588
  ### Planner = Inspectable Execution Plan
584
589
 
585
- Before DynamoDB is accessed, the runtime generates an execution plan.
586
- Plans can be used for tests, debugging, and RCU estimation.
590
+ DynamoDB にアクセスする前に、runtime が実行計画(execution plan)を生成する。
591
+ plan はテスト・デバッグ・RCU 見積もりに利用できる。
587
592
 
588
- ## Architecture
593
+ ## 🏗️ Architecture
589
594
 
590
- ```text
591
- Layer 1: Schema Definition
592
- Entity / Field / Key / GSI / Relation / Embedded
593
- -> EntityMetadata Registry
594
-
595
- Layer 2: Query Builder
596
- Type-safe DSL: key resolution, select validation
597
- -> ExecutionPlan
598
-
599
- Layer 3: Query Planner
600
- Relation traversal, Get vs Query vs BatchGet decisions
601
- Projection generation, GSI selection
602
- -> DynamoDBOperation[]
603
-
604
- Layer 4: Executor
605
- DynamoDB DocumentClient wrapper
606
- -> raw Items
607
-
608
- Layer 5: Hydrator
609
- raw Items -> Partial Entity instances
610
- type conversion, Embedded reconstruction, metadata attachment
595
+ GraphDDB のクエリは 5 つのレイヤーを通って実行される:
596
+
597
+ ```mermaid
598
+ flowchart TD
599
+ L1["🧩 Layer 1: Schema Definition<br/>Entity / Field / Key / GSI / Relation / Embedded"]
600
+ L2["🔧 Layer 2: Query Builder<br/>型安全な DSL: key 解決 / select バリデーション"]
601
+ L3["🗺️ Layer 3: Query Planner<br/>Relation traversal / Get vs Query vs BatchGet 判定<br/>Projection 生成 / GSI 選択"]
602
+ L4["⚡ Layer 4: Executor<br/>DynamoDB DocumentClient ラッパー"]
603
+ L5["💧 Layer 5: Hydrator<br/>型変換 / Embedded 再構築 / メタデータ付与"]
604
+
605
+ L1 -->|EntityMetadata Registry| L2
606
+ L2 -->|ExecutionPlan| L3
607
+ L3 -->|"DynamoDBOperation[]"| L4
608
+ L4 -->|raw Items| L5
609
+ L5 -->|Partial Entity インスタンス| OUT(["✅ 型付き結果"])
611
610
  ```
612
611
 
613
- ## Design Rules
612
+ ## 📐 Design Rules
614
613
 
615
- | Rule | Description |
614
+ | ルール | 内容 |
616
615
  |------|-------------|
617
- | Service Boundary First | One table represents one service boundary, not one entity. |
618
- | Access Pattern First | Model DynamoDB access patterns before modeling tables or ORM-style entities. |
619
- | Natural Mapping | Map Query / Get / BatchGet / Projection / cursor to a GraphQL-like query shape. |
620
- | Inspectable Plans | DynamoDB operations and execution plans are inspectable before execution. |
621
- | Single-item query | `query` is only for PK or unique GSI lookups. Use `list` for keys that can return multiple items. |
622
- | Explicit limits | `hasMany` and `list` require default/max limits. |
623
- | Bounded depth | Relation traversal defaults to depth=1. Deeper traversal must be explicitly allowed with `maxDepth`. |
624
- | Parallel relations | Independent relation sub-queries, BatchGet chunks, and nested resolution are dispatched concurrently (bounded). DynamoDB is HTTP-based, so there is no connection to serialize on. |
625
- | No Scan | Full table scans are forbidden. The linter detects and blocks them. |
626
- | Partial select | Query results are partial types. Only selected fields appear in the result type. |
627
- | Structured keys | Keys are built from `k` segment templates; partial keys compile to `begins_with` at a segment boundary. |
628
- | Typed consistentRead | `consistentRead` is only available for PK queries. GSI queries are rejected at compile time. |
629
- | Server-side filter | `filter` is a declarative DynamoDB `FilterExpression` (AppSync `ModelFilterInput`-compatible), typed against the full entity and able to reference unprojected attributes. It does not reduce RCU, and `limit` is applied before it. |
630
-
631
- ## Architectural Boundaries
632
-
633
- Relation traversal only follows access paths defined on Entities.
634
- Undefined relations, missing GSIs, lists without limits, and traversal that exceeds allowed depth are checked before execution.
635
-
636
- This makes cross-boundary queries and DynamoDB-unfriendly access patterns easier to see in code.
637
- The same architectural constraints apply regardless of whether code is written by humans or generated by AI.
638
-
639
- ## Linter
640
-
641
- Static analysis detects problems in Entity definitions before runtime:
642
-
643
- - Scan usage
644
- - missing limits on `hasMany` / `list`
645
- - ambiguous GSI resolution
646
- - query/list boundary violations for unique GSIs
647
-
648
- ## Beyond the Core
649
-
650
- The query / relation / write API above is the core layer. GraphDDB also ships:
651
-
652
- - **[CQRS contracts](./docs/cqrs-contract.md)** public Query/Command contract models with an
653
- N+1-safe cardinality matrix, cross-contract composition, and context-boundary enforcement.
654
- - **[Mutation → command derivation](./docs/mutation-command-derivation.md)** an internal
655
- write-plan composition DSL behind the public Command IF. Declare a model's write semantics
656
- (`entityWrites`: referential integrity, uniqueness, edge effects, derived counters, outbox
657
- events, idempotency) once; a `mutation` composes one or more write fragments that the compiler
658
- merges into a single atomic `TransactWriteItems`. All write paths route through one shared
659
- `commitTransaction` orchestration.
660
- - **[Opt-in class hydration](./docs/class-hydration.md)** — pass an `options.hydrate` factory to
661
- load a read result onto a host-language domain object instead of the default Typed Plain Object.
662
- Host-only and never serialized into the bridge SSoT. Phase 1 (`query` top-level) is implemented;
663
- `list` / per-relation hydration are planned future phases.
664
- - **[Multi-language (Python bridge)](./docs/python-bridge.md)** — TypeScript is the single source
665
- of truth; generate a Python client + runtime from the same definitions, kept in lockstep by a
666
- TS↔Python conformance suite.
667
- - **[In-memory testing](./docs/testing.md)** `graphddb/testing` runs model-mapping, query-plan,
668
- relation-traversal, and CDC tests **in-process, without Docker**.
669
- - **[CDC emulator](./docs/cdc-emulator.md)** — DynamoDB-Streams-equivalent change events for
670
- driving and testing incremental aggregation locally.
671
-
672
- ## Documentation
673
-
674
- | Document | Description |
616
+ | Service Boundary First | 1テーブルは1エンティティではなく、1つのサービス境界を表す。 |
617
+ | Access Pattern First | テーブルや ORM 風のエンティティをモデル化する前に、DynamoDB のアクセスパターンをモデル化する。 |
618
+ | Natural Mapping | Query / Get / BatchGet / Projection / cursor GraphQL ライクなクエリの形にマップする。 |
619
+ | Inspectable Plans | DynamoDB の操作と実行計画は、実行前に検査可能である。 |
620
+ | Single-item query | `query` PK または unique GSI lookup のみ。複数件返りうるキーには `list` を使う。 |
621
+ | Explicit limits | `hasMany` `list` default/max limit の指定が必須。 |
622
+ | Bounded depth | Relation traversal はデフォルトで depth=1。より深い traversal `maxDepth` で明示的に許可する必要がある。 |
623
+ | Parallel relations | 独立した relation のサブクエリ、BatchGet のチャンク、ネストした解決は並列にディスパッチされる(上限あり)。DynamoDB HTTP ベースのため、直列化すべきコネクションが存在しない。 |
624
+ | No Scan | Full table scan は禁止。linter が検出・ブロックする。 |
625
+ | Partial select | クエリ結果は partial 型。select したフィールドのみが結果の型に現れる。 |
626
+ | Structured keys | キーは `k` segment テンプレートから構築され、partial key segment 境界で `begins_with` にコンパイルされる。 |
627
+ | Typed consistentRead | `consistentRead` PK クエリでのみ利用可能。GSI クエリはコンパイル時に拒否される。 |
628
+ | Server-side filter | `filter` は宣言的な DynamoDB `FilterExpression`(AppSync `ModelFilterInput` 互換)で、エンティティ全体に対して型付けされ、projection されていない属性も参照できる。RCU は削減せず、`limit` はその前に適用される。 |
629
+
630
+ ## 🚧 Architectural Boundaries
631
+
632
+ Relation traversal は、Entity 上に定義されたアクセスパスのみをたどる。
633
+ 未定義の relation、欠けている GSI、limit のない list、許可された depth を超える traversal は、
634
+ 実行前にチェックされる。
635
+
636
+ これにより、境界をまたぐクエリや DynamoDB に不向きなアクセスパターンが、コード上で見えやすくなる。
637
+ コードが人間によって書かれたものであれ、AI によって生成されたものであれ、同じアーキテクチャ上の
638
+ 制約が適用される。
639
+
640
+ ## 🔍 Linter
641
+
642
+ 静的解析が、runtime の前に Entity 定義の問題を検出する:
643
+
644
+ - Scan の使用
645
+ - `hasMany` / `list` での limit 未指定
646
+ - 曖昧な GSI 解決
647
+ - unique GSI に対する query/list の境界違反
648
+
649
+ ## 🧱 Beyond the Core
650
+
651
+ 上記の query / relation / write API がコアレイヤーである。GraphDDB はさらに以下も提供する:
652
+
653
+ - **[CQRS contracts](./docs/cqrs-contract.md)** —— N+1 安全な cardinality matrix、
654
+ contract をまたぐ合成、context 境界の強制を備えた、public Query/Command contract モデル。
655
+ - **[Mutation command derivation](./docs/mutation-command-derivation.md)** —— public
656
+ Command IF の背後にある、内部的な write-plan 合成 DSL。モデルの書き込みセマンティクス
657
+ (`entityWrites`: 参照整合性、一意性、edge effect、派生カウンタ、outbox event、冪等性)を
658
+ 一度宣言すると、`mutation` が1つ以上の write fragment を合成し、コンパイラがそれらを単一の
659
+ atomic `TransactWriteItems` にマージする。すべての書き込みパスは1つの共有された
660
+ `commitTransaction` オーケストレーションを経由する。**リレーション維持アクセスパス**
661
+ (Epic #118)はこれを拡張する: リレーションの `write.maintainedOn` やスカラ `@aggregate`
662
+ フィールドが、source エンティティのライフサイクルに同期して *別の* 行を維持する —— 射影された
663
+ **snapshot**(`SET`)、上限付き **collection**(`list_append`)、スカラ **counter**
664
+ (`@aggregate` `count()` atomic `ADD ±1`)—— source write と同一の atomic transaction
665
+ 合成する([aggregate-counter](./examples/aggregate-counter/) 例を参照)。
666
+ - **[Opt-in class hydration](./docs/class-hydration.md)** —— `options.hydrate` ファクトリを
667
+ 渡すと、read 結果をデフォルトの Typed Plain Object ではなく、ホスト言語のドメインオブジェクトに
668
+ ロードできる。ホスト専用で、bridge SSoT にシリアライズされることはない。Phase 1
669
+ (`query` のトップレベル)は実装済み。`list` / relation ごとの hydration は今後の予定。
670
+ - **[Multi-language (Python bridge)](./docs/python-bridge.md)** —— TypeScript が単一の真実の源
671
+ であり、同じ定義から Python クライアント + runtime を生成し、TS↔Python の conformance スイートで
672
+ 歩調を合わせる。
673
+ - **[In-memory testing](./docs/testing.md)** —— `graphddb/testing` は model-mapping、query-plan、
674
+ relation-traversal、CDC のテストを **Docker なしの in-process** で実行する。
675
+ - **[CDC emulator](./docs/cdc-emulator.md)** —— ローカルでの差分集計を駆動・テストするための、
676
+ DynamoDB Streams 相当の変更イベント。
677
+
678
+ ## 📚 Documentation
679
+
680
+ | ドキュメント | 内容 |
675
681
  |----------|-------------|
676
- | [Specification](./docs/spec.md) | Core API: entities, structured keys/GSIs, query/filter, relations, batch/transactions, design rules, runtime behavior. |
677
- | [CQRS contract layer](./docs/cqrs-contract.md) | Public Query/Command contracts, cardinality matrix, N+1 safety, cross-contract composition, context boundaries. |
678
- | [Mutation → command derivation](./docs/mutation-command-derivation.md) | The internal write-plan composition DSL behind the Command IF: model write-semantics (`entityWrites`), fragment composition, and atomic `TransactWriteItems` derivation. |
679
- | [Class hydration](./docs/class-hydration.md) | Opt-in `options.hydrate` factory that loads a read result onto a host-language object (Phase 1: `query` top-level; Phases 2–3 future). |
680
- | [Multi-language / Python bridge](./docs/python-bridge.md) | TS-as-SSoT code generation and the Python runtime; TS↔Python conformance. |
681
- | [In-memory test adapter](./docs/testing.md) | Docker-free, in-process testing via `graphddb/testing` and `MemoryInspector`. |
682
- | [CDC emulator](./docs/cdc-emulator.md) | Change-event emulator (dev/test) for incremental aggregation patterns. |
683
- | [Benchmark](./benchmark/RESULTS.md) | Library comparison vs. hand-written AWS SDK, ElectroDB, DynamoDB Toolbox, and OneTable. |
684
-
685
- ## Examples
686
-
687
- | Example | Description |
682
+ | [Specification](./docs/spec.md) | コア API: entity、structured keys/GSIsquery/filter、relation、batch/transaction、design rule、runtime の挙動。 |
683
+ | [CQRS contract layer](./docs/cqrs-contract.md) | public Query/Command contract、cardinality matrixN+1 安全性、contract をまたぐ合成、context 境界。 |
684
+ | [Mutation → command derivation](./docs/mutation-command-derivation.md) | Command IF の背後にある内部的な write-plan 合成 DSL: モデルの write セマンティクス(`entityWrites`)、fragment 合成、atomic `TransactWriteItems` の導出。 |
685
+ | [Class hydration](./docs/class-hydration.md) | read 結果をホスト言語のオブジェクトにロードする opt-in `options.hydrate` ファクトリ(Phase 1: `query` トップレベル; Phase 2–3 は今後)。 |
686
+ | [Multi-language / Python bridge](./docs/python-bridge.md) | TS SSoT としたコード生成と Python runtime; TS↔Python conformance |
687
+ | [In-memory test adapter](./docs/testing.md) | `graphddb/testing` `MemoryInspector` による Docker 不要の in-process テスト。 |
688
+ | [CDC emulator](./docs/cdc-emulator.md) | 差分集計パターン向けの変更イベント emulatordev/test)。 |
689
+ | [Benchmark](./benchmark/RESULTS.md) | 手書きの AWS SDKElectroDBDynamoDB Toolbox、OneTable とのライブラリ比較。 |
690
+
691
+ ## 💡 Examples
692
+
693
+ | Example | 内容 |
688
694
  |---------|-------------|
689
- | [user-permissions](./examples/user-permissions/) | Manage users, groups, and permissions with Single Table Design. Includes adjacency lists, inverted indexes, relation traversal, and `explain`. |
690
- | [aggregate-tree-pattern](./examples/aggregate-tree-pattern/) | Incremental tree aggregation (`siteScoreAverage`) driven by the CDC emulator: dirty propagation, throttled sweep, and recompute. |
691
-
692
- ## Features
693
-
694
- - Entity metadata / decorators
695
- - DDBModel base class / connection management
696
- - Type system (SelectableOf, QueryResult, QueryKeyOf)
697
- - CRUD (put, update, delete, conditional writes — `WriteCondition` shares the full declarative filter operator set plus the `cond` raw escape hatch)
698
- - Query / List (planner, executor, hydrator, cursor pagination)
699
- - Type-safe filtering: declarative server-side `filter` (FilterExpression / AppSync `ModelFilterInput`-compatible), plus `Model.col` column refs and the `cond` raw escape hatch
700
- - Structured segment keys (`k` tag): partial keys compile to `begins_with` at a segment boundary
701
- - Explainable execution plans
702
- - Relations (hasMany, belongsTo, hasOne, depth limits, traversal)
703
- - BatchGet optimization
704
- - Parallel relation execution (independent sub-queries, BatchGet chunks, and nested resolution are dispatched concurrently with bounded concurrency)
705
- - Linter (no-scan, require-limit, query-boundary, GSI ambiguity, expanded design rules)
706
- - ConsistentRead type constraints
695
+ | [user-permissions](./examples/user-permissions/) | Single Table Design でユーザー・グループ・パーミッションを管理する。隣接リスト、転置インデックス、relation traversal、`explain` を含む。 |
696
+ | [aggregate-tree-pattern](./examples/aggregate-tree-pattern/) | CDC emulator で駆動される差分的なツリー集計(`siteScoreAverage`): dirty 伝播、throttle 付き sweep、再計算。 |
697
+ | [embedded-snapshot-pattern](./examples/embedded-snapshot-pattern/) | リレーションを **維持アクセスパス** として扱う(Epic #118): `pattern: 'embeddedSnapshot'` のリレーションが、source write と同一の atomic transaction で、非正規化された collection / snapshot を owner 行に同期維持する。 |
698
+ | [aggregate-counter](./examples/aggregate-counter/) | RFC §5.2 の `@aggregate` **counter**: `ThreadPost.created`/`removed` のライフサイクルが、source write と同一の `TransactWriteItems` 内で atomic な `ADD ±1` により `ThreadCounter.postCount` を同期維持する。 |
699
+
700
+ ## Features
701
+
702
+ - Entity メタデータ / decorator
703
+ - DDBModel 基底クラス / コネクション管理
704
+ - 型システム(SelectableOf, QueryResult, QueryKeyOf)
705
+ - CRUD(put, update, delete, 条件付き書き込み —— `WriteCondition` は宣言的 filter operator
706
+ フルセットに加え `cond` raw escape hatch を共有する)
707
+ - Query / List(planner, executor, hydrator, cursor pagination)
708
+ - 型安全な filtering: 宣言的なサーバーサイド `filter`(FilterExpression / AppSync
709
+ `ModelFilterInput` 互換)、加えて `Model.col` のカラム参照と `cond` raw escape hatch
710
+ - 構造化 segment キー(`k` タグ): partial key segment 境界で `begins_with` にコンパイルされる
711
+ - 説明可能な実行計画
712
+ - Relation(hasMany, belongsTo, hasOne, depth 制限, traversal)
713
+ - 維持アクセスパス(Epic #118): `pattern: 'embeddedSnapshot'` のリレーションが非正規化された collection / 単一行 snapshot を owner 行に同期維持し、`@aggregate` `count()` カウンタが atomic な `ADD ±1` で同期する。いずれも source write の atomic transaction に合成される(`maintainedOn` cross-entity トリガ、関数形 `projection`)。同期(`updateMode: 'mutation'`)の維持は TS / Python 両方で動作(conformance 検証済み)。stream / trim / `max()` / `materializedView` / `sparseView` は後続フェーズ
714
+ - BatchGet 最適化
715
+ - 並列 relation 実行(独立したサブクエリ、BatchGet チャンク、ネストした解決を上限付きの
716
+ 並列度で同時にディスパッチ)
717
+ - Linter(no-scan, require-limit, query-boundary, GSI ambiguity, 拡張された design rule)
718
+ - ConsistentRead の型制約
707
719
  - Transaction DSL
708
- - Batch operations
709
- - CQRS Query/Command contract layer (public IF, N+1-safe composition, context boundaries) — see [docs](./docs/cqrs-contract.md)
710
- - Mutation → command derivation: internal write-plan composition DSL (`entityWrites` + `mutation`) behind the Command IF, multi-fragment atomic composition into one `TransactWriteItems` — see [docs](./docs/mutation-command-derivation.md)
711
- - Opt-in class hydration (`options.hydrate`): load a read result onto a host object; host-only, never serialized. Phase 1 (`query` top-level) implemented — see [docs](./docs/class-hydration.md)
712
- - Multi-language: TS-as-SSoT Python code generator + runtime, with TS↔Python conformance — see [docs](./docs/python-bridge.md)
713
- - In-memory test adapter (`graphddb/testing`): Docker-free unit testing — see [docs](./docs/testing.md)
714
- - CDC emulator: DynamoDB-Streams-equivalent change events for dev/test — see [docs](./docs/cdc-emulator.md)
715
-
716
- ## License
720
+ - Batch 操作
721
+ - CQRS Query/Command contract レイヤー(public IF, N+1 安全な合成, context 境界)—— [docs](./docs/cqrs-contract.md) を参照
722
+ - Mutation → command derivation: Command IF の背後にある内部的な write-plan 合成 DSL
723
+ (`entityWrites` + `mutation`)、複数 fragment atomic 合成を1つの `TransactWriteItems`
724
+ —— [docs](./docs/mutation-command-derivation.md) を参照
725
+ - Opt-in class hydration(`options.hydrate`): read 結果をホストオブジェクトにロードする;
726
+ ホスト専用で、シリアライズされない。Phase 1(`query` トップレベル)実装済み
727
+ —— [docs](./docs/class-hydration.md) を参照
728
+ - Multi-language: TS を SSoT とした Python コード生成器 + runtime、TS↔Python の conformance 付き
729
+ —— [docs](./docs/python-bridge.md) を参照
730
+ - In-memory test adapter(`graphddb/testing`): Docker 不要のユニットテスト —— [docs](./docs/testing.md) を参照
731
+ - CDC emulator: dev/test 向けの DynamoDB Streams 相当の変更イベント —— [docs](./docs/cdc-emulator.md) を参照
732
+
733
+ ## 📄 License
717
734
 
718
735
  MIT