This repository has no description
1# User Domain Model (DDD)
2
3This document outlines the domain model for user management within this application, specifically focusing on linking existing Bluesky accounts via the ATProto OAuth flow.
4
5## Bounded Contexts
6
7We introduce a new **User Management Context**, distinct from the Annotations context.
8
91. **User Management Context:** Responsible for representing users within this application, linking them to their external Bluesky identity via OAuth, and managing their session state.
102. **Annotations Context:** (Existing) Manages annotations, fields, and templates. Users from the User Management context will be the actors performing actions within the Annotations context.
113. **ATProto Context:** (Existing) Provides low-level ATProto types (`StrongRef`, `TID`) and potentially infrastructure adapters for interacting with the AT Protocol (including OAuth).
12
13## Layered Architecture (User Management Context)
14
15### 1. Domain Layer
16
17Contains the core representation of a user within this application.
18
19- **Aggregates & Entities:**
20 - `User` (Aggregate Root):
21 - Represents a user account _within this application_.
22 - `id`: `string` (The user's Bluesky DID - `did:plc:...`). This acts as the unique identifier linking to the external identity and the primary key within this context.
23 - `handle`: `string` (The user's Bluesky handle at the time of linking or last refresh). Optional or fetched on demand.
24 - `linkedAt`: `Date` (Timestamp when the account was first linked via OAuth).
25 - `lastLoginAt`: `Date` (Timestamp of the last successful OAuth authentication/refresh).
26 - _(Potentially other application-specific user profile data)_
27 - _Note:_ This aggregate does _not_ directly store OAuth tokens (access/refresh). Token management is handled separately as part of the authentication session state, likely managed by the infrastructure layer based on the OAuth client library's needs.
28
29- **Value Objects:**
30 - `DID`: Represents a validated `did:plc` string.
31 - `Handle`: Represents a validated Bluesky handle string.
32
33- **Domain Events:**
34 - `UserAccountLinked` (`userId`: DID, `handle`: Handle, `linkedAt`: Date) - Dispatched when a user successfully completes the OAuth flow for the first time.
35 - `UserLoggedIn` (`userId`: DID, `loginAt`: Date) - Dispatched on successful authentication/callback completion.
36
37### 2. Application Layer
38
39Orchestrates the process of linking accounts and managing user data.
40
41- **Use Cases / Application Services:**
42 - `InitiateOAuthSignInUseCase`: (May not be needed if the OAuth client library handles redirect generation directly in the Presentation layer). Takes a handle (optional) and returns the authorization URL to redirect the user to.
43 - `CompleteOAuthSignInUseCase`: Handles the logic after the user returns from the OAuth provider's callback URL.
44 - Takes the callback parameters (code, state) as input.
45 - Uses an `IOAuthProcessor` (infra interface) to validate the callback and obtain the authenticated user's session data (including DID and tokens).
46 - Uses the `IUserRepository` to find or create a `User` aggregate based on the returned DID.
47 - Updates user details (e.g., `lastLoginAt`, potentially fetches/updates `handle`).
48 - Saves the `User` aggregate via `IUserRepository`.
49 - Dispatches `UserAccountLinked` or `UserLoggedIn` events.
50 - Returns a `UserOutputDTO` or session identifier for the presentation layer.
51 - `GetUserUseCase`: Retrieves a `User` by their DID.
52 - `GetCurrentUserUseCase`: Retrieves the `User` associated with the current valid session. (Requires session context).
53
54- **Data Transfer Objects (DTOs):**
55 - `UserOutputDTO`: Represents the `User` aggregate data for external callers (`id`, `handle`, `linkedAt`, `lastLoginAt`).
56 - `OAuthCallbackInputDTO`: Contains parameters from the OAuth callback URL (`code`, `state`).
57 - `CompleteOAuthSignInOutputDTO`: Contains `UserOutputDTO` and potentially status/session info.
58
59- **Repository Interfaces:**
60 - `IUserRepository`: Interface for persisting and retrieving `User` aggregates (`findById(did)`, `save(user)`).
61
62- **Infrastructure Service Interfaces:** (Interfaces defined here, implemented in Infrastructure)
63 - `IOAuthProcessor`: An abstraction over the `@atproto/oauth-client-node` library's core callback processing and session management logic. Methods like `processCallback(params)` returning authenticated user DID and potentially session handle. This interface allows decoupling the use case from the specific library implementation.
64 - `IOAuthSessionStore`: Interface matching the `sessionStore` required by `@atproto/oauth-client-node`. Defines `get(sub)`, `set(sub, session)`, `del(sub)`.
65 - `IOAuthStateStore`: Interface matching the `stateStore` required by `@atproto/oauth-client-node`. Defines `get(key)`, `set(key, state)`, `del(key)`.
66
67### 3. Infrastructure Layer
68
69Contains implementation details for persistence, OAuth handling, and external communication.
70
71- **Persistence:**
72 - `UserRepository`: Implements `IUserRepository` using Drizzle/SQL, mapping the `User` aggregate to a `users` table.
73 - `DrizzleOAuthSessionStore`: Implements `IOAuthSessionStore` using Drizzle/SQL (or Redis, etc.) to store the `Session` data required by the OAuth client library, keyed by user DID (`sub`).
74 - `DrizzleOAuthStateStore`: Implements `IOAuthStateStore` using Drizzle/SQL (or Redis with TTL) to store temporary OAuth state data, keyed by the state parameter.
75- **OAuth Handling:**
76 - Configuration and instantiation of `NodeOAuthClient` from `@atproto/oauth-client-node`.
77 - Provides the concrete implementations of `IOAuthSessionStore` and `IOAuthStateStore` to the `NodeOAuthClient`.
78 - `AtProtoOAuthProcessor`: Implements `IOAuthProcessor`. This class wraps the configured `NodeOAuthClient` instance. Its `processCallback` method would call the underlying `client.callback(params)` method and handle mapping the result/errors.
79- **API / Presentation (e.g., Express.js):**
80 - `/login` endpoint: Uses the configured `NodeOAuthClient` (or a thin wrapper) to generate the authorization URL (`client.authorize(...)`) and redirects the user.
81 - `/atproto-oauth-callback` endpoint:
82 1. Receives the callback request from the browser.
83 2. Extracts parameters (`code`, `state`) from the URL.
84 3. Calls the `CompleteOAuthSignInUseCase` with the parameters.
85 4. The Use Case uses the `IOAuthProcessor` (which uses `client.callback`) to handle the OAuth exchange. The `client.callback` internally uses the `IOAuthSessionStore` and `IOAuthStateStore` implementations.
86 5. The Use Case proceeds to find/create the `User` via `IUserRepository`.
87 6. The endpoint handles the response from the Use Case (e.g., sets an application session cookie, redirects the user to their dashboard).
88
89## Interaction Flow (OAuth Callback Example)
90
911. **User Redirected:** User is redirected from Bluesky back to `/atproto-oauth-callback?code=...&state=...`.
922. **Controller:** The Express route handler for `/atproto-oauth-callback` receives the request.
933. **Controller -> Use Case:** The handler extracts `code` and `state` and calls `CompleteOAuthSignInUseCase.execute({ code, state })`.
944. **Use Case -> OAuth Processor:** The use case calls `IOAuthProcessor.processCallback({ code, state })`.
955. **OAuth Processor -> OAuth Client Lib:** The `AtProtoOAuthProcessor` implementation calls `nodeOAuthClient.callback(params)`.
966. **OAuth Client Lib -> Stores:** The `nodeOAuthClient` interacts with the registered `IOAuthStateStore` (to validate state) and `IOAuthSessionStore` (to save the new tokens/session keyed by the user's DID (`sub`)).
977. **OAuth Processor -> Use Case:** The `IOAuthProcessor` returns the authenticated user's DID (and potentially other session info) to the use case.
988. **Use Case -> Repository:** The use case calls `IUserRepository.findById(did)`.
999. **Repository -> DB:** The `UserRepository` queries the database.
10010. **Use Case:** If user exists, updates `lastLoginAt`. If not, creates a new `User` aggregate instance with the DID, fetches handle (optional), sets `linkedAt`, `lastLoginAt`.
10111. **Use Case -> Repository:** Calls `IUserRepository.save(user)`.
10212. **Repository -> DB:** Inserts or updates the user record in the database.
10313. **Use Case -> Event Dispatcher:** Dispatches `UserAccountLinked` or `UserLoggedIn`.
10414. **Use Case -> Controller:** Returns `UserOutputDTO` (or session identifier).
10515. **Controller -> User:** Sends response (e.g., redirect, set cookie).