A social knowledge tool for researchers built on ATProto

Domain Model Outline (DDD)#

This document outlines a potential domain model based on the lexicon definitions, following Domain-Driven Design (DDD) principles and a layered architecture.

Bounded Contexts#

We identify three primary bounded contexts:

  1. Annotations Context: Manages the creation, definition, and querying of annotations, annotation fields, and templates.
  2. User Management Context: Responsible for representing users within this application, linking them to their external Bluesky identity via OAuth, and managing their session state. Users from this context act as actors within the Annotations context.
  3. ATProto Context: Deals with the underlying AT Protocol concepts like repositories, records, and strong references. This context primarily provides foundational types and potentially infrastructure adapters (like OAuth clients) used by other contexts.

Layered Architecture#

1. Domain Layer#

Contains the core business logic, entities, value objects, and aggregates. It is independent of application and infrastructure concerns.

Annotations Context#

  • Aggregates & Entities:

    • Annotation (Aggregate Root):
      • Entity representing a single annotation instance.
      • Contains AnnotationValue (Value Object) representing the specific data based on the field type.
      • References AnnotationField via StrongRef (Value Object from ATProto context).
      • May reference AnnotationTemplate(s) via StrongRef (Value Object from ATProto context).
      • Includes url (Value Object - potentially a specific URI type) and optional additionalIdentifiers (List of Identifier Value Objects).
      • Includes optional note (string).
      • Root ensures the consistency between the AnnotationField reference and the AnnotationValue type.
    • AnnotationField (Aggregate Root):
      • Entity defining the structure and type of an annotation.
      • Contains FieldDefinition (Value Object) which holds the specific configuration (e.g., DyadFieldDef, RatingFieldDef).
      • Properties: name, description, createdAt.
      • Root ensures the validity of the FieldDefinition.
    • AnnotationTemplate (Aggregate Root):
      • Entity grouping multiple AnnotationFields.
      • Properties: name, description, createdAt.
      • Contains a list of TemplateField (Value Object), each holding a StrongRef to an AnnotationField and a required flag.
      • Root ensures the integrity of the template definition.
  • Value Objects:

    • AnnotationValue: Represents the actual value of an annotation. Could be a union/interface implemented by:
      • DyadValue (value)
      • TriadValue (vertexA, vertexB, vertexC, sum)
      • RatingValue (rating)
      • SingleSelectValue (option)
      • MultiSelectValue (option array)
    • FieldDefinition: Represents the definition of an AnnotationField. Could be a union/interface implemented by:
      • DyadFieldDef (sideA, sideB)
      • TriadFieldDef (vertexA, vertexB, vertexC)
      • RatingFieldDef (numberOfStars)
      • SingleSelectFieldDef (options array)
      • MultiSelectFieldDef (options array)
    • TemplateField: Represents an entry in AnnotationTemplate.annotationFields. Contains fieldRef (StrongRef) and required (boolean).
    • Identifier: (type, value) - Represents app.annos.defs#identifier.
    • URI: Represents a validated URI string.

ATProto Context (as relevant to Annotations)#

  • Value Objects:
    • StrongRef: (cid, uri) - Represents com.atproto.repo.strongRef. Used for linking between aggregates/records.
    • TID: Represents a unique record identifier within the ATProto context. (Used as key in lexicons).

User Management Context (See docs/user-domain.md for details)#

  • Aggregates & Entities: User
  • Value Objects: DID, Handle
  • Domain Events: UserAccountLinked, UserLoggedIn

2. Application Layer#

Contains application-specific logic, orchestrates use cases, and coordinates domain objects. It depends on the Domain Layer but not the Infrastructure Layer (uses interfaces defined in the domain/application layer).

  • Use Cases / Application Services:

    • CreateAnnotationUseCase: Handles creating a new Annotation record. Validates input, fetches related AnnotationField, creates the Annotation aggregate, and uses a repository to persist it.
    • GetAnnotationUseCase: Retrieves an Annotation by its identifier.
    • CreateAnnotationFieldUseCase: Handles creating a new AnnotationField. Validates definition, creates the aggregate, persists.
    • GetAnnotationFieldUseCase: Retrieves an AnnotationField.
    • CreateAnnotationTemplateUseCase: Handles creating a new AnnotationTemplate. Validates input, resolves AnnotationField references, creates the aggregate, persists.
    • GetAnnotationTemplateUseCase: Retrieves an AnnotationTemplate.
    • FillAnnotationTemplateUseCase: Handles creating a set of Annotation records based on a provided AnnotationTemplate and input values for a specific target URL. Fetches the template and fields, validates input, creates multiple Annotation aggregates (populating fieldRef and templateRefs appropriately), and persists them.
    • AddAnnotationFieldToTemplateUseCase: Adds a field reference to an existing template.
    • ListAnnotationsForResourceUseCase: Finds annotations associated with a specific URL or identifier.
    • (Other CRUD operations and specific query use cases)
  • Data Transfer Objects (DTOs): Used for input and output of Application Services to decouple from the internal domain model and external interfaces (e.g., API requests/responses).

    • AnnotationInputDTO, AnnotationOutputDTO
    • AnnotationFieldInputDTO, AnnotationFieldOutputDTO
    • AnnotationTemplateInputDTO, AnnotationTemplateOutputDTO
    • FillAnnotationTemplateInputDTO: Input for FillAnnotationTemplateUseCase, containing template URI, target URL, and a map of field URIs to their values.
    • FillAnnotationTemplateOutputDTO: Output for FillAnnotationTemplateUseCase, containing a list of the created AnnotationOutputDTOs.
  • Repository Interfaces: (Defined here or in Domain, implemented in Infrastructure)

    • IAnnotationRepository
    • IAnnotationFieldRepository
    • IAnnotationTemplateRepository
    • IUserRepository
  • Infrastructure Service Interfaces: (Defined here, implemented in Infrastructure)

    • IOAuthProcessor
    • IOAuthSessionStore
    • IOAuthStateStore

3. Infrastructure Layer#

Contains implementation details for data persistence, external service integrations, UI frameworks, etc. It depends on the Application and Domain Layers.

  • Persistence (Example: Drizzle ORM):
    • Implementation of Repository Interfaces (AnnotationRepository, AnnotationFieldRepository, AnnotationTemplateRepository) using Drizzle ORM.
    • Defines Drizzle schemas mapping domain entities/aggregates to database tables.
    • Handles database connections and transactions.
    • Serializes/deserializes Value Objects (like AnnotationValue, FieldDefinition, StrongRef) for storage.
  • External Services:
    • Clients or adapters for interacting with other ATProto services if needed (e.g., resolving StrongRef details if not handled purely by reference).
  • API / Presentation:
    • (e.g., Express.js, Fastify) Handles HTTP requests, uses Application Services (Use Cases) to perform actions, and formats responses (often using DTOs). Maps API routes to use case handlers.

Interactions#

  • The Application Layer orchestrates interactions. For example, CreateAnnotationUseCase might fetch an AnnotationField using IAnnotationFieldRepository to validate the incoming AnnotationValue before creating the Annotation aggregate and saving it via IAnnotationRepository.
  • StrongRef Value Objects are used within the Annotations context domain models but their resolution or validation might involve calls (potentially abstracted via interfaces) to ATProto-specific logic or repositories.