Diagrams¶
This page collects the architecture and flow diagrams for the Dichit real-time API. Diagrams are authored in Mermaid and render on GitHub and most Markdown toolchains. They are kept in sync with protocol.md, connection.md, authentication.md, rooms.md, and lifecycle.md.
1. Connection lifecycle¶
stateDiagram-v2
[*] --> Connecting: client opens wss://.../ws
Connecting --> Authenticating: upgrade request + token
Authenticating --> Open: token valid
Authenticating --> Closed: token invalid (close 1008)
Open --> Subscribed: auction.subscribe / room.join
Subscribed --> Open: auction.unsubscribe / room.leave
Open --> Closing: client/server close
Subscribed --> Closing: client/server close
Closing --> Closed: cleanup done
Closed --> [*] See lifecycle.md.
2. Authentication flow¶
sequenceDiagram
participant C as Client
participant S as WebSocket Server
participant J as JWT Service
C->>S: GET /ws (Upgrade)
C->>S: Sec-WebSocket-Protocol: websocket.v1, bearer.<token>
S->>J: verifyAccessToken(token)
alt access token valid
J-->>S: { userId, role, type: 'access' }
S-->>C: 101 Switching Protocols
S->>S: build SocketUser (id, role, companyId, permissions)
else missing/invalid/refresh token
J-->>S: verification error
S--xC: close 1008 "Authentication failed"
end See authentication.md.
3. Auction bid flow¶
sequenceDiagram
participant C as Subscriber client
participant S as WebSocket Server
participant U as Unit of Work / DB
participant R as Redis Pub/Sub
participant B as Other clients
C->>S: auction.bid.place { cycleId, amount, idempotencyKey }
S->>S: validate + authorize + rate-limit
S->>U: placeBid (transaction)
alt accepted
U-->>S: bid accepted (bidId, stateVersion)
S-->>C: ack (bid result)
S->>R: publish ws:room:auction:<cycleId>
R--)S: bid.created / bid.updated
S-->>B: bid.created (leading) / bid.updated (outbid)
else rejected by rule
U-->>S: rejection (conflict/forbidden)
S-->>C: error CONFLICT / FORBIDDEN
end See events/auction.md.
4. Broadcast flow¶
sequenceDiagram
participant P as Producer (use case / queue)
participant S1 as WS Server A
participant R as Redis Pub/Sub
participant S2 as WS Server B
participant C1 as Client on A
participant C2 as Client on B
P->>S1: broadcastToRoom(auction:cyc, msg)
S1->>R: publish ws:room:auction:cyc
R-->>S1: deliverFromPubSub
S1-->>C1: send message (room members on A)
R-->>S2: deliverFromPubSub
S2-->>C2: send message (room members on B) See rooms.md.
5. Redis Pub/Sub architecture¶
flowchart TD
subgraph Producers
UC[Use cases / commands]
Q[Background queues]
end
subgraph Redis
GLOBAL[(ws:global)]
ROOM1[(ws:room:auction:cyc1)]
ROOM2[(ws:room:auction:cyc2)]
end
subgraph WS1[WebSocket Server A]
B1[Broadcaster]
SUB1[Redis subscriber]
RM1[(RoomManager)]
SK1[Sockets on A]
end
subgraph WS2[WebSocket Server B]
B2[Broadcaster]
SUB2[Redis subscriber]
RM2[(RoomManager)]
SK2[Sockets on B]
end
UC --> GLOBAL
UC --> ROOM1
UC --> ROOM2
Q --> GLOBAL
GLOBAL --- SUB1 --- B1 --- SK1
ROOM1 --- SUB1
ROOM2 --- SUB1
GLOBAL --- SUB2 --- B2 --- SK2
ROOM1 --- SUB2
ROOM2 --- SUB2
SK1 --> RM1
SK2 --> RM2 Key points:
- Producers publish to a channel (
ws:globalorws:room:<room>). - Every WS server holds a Redis subscriber connection per channel it needs.
Broadcaster.deliverFromPubSubfans out to local room members only.- A room channel subscription is dropped when no server has members.
6. Multiple WebSocket servers¶
flowchart LR
LB[Load Balancer]
subgraph Pool[WebSocket servers]
S1[Server A<br/>in-memory rooms + rate buckets]
S2[Server B<br/>in-memory rooms + rate buckets]
S3[Server C<br/>in-memory rooms + rate buckets]
end
R[Redis Pub/Sub]
DB[(PostgreSQL)]
C1[Client 1]
C2[Client 2]
C3[Client 3]
C1 --> LB
C2 --> LB
C3 --> LB
LB --> S1
LB --> S2
LB --> S3
S1 <--> R
S2 <--> R
S3 <--> R
S1 --> DB
S2 --> DB
S3 --> DB Implications:
- Any client can be served by any instance; room fan-out works globally through Redis.
- Room membership and rate-limit buckets are per instance.
- A client reconnecting may land on a different instance; recover via
auction.snapshot+lastSequence.
7. Room architecture¶
flowchart TD
A[Connection<br/>userId=sub_1] --> R1[auction:cyc1]
B[Connection<br/>userId=sub_2] --> R1
C[Connection<br/>company com_1] --> R1
C --> R2[company:com_1]
D[Connection<br/>userId=sub_1 2nd device] --> R3[user:sub_1]
A --> R3
E[SUPERADMIN connection] --> R4[admins]
A --> R5[notifications]
R1 --> Redis1[(ws:room:auction:cyc1)]
R2 --> Redis2[(ws:room:company:com_1)]
R4 --> Redis3[(ws:room:admins)] Room rules summary (see rooms.md):
| Room | Allowed joiner |
|---|---|
auction:<id> | any authenticated connection with room:join (real data via auction.subscribe) |
company:<id> | own company only |
user:<id> | self only |
admins | SUPERADMIN only |
notifications | any authenticated connection |
8. Event routing¶
flowchart TD
RAW[Raw WebSocket frame] --> JSON[Parse JSON]
JSON -->|fail| ERR1[error BAD_REQUEST]
JSON --> ENV[Validate envelope]
ENV -->|fail| ERR1
ENV --> ROUTE[Lookup route by type]
ROUTE -->|unknown| ERR2[error BAD_REQUEST unsupported type]
ROUTE --> AUTH[Authenticate]
AUTH -->|fail| ERR3[error UNAUTHORIZED]
AUTH --> RATE[Rate limit user + IP]
RATE -->|exceeded| ERR4[error RATE_LIMITED]
RATE --> VALID[Validate data (Zod)]
VALID -->|fail| ERR5[error BAD_REQUEST]
VALID --> PERM[Authorize permissions]
PERM -->|denied| ERR6[error FORBIDDEN]
PERM --> HANDLER[Execute handler]
HANDLER -->|result| ACK[ack envelope]
HANDLER -->|thrown| MAP[Map error]
MAP --> ERR7[error INTERNAL / domain code]
ACK --> SEND[Send to socket]
ERR1 --> SEND
ERR2 --> SEND
ERR3 --> SEND
ERR4 --> SEND
ERR5 --> SEND
ERR6 --> SEND
ERR7 --> SEND 9. Authorization flow¶
flowchart TD
start([Message received]) --> identify{Has authenticated user?}
identify -->|no| unauth[error UNAUTHORIZED]
identify -->|yes| perm{Route requires permissions?}
perm -->|none required| allow[Execute handler]
perm -->|required P1..Pn| check{Does connection have all P1..Pn?}
check -->|yes| allow
check -->|no| denied[error FORBIDDEN]
allow --> result[ack / domain events] Permission sets by role (see authentication.md):
| Permission | SUBSCRIBER | COMPANY | SUPERADMIN |
|---|---|---|---|
auction:subscribe | ✅ | ✅ | ✅ |
auction:bid:place | ✅ | ❌ | ❌ |
auction:staff | ❌ | ✅ | ✅ |
broadcast:receive | ❌ | ✅ | ✅ |
room:join / room:leave | ✅ | ✅ | ✅ |
Keeping diagrams in sync¶
When protocol behaviour changes, update the matching diagram in this file and the related document in the same change:
| Diagram | Owning document |
|---|---|
| 1 · 2 · 3 | connection.md, authentication.md, events/auction.md |
| 4 · 5 · 6 · 7 | rooms.md |
| 8 · 9 | protocol.md, authentication.md |