A social knowledge tool for researchers built on ATProto
Card Subdomain (DDD)#
This document outlines the domain model for the Card feature, following Domain-Driven Design principles.
Bounded Context#
The Card context represents a system where users can save various types of content (URLs, notes, highlights, screenshots, files) to their personal libraries and collections.
Domain Model#
Aggregates#
-
Card Aggregate
- Represents a saveable item (URL, note, highlight, screenshot, file)
- Responsible for maintaining its own integrity
- Contains references to related cards (highlights, notes, screenshots)
- Does NOT directly manage which collections it belongs to
-
Collection Aggregate
- Represents a user-defined grouping of cards
- Manages which cards belong to it
- Responsible for maintaining collection membership
Entities#
-
Card (Aggregate Root)
- Has identity through CardId
- Properties: type, content, metadata, creation date
- May reference other cards (for highlights, notes, screenshots)
-
Collection (Aggregate Root)
- Has identity through CollectionId
- Properties: name, description, creation date
- Contains references to member cards
Value Objects#
-
CardId
- Unique identifier for a card
-
CardType
- Enumeration of possible card types (URL, Note, Highlight)
-
CardContent
- Value object representing the content of a card with type-specific validation
- URL cards contain URL and optional UrlMetadata
- Note cards contain text and optional title
- Highlight cards contain highlighted text with robust anchoring using multiple selectors (TextQuote, TextPosition, Range) following Hypothes.is approach for reliable re-anchoring
-
CollectionId
- Unique identifier for a collection
-
CuratorId
- References the user who owns/created the card or collection
-
URL
- Value object representing a valid URL
-
UrlMetadata
- Value object containing metadata about a URL (title, description, author, etc.)
Domain Services#
- LibraryService
- Coordinates operations that span multiple aggregates
- Handles adding cards to a user's library and collections
- Provides access to a user's entire content (their "library")
Infrastructure Services#
-
IMetadataService
- Interface for external metadata retrieval services
- Abstracts the details of specific metadata providers (Citoid, etc.)
-
IUrlMetadataRepository
- Repository interface for storing and retrieving URL metadata
- Provides caching layer for metadata to avoid repeated API calls
Relationships and Boundaries#
-
Card-Collection Relationship: This is a many-to-many relationship. A card can be in multiple collections, and a collection can contain multiple cards.
-
Aggregate Boundaries:
- The Card aggregate is responsible for its own data and direct relationships to other cards.
- The Collection aggregate manages which cards are members of the collection.
- When adding a card to a collection, the operation is handled by the Collection aggregate.
-
Card-to-Card Relationships:
- URL cards are standalone and do not reference other cards
- Note cards can be standalone or reference other cards via parentCardId
- Highlight cards must reference a parent card (the content being highlighted) via parentCardId
- These relationships are maintained within the Card aggregate with validation rules
Use Cases#
-
AddCardToLibrary
- Adds a new card to a user's library
- May also add the card to one or more collections
- May create related cards (notes, highlights, screenshots)
-
AddCardToCollection
- Adds an existing card to a collection
-
AnnotateCard
- Creates annotation cards (notes, highlights) linked to an existing card
-
GetUrlMetadataUseCase
- Retrieves metadata for a given URL
- First checks if metadata already exists in the repository
- If not found, fetches from external metadata service (e.g., Citoid API)
- Stores retrieved metadata for future use
Implementation Considerations#
- When a card is added to multiple collections, this should be handled as separate operations on each Collection aggregate.
- The LibraryService domain service coordinates operations that span multiple aggregates.
- Card-to-card relationships (like highlights of a URL) are maintained within the Card aggregate.
- "Library" is a conceptual grouping of a user's content rather than an aggregate with its own identity and lifecycle.
- A user's library is accessed through repository methods (e.g.,
findByCuratorId()) and coordinated through the LibraryService.
External API Integration Patterns#
- Repository Pattern: Use
IUrlMetadataRepositoryto abstract metadata storage and provide caching - Service Interface: Use
IMetadataServiceto abstract external API calls (Citoid, etc.) - Dependency Inversion: Domain layer depends on interfaces, infrastructure layer implements them
- Caching Strategy: Check repository first before making external API calls to reduce latency and API usage
- Error Handling: External API failures should be handled gracefully with fallback strategies