This repository has no description
0

Configure Feed

Select the types of activity you want to include in your feed.

semble / docs / user-domain.md
8.5 kB 105 lines
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).