Blocks
Info
Architectural truth for
Blocksin theDocumentsfeature. 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 tomilton-dbvia Entity Framework Core.GetBlockEndpoint: Retrieves a block and its children. UsesAsNoTrackingand includes child elements.ReorderBlocksEndpoint: Receives an ordered list of block IDs and updates theirSortOrderwithin 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,ContentBlockDtoUpdateBlockRequest,ProcessBlockRequestProcessBlockResponse(indicates whether the block was successfully queued)
Key Constraints & Patterns
- Message-Driven: Block processing kicks off via
Prepare*GenerationCommand. The saga ID is set toGuid.Emptybecause 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.