Blocks

Info

Architectural truth for Blocks in the Documents feature. This leaf module contains the domain logic, endpoints, and messaging orchestration for manipulating individual text and data blocks within a document’s sections.

Overview

In MILTON, documents are strictly structured into Sections, which in turn contain ContentBlock elements. Blocks are the fundamental unit of content and generation in the system. A block is polymorphic; its Type determines its semantic intent (e.g., Requirement, TestCase, Mermaid, Scanner), and its schema-less data is stored in a JSONB field (PropertiesJson).

The Blocks feature slice provides HTTP endpoints (via FastEndpoints) to manually add, retrieve, update, and reorder blocks. Crucially, it also provides the ProcessBlockEndpoint, which manually triggers the asynchronous generation pipeline for a specific block.

Business Logic Intent

The core intent of this module is to decouple the structural storage of content from the generation pipeline. Clients interact with blocks synchronously for simple CRUD operations, but when a block requires LLM or scanner processing (e.g., “Generate test cases for this requirement”), the API transitions into an asynchronous CQRS flow. This ensures the API remains fast and responsive while heavy computation happens in background workers.

Endpoints & Flow

  • AddBlockEndpoint / UpdateBlockEndpoint: Standard synchronous CRUD operations saving directly to milton-db via Entity Framework Core.
  • GetBlockEndpoint: Retrieves a block and its children. Uses AsNoTracking and includes child elements.
  • ReorderBlocksEndpoint: Receives an ordered list of block IDs and updates their SortOrder within a section.
  • ProcessBlockEndpoint: The bridge between synchronous API requests and the asynchronous generation pipeline.

Sequence: Triggering Block Processing

When a user clicks “Process” on a specific block, the API synchronously enqueues a message to the worker. The user is immediately returned a 200 OK, and the UI relies on SignalR notifications for the final result.

sequenceDiagram
    participant Client
    participant FastEndpoint as ProcessBlockEndpoint
    participant DB as AppDbContext
    participant Bus as Wolverine IMessageBus
    participant Worker as DocumentGenerator Worker

    Client->>FastEndpoint: POST /documents/blocks/process { BlockId }
    FastEndpoint->>DB: Query Block by Id
    DB-->>FastEndpoint: Return Block (Type, Status)
    
    alt is processable type (e.g. Scanner, Requirement, TestCase)
        FastEndpoint->>Bus: Publish Prepare[Type]GenerationCommand
        FastEndpoint-->>Client: 200 OK (Queued for processing)
        
        Bus->>Worker: (Async Pipeline starts)
    else is not processable
        FastEndpoint-->>Client: 200 OK (Success=false, not processable)
    end

Domain Models

  • ContentBlock: The EF Core entity representing a block. Key fields: SectionId, Type, Body, PropertiesJson, Status, SortOrder.
  • Request / Response DTOs:
    • AddBlockRequest, ContentBlockDto
    • UpdateBlockRequest, ProcessBlockRequest
    • ProcessBlockResponse (indicates whether the block was successfully queued)

Key Constraints & Patterns

  • Message-Driven: Block processing kicks off via Prepare*GenerationCommand. The saga ID is set to Guid.Empty because this is a manual, single-block process rather than a full document instantiation saga.
  • No Shared State: The API owns the DB. Background processing does not directly update the block; it replies via Wolverine, and the API’s Apply* handlers execute the database update.

0 items under this folder.