Core
Info
Architectural truth for
Corein theProjectsfeature. This leaf module contains the foundational endpoints for managing theProjectaggregate root.
Overview
In MILTON, the Project is the highest-level entity in the tenant space. Every Document, Section, ContentBlock, and Template is ultimately scoped to a Project. This feature slice handles the lifecycle and querying of projects.
Business Logic Intent
The logic in Projects/Core is straightforward and synchronous, primarily acting as a CRUD boundary over milton-db. However, it establishes the CQRS pattern used throughout the API: even synchronous HTTP endpoints delegate to Wolverine handlers via IMessageBus.InvokeAsync<T>.
When a Project is created, the system must also provision a default ProjectConfig entity. The ProjectConfig holds project-level settings such as Git repository links and LLM prompt overrides (managed in Features/ProjectConfig).
Endpoints & Flow
CreateProject: HTTP POST endpoint. DispatchesCreateProjectCommandto Wolverine.GetProjectsEndpoint: Fetches projects assigned to a specific user (via theUserIdroute param).ListProjectsEndpoint: Fetches all projects (tenant-scoped) with aggregated summary metadata (e.g., Repository count).
Sequence: Project Creation
sequenceDiagram participant Client participant Endpoint as CreateProject Endpoint participant Bus as Wolverine IMessageBus participant Handler as CreateProjectHandler participant DB as milton-db Client->>Endpoint: POST /projects { Title, Description } Endpoint->>Bus: InvokeAsync(CreateProjectCommand) Bus->>Handler: Handle(CreateProjectCommand) Handler->>DB: Check for duplicate Title alt Duplicate Exists Handler-->>Bus: return response (ProjectId = 0) Bus-->>Endpoint: CreateProjectResponse Endpoint-->>Client: 409 Conflict else Is Unique Handler->>Handler: Create Project Entity Handler->>Handler: Create default ProjectConfig Handler->>DB: Add & SaveChangesAsync() Handler-->>Bus: return response (ProjectId > 0) Bus-->>Endpoint: CreateProjectResponse Endpoint-->>Client: 200 OK end
Wolverine Contracts & Handlers
- Commands/Queries:
CreateProjectCommand,GetProjectsCommand,ListAllProjectsQuery - Handlers:
CreateProjectHandler,GetProjectsHandler,ListAllProjectsHandler
Domain Models
Project: The EF Core entity. IncludesTitle,Description, and a one-to-one relationship withProjectConfig.- Request / Response:
CreateProjectRequest,GetProjectsRequest,GetProjectResponse,CreateProjectResponse
Caching (HybridCache)
ListAllProjectsHandler (the home-page project list) caches the List<GetProjectResponse> under projects:{tenantId}:{userId} (5-minute TTL) — the user id is part of the key, so the per-user membership filter is preserved exactly and no user ever reads another user’s list. Each entry is tagged with the key plus one project-meta:{tenantId}:{projectId} tag per visible project, so a config change for one project refreshes every user’s list. GetProjectsHandler (the /projects/user/{userId} variant) is deliberately not cached (lower priority; the frontend uses ListProjectsEndpoint).
Invalidation
UpdateProjectConfigHandlerremovesproject-meta:{tenantId}:{projectId}after save (alongside the existingconfig/repotags).CreateProjectHandlerremovesprojects:{tenantId}:{userId}for the creating user after save (a new project appears in their list).