@martinffx/atelier 0.1.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/CHANGELOG.md +38 -0
- package/LICENSE +21 -0
- package/README.md +285 -0
- package/agents/architect.md +38 -0
- package/agents/oracle.md +40 -0
- package/agents/recon.md +39 -0
- package/dist/atelier.js +33082 -0
- package/package.json +44 -0
- package/skills/code-conventional-commit/SKILL.md +134 -0
- package/skills/code-conventional-commit/evals/evals.json +23 -0
- package/skills/code-conventional-commit/workspace/iteration-1/eval-0/with_skill/outputs/commit-message.txt +4 -0
- package/skills/code-conventional-commit/workspace/iteration-1/eval-0/with_skill/outputs/eval_metadata.json +6 -0
- package/skills/code-conventional-commit/workspace/iteration-1/eval-1/with_skill/outputs/eval_metadata.json +6 -0
- package/skills/code-conventional-commit/workspace/iteration-1/eval-1/with_skill/outputs/validation-result.txt +16 -0
- package/skills/code-conventional-commit/workspace/iteration-1/eval-2/with_skill/outputs/commit-message.txt +8 -0
- package/skills/code-conventional-commit/workspace/iteration-1/eval-2/with_skill/outputs/eval_metadata.json +6 -0
- package/skills/code-conventional-commit/workspace/iteration-1/report.html +1325 -0
- package/skills/code-debug/SKILL.md +271 -0
- package/skills/code-debug/references/language-tools.md +139 -0
- package/skills/code-debug/references/techniques.md +117 -0
- package/skills/code-docs/SKILL.md +171 -0
- package/skills/code-docs/references/api-docs.md +116 -0
- package/skills/code-docs/references/changelog.md +64 -0
- package/skills/code-docs/references/readme-structure.md +94 -0
- package/skills/code-git-worktrees/SKILL.md +218 -0
- package/skills/code-perf/SKILL.md +163 -0
- package/skills/code-perf/references/common-bottlenecks.md +124 -0
- package/skills/code-perf/references/optimization-patterns.md +167 -0
- package/skills/code-perf/references/profiling-tools.md +126 -0
- package/skills/code-review/SKILL.md +78 -0
- package/skills/code-review/references/output.md +200 -0
- package/skills/code-review/references/reviewers.md +719 -0
- package/skills/code-review/references/rq.md +357 -0
- package/skills/code-review/references/rs.md +30 -0
- package/skills/code-security/SKILL.md +183 -0
- package/skills/code-security/references/owasp-top-10.md +129 -0
- package/skills/code-security/references/security-tools.md +102 -0
- package/skills/code-security/references/vulnerability-patterns.md +129 -0
- package/skills/code-stacked-commit/SKILL.md +155 -0
- package/skills/code-subagents/SKILL.md +184 -0
- package/skills/code-subagents/references/code-quality-reviewer-prompt.md +48 -0
- package/skills/code-subagents/references/implementor-prompt.md +79 -0
- package/skills/code-subagents/references/spec-reviewer-prompt.md +62 -0
- package/skills/oracle-architect/SKILL.md +246 -0
- package/skills/oracle-architect/references/api-design.md +345 -0
- package/skills/oracle-architect/references/data-modeling.md +318 -0
- package/skills/oracle-architect/references/ddd-patterns.md +247 -0
- package/skills/oracle-challenge/SKILL.md +126 -0
- package/skills/oracle-testing/SKILL.md +237 -0
- package/skills/oracle-testing/references/anti-patterns.md +618 -0
- package/skills/oracle-testing/references/boundaries.md +579 -0
- package/skills/oracle-testing/references/core-testing.md +691 -0
- package/skills/oracle-testing/references/edge-testing.md +794 -0
- package/skills/oracle-testing/references/stub-driven-tdd.md +322 -0
- package/skills/oracle-thinkdeep/SKILL.md +164 -0
- package/skills/python-architecture/SKILL.md +148 -0
- package/skills/python-architecture/references/data-modeling.md +558 -0
- package/skills/python-architecture/references/ddd.md +569 -0
- package/skills/python-architecture/references/functional-core.md +337 -0
- package/skills/python-build-tools/SKILL.md +153 -0
- package/skills/python-build-tools/references/basedpyright.md +321 -0
- package/skills/python-build-tools/references/ruff.md +225 -0
- package/skills/python-build-tools/references/uv.md +257 -0
- package/skills/python-fastapi/SKILL.md +184 -0
- package/skills/python-fastapi/references/api-design.md +563 -0
- package/skills/python-fastapi/references/dependencies.md +479 -0
- package/skills/python-fastapi/references/middleware.md +475 -0
- package/skills/python-fastapi/references/validation.md +497 -0
- package/skills/python-modern-python/SKILL.md +201 -0
- package/skills/python-modern-python/references/async.md +76 -0
- package/skills/python-modern-python/references/pattern-matching.md +89 -0
- package/skills/python-modern-python/references/typing.md +96 -0
- package/skills/python-monorepo/SKILL.md +146 -0
- package/skills/python-monorepo/references/docker.md +515 -0
- package/skills/python-monorepo/references/namespace-packages.md +426 -0
- package/skills/python-monorepo/references/workspace-config.md +341 -0
- package/skills/python-sqlalchemy/SKILL.md +119 -0
- package/skills/python-sqlalchemy/references/async.md +51 -0
- package/skills/python-sqlalchemy/references/models.md +67 -0
- package/skills/python-sqlalchemy/references/queries.md +57 -0
- package/skills/python-temporal/SKILL.md +128 -0
- package/skills/python-temporal/references/patterns.md +87 -0
- package/skills/python-temporal/references/testing.md +54 -0
- package/skills/python-testing/SKILL.md +161 -0
- package/skills/python-testing/references/boundaries.md +449 -0
- package/skills/python-testing/references/mocking.md +449 -0
- package/skills/python-testing/references/pytest.md +524 -0
- package/skills/spec-brainstorm/SKILL.md +325 -0
- package/skills/spec-finish/SKILL.md +159 -0
- package/skills/spec-implement/SKILL.md +296 -0
- package/skills/spec-orchestrator/SKILL.md +156 -0
- package/skills/spec-plan/SKILL.md +248 -0
- package/skills/typescript-api-design/SKILL.md +396 -0
- package/skills/typescript-api-design/references/error-responses.md +469 -0
- package/skills/typescript-build-tools/SKILL.md +447 -0
- package/skills/typescript-build-tools/references/biome.md +436 -0
- package/skills/typescript-build-tools/references/bun.md +432 -0
- package/skills/typescript-build-tools/references/turborepo.md +426 -0
- package/skills/typescript-build-tools/references/vitest.md +518 -0
- package/skills/typescript-drizzle-orm/SKILL.md +398 -0
- package/skills/typescript-drizzle-orm/references/cloudflare.md +392 -0
- package/skills/typescript-drizzle-orm/references/entity-pattern.md +412 -0
- package/skills/typescript-drizzle-orm/references/postgresql.md +191 -0
- package/skills/typescript-drizzle-orm/references/repository-pattern.md +479 -0
- package/skills/typescript-drizzle-orm/references/sqlite.md +291 -0
- package/skills/typescript-dynamodb-toolbox/SKILL.md +370 -0
- package/skills/typescript-dynamodb-toolbox/references/entity-layer.md +574 -0
- package/skills/typescript-dynamodb-toolbox/references/error-handling.md +495 -0
- package/skills/typescript-dynamodb-toolbox/references/modeling.md +332 -0
- package/skills/typescript-dynamodb-toolbox/references/repository-layer.md +631 -0
- package/skills/typescript-dynamodb-toolbox/references/testing.md +718 -0
- package/skills/typescript-dynamodb-toolbox/references/transactions.md +423 -0
- package/skills/typescript-effect-ts/SKILL.md +341 -0
- package/skills/typescript-effect-ts/references/core.md +412 -0
- package/skills/typescript-effect-ts/references/error-handling.md +468 -0
- package/skills/typescript-effect-ts/references/resources.md +476 -0
- package/skills/typescript-effect-ts/references/schema.md +536 -0
- package/skills/typescript-effect-ts/references/services.md +461 -0
- package/skills/typescript-fastify/SKILL.md +405 -0
- package/skills/typescript-fastify/references/plugins.md +329 -0
- package/skills/typescript-fastify/references/typeid.md +315 -0
- package/skills/typescript-functional-patterns/SKILL.md +402 -0
- package/skills/typescript-functional-patterns/references/adts.md +497 -0
- package/skills/typescript-functional-patterns/references/branded-types.md +542 -0
- package/skills/typescript-functional-patterns/references/migration-guide.md +695 -0
- package/skills/typescript-functional-patterns/references/option-result.md +585 -0
- package/skills/typescript-testing/SKILL.md +353 -0
- package/skills/typescript-testing/references/mocking.md +675 -0
- package/skills/typescript-testing/references/msw.md +852 -0
- package/skills/typescript-testing/references/snapshot-testing.md +774 -0
|
@@ -0,0 +1,332 @@
|
|
|
1
|
+
# DynamoDB Data Modeling Methodology
|
|
2
|
+
|
|
3
|
+
This guide walks through the complete data modeling process for DynamoDB single-table design.
|
|
4
|
+
|
|
5
|
+
## When to Use DynamoDB
|
|
6
|
+
|
|
7
|
+
### Ideal Scenarios
|
|
8
|
+
|
|
9
|
+
✓ **Well-defined, stable access patterns** - You can enumerate every query upfront
|
|
10
|
+
✓ **Predictable sub-10ms performance** - Need consistent latency at scale
|
|
11
|
+
✓ **Microservices with clear boundaries** - Domain-driven service design
|
|
12
|
+
✓ **Cost predictability matters** - Known capacity or pay-per-request
|
|
13
|
+
✓ **Operational workloads** - CRUD operations over analytics
|
|
14
|
+
|
|
15
|
+
### Avoid DynamoDB When
|
|
16
|
+
|
|
17
|
+
✗ **Prototyping** - Requirements remain fluid
|
|
18
|
+
✗ **Ad-hoc reporting/analytics** - Need flexible querying
|
|
19
|
+
✗ **Team lacks expertise** - Learning curve is steep
|
|
20
|
+
✗ **GraphQL resolvers** - Unpredictable query patterns
|
|
21
|
+
✗ **Complex JOINs required** - Better suited for relational
|
|
22
|
+
|
|
23
|
+
> **The Phase Transition Problem**: Simple key-value feels magical initially, but complexity balloons when modeling real relationships. Teams either abandon DynamoDB or commit fully to single-table patterns—there's no comfortable middle ground.
|
|
24
|
+
|
|
25
|
+
## Modeling Process
|
|
26
|
+
|
|
27
|
+
### Step 1: Define Entity Relationships (ERD)
|
|
28
|
+
|
|
29
|
+
Create an Entity-Relationship Diagram showing all entities and their relationships.
|
|
30
|
+
|
|
31
|
+
**Example: GitHub-like System**
|
|
32
|
+
|
|
33
|
+
```mermaid
|
|
34
|
+
erDiagram
|
|
35
|
+
User ||--o{ Organization : "belongs to"
|
|
36
|
+
User ||--o{ Repository : "owns"
|
|
37
|
+
User }o--o{ Repository : "stars"
|
|
38
|
+
|
|
39
|
+
Organization ||--o{ Repository : "owns"
|
|
40
|
+
Organization ||--o{ User : "has members"
|
|
41
|
+
|
|
42
|
+
Repository ||--o{ Issue : "has"
|
|
43
|
+
Repository ||--o{ PullRequest : "has"
|
|
44
|
+
Repository ||--o{ Fork : "forked to"
|
|
45
|
+
|
|
46
|
+
Issue ||--o{ IssueComment : "has"
|
|
47
|
+
Issue ||--o{ Reaction : "has"
|
|
48
|
+
|
|
49
|
+
PullRequest ||--o{ PRComment : "has"
|
|
50
|
+
PullRequest ||--o{ Reaction : "has"
|
|
51
|
+
|
|
52
|
+
User {
|
|
53
|
+
string Username PK
|
|
54
|
+
string Email
|
|
55
|
+
string Bio
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
Repository {
|
|
59
|
+
string Owner
|
|
60
|
+
string RepoName
|
|
61
|
+
string Description
|
|
62
|
+
boolean IsPrivate
|
|
63
|
+
datetime UpdatedAt
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
Issue {
|
|
67
|
+
string RepoOwner
|
|
68
|
+
string RepoName
|
|
69
|
+
number IssueNumber
|
|
70
|
+
string Title
|
|
71
|
+
string Status
|
|
72
|
+
string Author
|
|
73
|
+
}
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
**Key Questions:**
|
|
77
|
+
- What are the core entities?
|
|
78
|
+
- What are the relationships (1:1, 1:N, N:M)?
|
|
79
|
+
- What attributes belong to each entity?
|
|
80
|
+
|
|
81
|
+
### Step 2: Create Entity Chart (PK/SK Patterns)
|
|
82
|
+
|
|
83
|
+
Map each entity to its Primary Key (PK) and Sort Key (SK) pattern.
|
|
84
|
+
|
|
85
|
+
**GitHub Example - Main Table:**
|
|
86
|
+
|
|
87
|
+
| Entity | Partition Key (PK) | Sort Key (SK) | Purpose |
|
|
88
|
+
|--------|-------------------|---------------|---------|
|
|
89
|
+
| **User** | `ACCOUNT#{username}` | `ACCOUNT#{username}` | Direct user access |
|
|
90
|
+
| **Organization** | `ACCOUNT#{org_name}` | `ACCOUNT#{org_name}` | Direct org access |
|
|
91
|
+
| **Repository** | `REPO#{owner}#{repo_name}` | `REPO#{owner}#{repo_name}` | Direct repo access |
|
|
92
|
+
| **Issue** | `ISSUE#{owner}#{repo}#{padded_num}` | `ISSUE#{owner}#{repo}#{padded_num}` | Direct issue access |
|
|
93
|
+
| **IssueComment** | `REPO#{owner}#{repo}` | `ISSUE#{padded_num}#COMMENT#{id}` | Comments under issue |
|
|
94
|
+
| **Star** | `ACCOUNT#{username}` | `STAR#{owner}#{repo}#{timestamp}` | User's starred repos |
|
|
95
|
+
| **Fork** | `REPO#{original_owner}#{original_repo}` | `FORK#{fork_owner}` | Forks of original |
|
|
96
|
+
| **Counter** | `COUNTER#{org}#{repo}` | `METADATA` | Issue number sequence |
|
|
97
|
+
|
|
98
|
+
**Key Pattern Rules:**
|
|
99
|
+
|
|
100
|
+
1. **Simple Entity**: `ENTITY#{id}` - Single identifier (User, Organization)
|
|
101
|
+
2. **Composite Entity**: `ENTITY#{parent}#{child}` - Multiple identifiers (Repository, Issue)
|
|
102
|
+
3. **Hierarchical**: `PARENT#{id}` / `CHILD#{id}#...` - Parent/child split (Comments under Issue)
|
|
103
|
+
4. **Adjacency List**: Same PK, different SK prefixes - Many-to-many (Stars, Memberships)
|
|
104
|
+
5. **Temporal Sorting**: Include `#{timestamp}` in SK - Time-ordered (Stars, Activities)
|
|
105
|
+
|
|
106
|
+
**Zero-Padding for Numeric IDs:**
|
|
107
|
+
```
|
|
108
|
+
Issue #1 → ISSUE#owner#repo#00000001
|
|
109
|
+
Issue #999 → ISSUE#owner#repo#00000999
|
|
110
|
+
```
|
|
111
|
+
Ensures lexicographic sorting matches numeric order.
|
|
112
|
+
|
|
113
|
+
### Step 3: Design GSI Strategy
|
|
114
|
+
|
|
115
|
+
Plan Global Secondary Indexes for secondary access patterns.
|
|
116
|
+
|
|
117
|
+
**GSI Design Principles:**
|
|
118
|
+
- GSI1/GSI2: Alternate entity lookups, inverted relationships
|
|
119
|
+
- GSI3: Parent-child queries with temporal sorting
|
|
120
|
+
- GSI4: Status-based or filtered queries
|
|
121
|
+
|
|
122
|
+
**GitHub Example - GSI Tables:**
|
|
123
|
+
|
|
124
|
+
#### GSI1: Collection Queries
|
|
125
|
+
|
|
126
|
+
| Entity | GSI1PK | GSI1SK | Access Pattern |
|
|
127
|
+
|--------|--------|--------|----------------|
|
|
128
|
+
| **User** | `ACCOUNT#{username}` | `ACCOUNT#{username}` | Account lookup |
|
|
129
|
+
| **Organization** | `ACCOUNT#{org_name}` | `ACCOUNT#{org_name}` | Account lookup |
|
|
130
|
+
| **Repository** | `REPO#{owner}#{repo}` | `REPO#{owner}#{repo}` | Repo self-reference |
|
|
131
|
+
| **Issue** | `ISSUE#{owner}#{repo}` | `ISSUE#{padded_number}` | **List issues by repo** |
|
|
132
|
+
| **PullRequest** | `PR#{owner}#{repo}` | `PR#{padded_number}` | **List PRs by repo** |
|
|
133
|
+
| **Star** | `REPO#{owner}#{repo}` | `STAR#{username}#{timestamp}` | **List stargazers** |
|
|
134
|
+
|
|
135
|
+
#### GSI2: Entity-Specific Queries
|
|
136
|
+
|
|
137
|
+
| Entity | GSI2PK | GSI2SK | Access Pattern |
|
|
138
|
+
|--------|--------|--------|----------------|
|
|
139
|
+
| **Repository** | `REPO#{owner}#{repo}` | `REPO#{owner}#{repo}` | Repo self-lookup |
|
|
140
|
+
| **Fork** | `REPO#{original_owner}#{original_repo}` | `FORK#{fork_owner}` | **Find all forks of original repo** |
|
|
141
|
+
|
|
142
|
+
#### GSI3: Temporal Hierarchical Queries
|
|
143
|
+
|
|
144
|
+
| Entity | GSI3PK | GSI3SK | Access Pattern |
|
|
145
|
+
|--------|--------|--------|----------------|
|
|
146
|
+
| **User** | `ACCOUNT#{username}` | `ACCOUNT#{username}` | User lookup |
|
|
147
|
+
| **Organization** | `ACCOUNT#{org_name}` | `ACCOUNT#{org_name}` | Org lookup |
|
|
148
|
+
| **Repository** | `ACCOUNT#{owner}` | `#{updated_at}` | **List repos by owner, sorted by date** |
|
|
149
|
+
|
|
150
|
+
The `#` prefix on GSI3SK ensures repositories sort before the account record itself.
|
|
151
|
+
|
|
152
|
+
#### GSI4: Status-Based Filtering
|
|
153
|
+
|
|
154
|
+
| Entity | GSI4PK | GSI4SK | Access Pattern |
|
|
155
|
+
|--------|--------|--------|----------------|
|
|
156
|
+
| **Repository** | `REPO#{owner}#{repo}` | `#REPO#{owner}#{repo}` | Repo metadata |
|
|
157
|
+
| **Open Issue** | `ISSUE#{owner}#{repo}` | `ISSUE#OPEN#{reverse_padded_num}` | **Open issues (newest first)** |
|
|
158
|
+
| **Closed Issue** | `ISSUE#{owner}#{repo}` | `#ISSUE#CLOSED#{padded_num}` | **Closed issues (oldest first)** |
|
|
159
|
+
|
|
160
|
+
**Smart Sort Key Design:**
|
|
161
|
+
```typescript
|
|
162
|
+
// Open issues: Reverse numbering for newest-first
|
|
163
|
+
const reverseNumber = String(99999999 - issue_number).padStart(8, "0")
|
|
164
|
+
GSI4SK = `ISSUE#OPEN#${reverseNumber}`
|
|
165
|
+
|
|
166
|
+
// Closed issues: Normal numbering for oldest-first
|
|
167
|
+
const paddedNumber = String(issue_number).padStart(8, "0")
|
|
168
|
+
GSI4SK = `#ISSUE#CLOSED#${paddedNumber}`
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
The `#` prefix ensures closed issues sort after open issues when querying without range filter.
|
|
172
|
+
|
|
173
|
+
### Step 4: Document Access Patterns
|
|
174
|
+
|
|
175
|
+
List every query the application needs and map to table or GSI.
|
|
176
|
+
|
|
177
|
+
**Format:**
|
|
178
|
+
|
|
179
|
+
| Access Pattern | Query Type | Table/Index | Partition Key | Sort Key Condition |
|
|
180
|
+
|----------------|-----------|-------------|---------------|-------------------|
|
|
181
|
+
| Get user by username | GetItem | Main | `ACCOUNT#{username}` | Equal |
|
|
182
|
+
| List repos by owner | Query | GSI3 | `ACCOUNT#{owner}` | `beginsWith("#")` |
|
|
183
|
+
| List issues for repo | Query | GSI1 | `ISSUE#{owner}#{repo}` | All |
|
|
184
|
+
| List open issues for repo | Query | GSI4 | `ISSUE#{owner}#{repo}` | `beginsWith("ISSUE#OPEN#")` |
|
|
185
|
+
| Get stargazers for repo | Query | GSI1 | `REPO#{owner}#{repo}` | `beginsWith("STAR#")` |
|
|
186
|
+
| Get user's starred repos | Query | Main | `ACCOUNT#{username}` | `beginsWith("STAR#")` |
|
|
187
|
+
| Find forks of repo | Query | GSI2 | `REPO#{owner}#{repo}` | `beginsWith("FORK#")` |
|
|
188
|
+
|
|
189
|
+
**Critical Rule:** If you can't map an access pattern to a GetItem or Query, you need to redesign your keys or add a GSI. **Never rely on Scan in production.**
|
|
190
|
+
|
|
191
|
+
## Key Design Patterns
|
|
192
|
+
|
|
193
|
+
### 1. Overloaded Attributes
|
|
194
|
+
|
|
195
|
+
Use the same key pattern for multiple entity types.
|
|
196
|
+
|
|
197
|
+
**Example:** `ACCOUNT#` prefix for both Users and Organizations
|
|
198
|
+
- Simplifies queries when you want "all accounts"
|
|
199
|
+
- Use entity type attribute (`_et`) to distinguish in code
|
|
200
|
+
|
|
201
|
+
### 2. Item Collections
|
|
202
|
+
|
|
203
|
+
Group related items under the same partition key.
|
|
204
|
+
|
|
205
|
+
**Example:** Comments under an issue
|
|
206
|
+
```
|
|
207
|
+
PK: REPO#owner#repo
|
|
208
|
+
SK: ISSUE#00000001#COMMENT#c1
|
|
209
|
+
SK: ISSUE#00000001#COMMENT#c2
|
|
210
|
+
SK: ISSUE#00000001#COMMENT#c3
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
Query with `partition = "REPO#owner#repo"` and `range.beginsWith("ISSUE#00000001#COMMENT#")`
|
|
214
|
+
|
|
215
|
+
### 3. Adjacency List (Many-to-Many)
|
|
216
|
+
|
|
217
|
+
Store both sides of the relationship as separate items.
|
|
218
|
+
|
|
219
|
+
**Example:** Stars (Users ↔ Repositories)
|
|
220
|
+
|
|
221
|
+
Main Table:
|
|
222
|
+
```
|
|
223
|
+
PK: ACCOUNT#alice SK: STAR#owner#repo1#2024-01-01 (Alice starred repo1)
|
|
224
|
+
PK: ACCOUNT#bob SK: STAR#owner#repo1#2024-01-02 (Bob starred repo1)
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
GSI1:
|
|
228
|
+
```
|
|
229
|
+
GSI1PK: REPO#owner#repo1 GSI1SK: STAR#alice#2024-01-01 (repo1 starred by Alice)
|
|
230
|
+
GSI1PK: REPO#owner#repo1 GSI1SK: STAR#bob#2024-01-02 (repo1 starred by Bob)
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
Enables bidirectional queries:
|
|
234
|
+
- User's starred repos: Query main table with `PK = ACCOUNT#alice`, `SK begins_with STAR#`
|
|
235
|
+
- Repo's stargazers: Query GSI1 with `GSI1PK = REPO#owner#repo1`, `GSI1SK begins_with STAR#`
|
|
236
|
+
|
|
237
|
+
### 4. Sparse Indexes
|
|
238
|
+
|
|
239
|
+
Only items with the GSI attributes appear in the GSI. Use this to filter item types.
|
|
240
|
+
|
|
241
|
+
**Example:** Not all entities have GSI4PK/SK - only repositories and issues appear in GSI4.
|
|
242
|
+
|
|
243
|
+
### 5. Composite Sort Keys
|
|
244
|
+
|
|
245
|
+
Enable complex querying and sorting by encoding multiple values in SK.
|
|
246
|
+
|
|
247
|
+
**Example:** Status + reverse number for newest-open-issues-first
|
|
248
|
+
|
|
249
|
+
## Modeling Checklist
|
|
250
|
+
|
|
251
|
+
Before writing code:
|
|
252
|
+
|
|
253
|
+
- [ ] **ERD Complete** - All entities and relationships documented
|
|
254
|
+
- [ ] **Entity Chart Complete** - Every entity has PK/SK pattern
|
|
255
|
+
- [ ] **GSI Strategy Defined** - Each GSI has clear purpose
|
|
256
|
+
- [ ] **Access Patterns Documented** - Every query mapped to table/GSI
|
|
257
|
+
- [ ] **No Scan Operations** - All queries use GetItem or Query
|
|
258
|
+
- [ ] **Numeric IDs Zero-Padded** - For lexicographic sorting
|
|
259
|
+
- [ ] **Temporal Fields Include Prefix** - `#` prefix for correct ordering
|
|
260
|
+
- [ ] **Many-to-Many Uses Adjacency List** - Both directions queryable
|
|
261
|
+
|
|
262
|
+
## Common Pitfalls
|
|
263
|
+
|
|
264
|
+
### ❌ Forgetting Zero-Padding
|
|
265
|
+
|
|
266
|
+
```typescript
|
|
267
|
+
// WRONG - "Issue 2" sorts after "Issue 10"
|
|
268
|
+
SK: `ISSUE#${owner}#${repo}#${issue_number}`
|
|
269
|
+
|
|
270
|
+
// RIGHT - Proper lexicographic order
|
|
271
|
+
SK: `ISSUE#${owner}#${repo}#${String(issue_number).padStart(8, "0")}`
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
### ❌ Missing Temporal Prefix
|
|
275
|
+
|
|
276
|
+
```typescript
|
|
277
|
+
// WRONG - Timestamps sort before entity records
|
|
278
|
+
GSI3SK: new Date().toISOString() // "2024-01-01..." sorts before "ACCOUNT#..."
|
|
279
|
+
|
|
280
|
+
// RIGHT - # prefix ensures entity sorts first
|
|
281
|
+
GSI3SK: `#${new Date().toISOString()}`
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
### ❌ Not Planning for Many-to-Many
|
|
285
|
+
|
|
286
|
+
```typescript
|
|
287
|
+
// WRONG - Can only query one direction
|
|
288
|
+
PK: ACCOUNT#{user} SK: REPO#{owner}#{repo}
|
|
289
|
+
|
|
290
|
+
// RIGHT - Use adjacency list with GSI inversion
|
|
291
|
+
PK: ACCOUNT#{user} SK: STAR#{owner}#{repo}#{timestamp}
|
|
292
|
+
GSI1PK: REPO#{owner}#{repo} GSI1SK: STAR#{user}#{timestamp}
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
### ❌ Overusing GSIs
|
|
296
|
+
|
|
297
|
+
Each GSI doubles storage costs. Evaluate if access pattern is needed or if client-side filtering suffices.
|
|
298
|
+
|
|
299
|
+
## Example: Full Modeling Session
|
|
300
|
+
|
|
301
|
+
**Requirement:** Model a GitHub-like system
|
|
302
|
+
|
|
303
|
+
**1. ERD:** (See diagram above)
|
|
304
|
+
|
|
305
|
+
**2. Entity Chart:**
|
|
306
|
+
- User: `ACCOUNT#{username}` / `ACCOUNT#{username}`
|
|
307
|
+
- Repository: `REPO#{owner}#{repo}` / `REPO#{owner}#{repo}`
|
|
308
|
+
- Issue: `ISSUE#{owner}#{repo}#{padded_num}` / same
|
|
309
|
+
- IssueComment: `REPO#{owner}#{repo}` / `ISSUE#{padded_num}#COMMENT#{id}`
|
|
310
|
+
- Star: `ACCOUNT#{user}` / `STAR#{owner}#{repo}#{timestamp}`
|
|
311
|
+
|
|
312
|
+
**3. GSI Strategy:**
|
|
313
|
+
- GSI1: Collection queries (issues by repo, stargazers)
|
|
314
|
+
- GSI2: Fork relationships
|
|
315
|
+
- GSI3: Temporal owner queries (repos by owner with sorting)
|
|
316
|
+
- GSI4: Status filtering (open vs closed issues)
|
|
317
|
+
|
|
318
|
+
**4. Access Patterns:**
|
|
319
|
+
- ✓ Get user → Main table GetItem
|
|
320
|
+
- ✓ List user's repos → GSI3 Query with reverse sort
|
|
321
|
+
- ✓ List issues for repo → GSI1 Query
|
|
322
|
+
- ✓ List open issues → GSI4 Query with beginsWith
|
|
323
|
+
- ✓ Get stargazers → GSI1 Query
|
|
324
|
+
- ✓ Get user's stars → Main table Query
|
|
325
|
+
|
|
326
|
+
**5. Validate:**
|
|
327
|
+
- No Scan operations needed ✓
|
|
328
|
+
- All numeric IDs padded ✓
|
|
329
|
+
- Temporal fields prefixed ✓
|
|
330
|
+
- Many-to-many (stars) use adjacency list ✓
|
|
331
|
+
|
|
332
|
+
**Ready to implement!**
|