Skip to content

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#&lt;author&gt;<br>SK: &lt;ts&gt;#POST#&lt;id&gt;"]
        G2["GSI2 PostsByTag<br>PK: TAG#&lt;tag&gt;<br>SK: &lt;ts&gt;#POST#&lt;id&gt;"]
        G3["GSI3 FeedByUser<br>PK: FEED#&lt;user&gt;<br>SK: &lt;ts&gt;#&lt;shard&gt;#POST#&lt;id&gt;"]
        GF["GSI_FOL Followers<br>PK: FOLLOWEE#&lt;user&gt;<br>SK: &lt;ts&gt;#FOLLOWER#&lt;id&gt;"]
        GN["GSI_NOTIF Unread<br>PK: USER#&lt;id&gt;#UNREAD<br>SK: &lt;ts&gt;#&lt;notif&gt;"]
        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