Skip to content

Commit 8a75d00

Browse files
committed
Add component architecture diagrams to aid in visualizing this new system
1 parent 74acf1c commit 8a75d00

File tree

1 file changed

+187
-48
lines changed

1 file changed

+187
-48
lines changed

docs/implementation-plans/sse-communication.md

Lines changed: 187 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,193 @@ Support critical system events like forcing a user to logout when viewing any pa
2727

2828
---
2929

30+
## Architecture Diagram
31+
32+
### Overall System Architecture
33+
34+
```mermaid
35+
graph TB
36+
subgraph Frontend["Frontend (Browser)"]
37+
Tab1["Browser Tab 1<br/>EventSource<br/>(Coach)"]
38+
Tab2["Browser Tab 2<br/>EventSource<br/>(Coachee)"]
39+
end
40+
41+
subgraph Nginx["Nginx Reverse Proxy"]
42+
SSERoute["/api/sse<br/>proxy_buffering off<br/>proxy_read_timeout 24h"]
43+
end
44+
45+
subgraph Backend["Backend (Single Instance)"]
46+
Handler["SSE Handler<br/>(handler.rs)<br/>• Extract AuthenticatedUser<br/>• Create channel<br/>• Register connection"]
47+
48+
Manager["SSE Manager<br/>(manager.rs)<br/>• DashMap connections<br/>• Filter by scope<br/>• Route messages"]
49+
50+
Controller["Action Controller<br/>(action_controller.rs)<br/>• Create resource in DB<br/>• Determine recipient<br/>• Send SSE message"]
51+
52+
DB[(PostgreSQL)]
53+
end
54+
55+
Tab1 -->|"GET /api/sse<br/>(session cookie)"| SSERoute
56+
Tab2 -->|"GET /api/sse<br/>(session cookie)"| SSERoute
57+
58+
SSERoute -->|"Long-lived connection"| Handler
59+
60+
Handler -->|"register_connection(metadata)"| Manager
61+
62+
Controller -->|"send_message(SseMessage)"| Manager
63+
Controller -->|"Save resource"| DB
64+
65+
Manager -.->|"Event stream"| Handler
66+
Handler -.->|"SSE events"| SSERoute
67+
SSERoute -.->|"Server-Sent Events"| Tab1
68+
SSERoute -.->|"Server-Sent Events"| Tab2
69+
70+
style Manager fill:#b3e5fc,stroke:#01579b,stroke-width:2px,color:#000
71+
style Handler fill:#fff9c4,stroke:#f57f17,stroke-width:2px,color:#000
72+
style Controller fill:#f8bbd0,stroke:#880e4f,stroke-width:2px,color:#000
73+
style SSERoute fill:#c8e6c9,stroke:#1b5e20,stroke-width:2px,color:#000
74+
```
75+
76+
### Message Flow Sequence
77+
78+
```mermaid
79+
sequenceDiagram
80+
participant Coach as Coach Browser
81+
participant Coachee as Coachee Browser
82+
participant Nginx as Nginx
83+
participant Handler as SSE Handler
84+
participant Manager as SSE Manager
85+
participant Controller as Action Controller
86+
participant DB as Database
87+
88+
Note over Coach,Coachee: Connection Establishment
89+
Coach->>+Nginx: GET /api/sse (session cookie)
90+
Nginx->>+Handler: Forward request
91+
Handler->>Handler: Extract user from<br/>AuthenticatedUser
92+
Handler->>Manager: register_connection(coach_metadata)
93+
Handler-->>Coach: SSE connection established
94+
95+
Coachee->>+Nginx: GET /api/sse (session cookie)
96+
Nginx->>+Handler: Forward request
97+
Handler->>Handler: Extract user from<br/>AuthenticatedUser
98+
Handler->>Manager: register_connection(coachee_metadata)
99+
Handler-->>Coachee: SSE connection established
100+
101+
Note over Coach,DB: Resource Creation Flow
102+
Coach->>Controller: POST /actions<br/>{action data}
103+
Controller->>DB: Insert action
104+
DB-->>Controller: Action saved
105+
Controller->>Controller: Determine recipient<br/>(Coachee)
106+
Controller->>Manager: send_message(SseMessage)<br/>scope: User{coachee_id}
107+
Manager->>Manager: Filter connections<br/>by user_id
108+
Manager-->>Handler: Send to Coachee's channel
109+
Handler-->>Nginx: SSE event
110+
Nginx-->>Coachee: event: action_created<br/>data: {action}
111+
Controller-->>Coach: HTTP 201 Created<br/>{action}
112+
113+
Note over Coachee: Coachee sees action immediately
114+
```
115+
116+
### SSE Manager Internal Structure
117+
118+
```mermaid
119+
graph LR
120+
subgraph "SseManager (In-Memory)"
121+
DashMap["DashMap&lt;ConnectionId, Metadata&gt;"]
122+
123+
subgraph Connections["Active Connections"]
124+
C1["conn_uuid_1<br/>• user_id: coach_id<br/>• sender: Channel"]
125+
C2["conn_uuid_2<br/>• user_id: coachee_id<br/>• sender: Channel"]
126+
C3["conn_uuid_3<br/>• user_id: coach_id<br/>• sender: Channel"]
127+
end
128+
end
129+
130+
subgraph "Message Routing"
131+
Msg["SseMessage<br/>• event: ActionCreated<br/>• scope: User{coachee_id}"]
132+
Filter{"Filter by<br/>scope"}
133+
end
134+
135+
Msg --> Filter
136+
Filter -->|"user_id == coachee_id"| C2
137+
Filter -.->|"Skip"| C1
138+
Filter -.->|"Skip"| C3
139+
140+
DashMap --- Connections
141+
142+
style C2 fill:#81c784,stroke:#2e7d32,stroke-width:2px,color:#000
143+
style C1 fill:#ef9a9a,stroke:#c62828,stroke-width:2px,color:#000
144+
style C3 fill:#ef9a9a,stroke:#c62828,stroke-width:2px,color:#000
145+
style Filter fill:#ffb74d,stroke:#e65100,stroke-width:2px,color:#000
146+
```
147+
148+
### Event Types and Scopes
149+
150+
```mermaid
151+
graph TD
152+
subgraph "SseEvent Types"
153+
Session["Session-Scoped<br/>• ActionCreated<br/>• ActionUpdated<br/>• ActionDeleted<br/>• NoteCreated<br/>• NoteUpdated<br/>• NoteDeleted"]
154+
155+
Relationship["Relationship-Scoped<br/>• AgreementCreated<br/>• AgreementUpdated<br/>• AgreementDeleted<br/>• GoalCreated<br/>• GoalUpdated<br/>• GoalDeleted"]
156+
157+
System["System Events<br/>• ForceLogout"]
158+
end
159+
160+
subgraph "MessageScope"
161+
User["User Scope<br/>Send to specific user_id<br/>(all their connections)"]
162+
Broadcast["Broadcast Scope<br/>Send to all connected users"]
163+
end
164+
165+
Session --> User
166+
Relationship --> User
167+
System --> User
168+
System --> Broadcast
169+
170+
style Session fill:#b3e5fc,stroke:#01579b,stroke-width:2px,color:#000
171+
style Relationship fill:#f8bbd0,stroke:#880e4f,stroke-width:2px,color:#000
172+
style System fill:#ffcdd2,stroke:#b71c1c,stroke-width:2px,color:#000
173+
style User fill:#c8e6c9,stroke:#1b5e20,stroke-width:2px,color:#000
174+
style Broadcast fill:#fff9c4,stroke:#f57f17,stroke-width:2px,color:#000
175+
```
176+
177+
### Connection Lifecycle
178+
179+
```mermaid
180+
stateDiagram-v2
181+
[*] --> Connecting: User opens browser
182+
183+
Connecting --> Authenticating: GET /api/sse
184+
Authenticating --> Registered: Session cookie valid
185+
Authenticating --> [*]: Auth failed (401)
186+
187+
Registered --> Active: Connection in DashMap
188+
189+
Active --> ReceivingEvents: Listening for events
190+
ReceivingEvents --> Active: Event received
191+
192+
Active --> KeepAlive: Every 15 seconds
193+
KeepAlive --> Active: Heartbeat sent
194+
195+
Active --> Disconnecting: Browser closed/<br/>Network error
196+
Disconnecting --> CleanedUp: unregister_connection()
197+
CleanedUp --> [*]
198+
199+
Active --> ForceDisconnect: 24h timeout (nginx)
200+
ForceDisconnect --> CleanedUp
201+
202+
note right of Active
203+
Connection stored in DashMap:
204+
• connection_id (UUID)
205+
• user_id (from session)
206+
• sender (Channel)
207+
end note
208+
209+
note right of KeepAlive
210+
Prevents nginx from closing
211+
idle connections
212+
end note
213+
```
214+
215+
---
216+
30217
## Phase 0: Docker Compose Documentation
31218

32219
### 0.1 Add SSE Scaling Warning to docker-compose.yaml
@@ -965,54 +1152,6 @@ mod tests {
9651152

9661153
---
9671154

968-
## Architecture Diagram
969-
970-
```
971-
┌─────────────────────────────────────────────────────────────┐
972-
│ Frontend │
973-
│ ┌──────────────────┐ ┌──────────────────┐ │
974-
│ │ Browser Tab 1 │ │ Browser Tab 2 │ │
975-
│ │ EventSource │ │ EventSource │ │
976-
│ │ (user session) │ │ (user session) │ │
977-
│ └────────┬─────────┘ └────────┬─────────┘ │
978-
└───────────┼──────────────────────────┼──────────────────────┘
979-
│ │
980-
│ GET /sse (with cookie) │ GET /sse (with cookie)
981-
│ │
982-
┌───────────┼──────────────────────────┼──────────────────────┐
983-
│ ▼ ▼ Backend │
984-
│ ┌────────────────────────────────────────────────┐ │
985-
│ │ SSE Handler (handler.rs) │ │
986-
│ │ - Extract user from AuthenticatedUser │ │
987-
│ │ - Create channel for connection │ │
988-
│ │ - Register with SseManager │ │
989-
│ └──────────────────┬─────────────────────────────┘ │
990-
│ │ │
991-
│ ▼ │
992-
│ ┌────────────────────────────────────────────────┐ │
993-
│ │ SseManager (manager.rs) │ │
994-
│ │ ┌──────────────────────────────────────────┐ │ │
995-
│ │ │ DashMap<ConnectionId, Metadata> │ │ │
996-
│ │ │ - connection_1 → {user_id, sender} │ │ │
997-
│ │ │ - connection_2 → {user_id, sender} │ │ │
998-
│ │ └──────────────────────────────────────────┘ │ │
999-
│ │ │ │
1000-
│ │ send_message(SseMessage) │ │
1001-
│ │ → Filter connections by scope │ │
1002-
│ │ → Send to matching channels │ │
1003-
│ └──────────────────▲───────────────────────────┘ │
1004-
│ │ │
1005-
│ ┌──────────────────┴───────────────────────────┐ │
1006-
│ │ Action Controller (action_controller.rs) │ │
1007-
│ │ - Create action in DB │ │
1008-
│ │ - Determine OTHER user in relationship │ │
1009-
│ │ - Send User-scoped SseMessage │ │
1010-
│ └───────────────────────────────────────────────┘ │
1011-
└─────────────────────────────────────────────────────────────┘
1012-
```
1013-
1014-
---
1015-
10161155
## Key Design Decisions Summary
10171156

10181157
| Decision | Choice | Rationale |

0 commit comments

Comments
 (0)