Feed Diagrams
B1 ACCESS PATTERN MATRIX
flowchart LR
subgraph SVC["Consumers / Services"]
U1["FEED-001 UserSvc<br>Get User"]
P2["FEED-002 PostSvc<br>List Posts"]
F3["FEED-003 FeedSvc<br>Get Home Feed"]
P4["FEED-004 PostSvc<br>Get Post"]
E5["FEED-005 EngageSvc<br>List Likes"]
E6["FEED-006 EngageSvc<br>List Comments"]
S7["FEED-007 SearchSvc<br>Posts by Tag"]
G8["FEED-008 GraphSvc<br>Followers of"]
N9["FEED-009 NotifySvc<br>Notifications (unread)"]
O10["FEED-010 Ops<br>Top Authors (period)"]
end
subgraph PATHS["Physical Access Paths"]
B0["BASE TABLE<br>PK+SK"]
G1["GSI1 PostsByAuthor<br>PK: USER#<author><br>SK: <ts>#POST#<id>"]
G2["GSI2 PostsByTag<br>PK: TAG#<tag><br>SK: <ts>#POST#<id>"]
G3["GSI3 FeedByUser<br>PK: FEED#<user><br>SK: <ts>#<shard>#POST#<id>"]
GF["GSI_FOL Followers<br>PK: FOLLOWEE#<user><br>SK: <ts>#FOLLOWER#<id>"]
GN["GSI_NOTIF Unread<br>PK: USER#<id>#UNREAD<br>SK: <ts>#<notif>"]
M1["Metrics / Leaderboard<br>(precomputed via Streams→Lambda)"]
end
U1 -- "GetItem<br>PK=USER#id, SK=META" --> B0
P2 -- "Query<br>GSI1PK=USER#author<br>DESC" --> G1
F3 -- "Query<br>GSI3PK=FEED#user<br>DESC" --> G3
P4 -- "GetItem<br>PK=POST#id, SK=META" --> B0
E5 -- "Query<br>PK=POST#id + begins_with(SK,'LIKE#')<br>DESC" --> B0
E6 -- "Query<br>PK=POST#id + begins_with(SK,'COMMENT#')<br>ASC" --> B0
S7 -- "Query<br>GSI2PK=TAG#tag<br>DESC" --> G2
G8 -- "Query<br>PK=FOLLOWEE#user<br>DESC" --> GF
N9 -- "Query<br>PK=USER#id#UNREAD<br>DESC" --> GN
O10 -- Read<br>Top N by month --> M1
U1:::base
P2:::gsi
F3:::gsi
P4:::base
E5:::base
E6:::base
S7:::gsi
G8:::gsi
N9:::gsi
O10:::off
B0:::base
G1:::gsi
G2:::gsi
G3:::gsi
GF:::gsi
GN:::gsi
M1:::off
classDef base fill:#ecfdf5,stroke:#10b981,color:#064e3b
classDef gsi fill:#fff7ed,stroke:#fb923c,color:#7c2d12
classDef wr fill:#eff6ff,stroke:#60a5fa,color:#0c4a6e
classDef off fill:#f8fafc,stroke:#94a3b8,color:#334155,stroke-dasharray:4 3
B2 KEY DESIGN CHEATSHEET
flowchart TB
%% Styles
classDef base fill:#ecfdf5,stroke:#10b981,rx:6,ry:6,color:#064e3b;
classDef gsi fill:#fff7ed,stroke:#fb923c,rx:6,ry:6,color:#7c2d12;
classDef item fill:#f8fafc,stroke:#94a3b8,rx:6,ry:6,color:#334155;
classDef rule fill:#eff6ff,stroke:#60a5fa,rx:6,ry:6,color:#0c4a6e;
classDef misc fill:#fefce8,stroke:#eab308,rx:6,ry:6,color:#713f12;
subgraph BASE["Base Table Keys"]
BPK["PK formats:
• USER#<user_id>
• POST#<post_id>
• FOLLOWEE#<user_id>"]:::base
BSK["SK formats:
• META
• COMMENT#<comment_id>
• LIKE#<user_id>
• TAG#<tag>
• FOLLOWER#<user_id>#<ts>"]:::base
end
subgraph GSIs["Global Secondary Indexes"]
G1["GSI1 PostsByAuthor
PK: USER#<author_id>
SK: <ts>#POST#<post_id> (DESC)"]:::gsi
G2["GSI2 PostsByTag
PK: TAG#<tag>
SK: <ts>#POST#<post_id> (DESC)"]:::gsi
G3["GSI3 FeedByUser
PK: FEED#<user_id>
SK: <ts>#<shard>#POST#<post_id> (DESC)"]:::gsi
G4["GSI_FOL Followers
PK: FOLLOWEE#<user_id>
SK: <ts>#FOLLOWER#<follower_id>"]:::gsi
G5["GSI_NOTIF Unread
PK: USER#<user_id>#UNREAD
SK: <ts>#<notif_id>"]:::gsi
end
subgraph ITEMS["Item Types & SK prefixes"]
UU["User
PK=USER#id, SK=META"]:::item
UP["Post (meta)
PK=POST#id, SK=META"]:::item
UC["Comment
PK=POST#id, SK=COMMENT#<id>"]:::item
UL["Like
PK=POST#id, SK=LIKE#<user_id>"]:::item
UT["Post Tag
PK=POST#id, SK=TAG#<tag>"]:::item
UF["Follow Edge
PK=FOLLOWEE#user, SK=FOLLOWER#follower#<ts>"]:::item
UN["Notification (Unread)
PK=USER#id#UNREAD, SK=<ts>#<notif_id>"]:::item
end
subgraph RULES["Write Rules / Idempotency / Ordering"]
R1["Create Post:
PutItem IF attribute_not_exists(PK) AND attribute_not_exists(SK)"]:::rule
R2["Add Like:
PutItem IF attribute_not_exists(SK) (idempotent)"]:::rule
R3["Add Comment:
PutItem IF attribute_not_exists(SK) (idempotent by comment_id)"]:::rule
R4["Fanout → Feed:
PutItem to GSI3 partition(s) with sharded SK"]:::rule
R5["Follow:
PutItem IF attribute_not_exists(SK)"]:::rule
end
subgraph MISC["TTL / Streams / Sharding / Projection"]
T1["TTL:
• ephemeral notifications (read true → move or delete)
• temporary fanout artifacts"]:::misc
T2["Streams:
• Build fanout / notifications
• Metrics (top authors, tag velocity)"]:::misc
T3["Sharding:
• home feed SK: <ts>#<shard>#POST#<id>"]:::misc
T4["Projection:
• minimal INCLUDE on GSIs to keep RCU low"]:::misc
end
%% Link the sections
BPK --> BSK
GSIs --> ITEMS
ITEMS --> RULES
RULES --> MISC
B3 ITEM COLLECTION POST
flowchart TB
%% B3 Item Collection: PK=POST#<id>
classDef head fill:#eef5ff,stroke:#5b8def,stroke-width:1px,rx:6,ry:6;
classDef item fill:#ffffff,stroke:#94a3b8,rx:6,ry:6;
classDef tag fill:#dcfce7,stroke:#22c55e,rx:6,ry:6;
subgraph PK["PK = POST#abc123"]
direction TB
META["SK = META \n {author_id,created_at,visibility,media_ref}"]:::head
C1["SK = COMMENT#c001 \n {user_id,created_at,text_len}"]:::item
L1["SK = LIKE#u001 \n {created_at}"]:::item
T1["SK = TAG#dynamodb"]:::tag
end
B4 INDEX OVERLAY
flowchart LR
%% B4 Index Overlay: GSI1 PostsByAuthor, GSI2 PostsByTag, GSI3 FeedByUser
classDef index fill:#f5f3ff,stroke:#8b5cf6,rx:6,ry:6;
classDef proj fill:#ffffff,stroke:#94a3b8,rx:6,ry:6;
subgraph GSI1["PostsByAuthor (PK=author, SK=ts DESC)"]
P1_PK[("PK = USER#<author_id>")]:::index
P1_SK[("SK = <ts>#POST#<id>")]:::index
end
subgraph GSI2["PostsByTag (PK=tag, SK=ts DESC)"]
T_PK[("PK = TAG#<tag>")]:::index
T_SK[("SK = <ts>#POST#<id>")]:::index
end
subgraph GSI3["FeedByUser (PK=user, SK=ts#shard#POST#id)"]
F_PK[("PK = FEED#<user_id>")]:::index
F_SK[("SK = <ts>#<shard>#POST#<id>")]:::index
end
B5 SHARDED FEEDS
flowchart TB
%% B5 Sharded Feeds: write-time fanout with shard
classDef svc fill:#f0f9ff,stroke:#0ea5e9,rx:6,ry:6;
classDef idx fill:#f5f3ff,stroke:#8b5cf6,rx:6,ry:6;
classDef note fill:#ecfeff,stroke:#06b6d4,rx:6,ry:6;
subgraph PostSvc["Post Service"]
p1["New post created"]:::svc
p2["Resolve followers (bounded)"]:::svc
p3["For each follower: shard = hash(post_id+follower_id)%N"]:::svc
p4["Write to GSI3: PK=FEED#<follower>, SK=<ts>#<shard>#POST#<id>"]:::svc
end
subgraph DDB["GSI3 FeedByUser"]
pkA[("PK = FEED#U001")]:::idx
sk["SK = <ts>#<shard>#POST#<id> DESC"]:::idx
end
q1(["Query: PK=FEED#<user> order by SK DESC limit 50"]):::note
p1 --> p2 --> p3 --> p4 --> pkA
q1 --> pkA
B6 STREAMS PIPELINE
flowchart LR
%% B6 Streams Pipeline: notifications, counters, search
classDef ddb fill:#ecfdf5,stroke:#10b981,rx:6,ry:6;
classDef lam fill:#f0f9ff,stroke:#0ea5e9,rx:6,ry:6;
classDef sink fill:#fff7ed,stroke:#fb923c,rx:6,ry:6;
classDef guard fill:#fef2f2,stroke:#ef4444,rx:6,ry:6;
P["Posts/Engagement mutations"]:::ddb --> S["DynamoDB Streams"]:::ddb --> L["Lambda Multiplexer"]:::lam
L --> N1["Notifications"]
L --> N2["Counters (likes/comments)"]
L --> N3["Search/Index Sync"]
L -. on failure .-> DLQ["DLQ"]:::guard
B7 IDEMPOTENT LIKE SEQUENCE
sequenceDiagram
autonumber
participant C as Client App
participant API as Engage API
participant DDB as DynamoDB
participant STR as Streams
participant L as Lambda (Counters)
C->>API: POST /posts/{id}/like (Idempotency-Key)
API->>DDB: PutItem LIKE#<user> (Condition: not exists)
alt First like
DDB-->>API: OK
DDB-->>STR: INSERT
STR-->>L: Batch
L->>DDB: Update post likes counter (SET likes = if_not_exists(likes,0)+1)
else Duplicate like
DDB-->>API: ConditionalCheckFailed
API-->>C: 200 OK (idempotent)
end
November 5, 2025
November 5, 2025