BFF APIs¶
Target Architecture — Final-State Design
This page describes the final-state Backend-for-Frontend (BFF) tier of the Factory Studio. The .NET ASP.NET Core BFF pattern, the SignalR substrate (ConnectSoft.Extensions.Saas.AspNetCore.SignalR), and OpenIddict-validated auth are implemented today; the 8 ConnectSoft.Factory.Studio.* services below are the designed end state.
The Factory Studio is served by a tier of 8 microservices in the ConnectSoft.Factory.Studio.* namespace, built on .NET 10 ASP.NET Core and following the Clean Architecture layering of the microservice template. They implement the Backend-for-Frontend (BFF) pattern: each service shapes data specifically for the experience, aggregating multiple source-of-truth platform APIs into a single, tenant-scoped, view-shaped response so the Blazor microfrontends stay thin and chatty round-trips stay off the browser.
The 8 microservices¶
| Service | Namespace | Responsibility | Aggregates / owns |
|---|---|---|---|
| Studio BFF | ConnectSoft.Factory.Studio.StudioBff |
The primary edge BFF and composition gateway. Fronts the shell, routes to the specialized BFFs, and aggregates cross-cutting reads. | Fans out to all platforms and to the other Studio services |
| Studio Notification Service | ConnectSoft.Factory.Studio.StudioNotificationService |
Owns in-app, digest, and escalation notifications scoped to tenant, project, and role. | Owns StudioNotification |
| Studio Realtime Gateway | ConnectSoft.Factory.Studio.StudioRealtimeGateway |
SignalR hub host. Subscribes to factory events and pushes authorized live updates to browsers. | Owns no domain state; owns connection/group state in Redis |
| Review Center Service | ConnectSoft.Factory.Studio.ReviewCenterService |
The human-in-the-loop engine. Manages review queue, decisions, escalation, and override. | Owns ReviewRequest, ReviewDecision |
| Dashboard Aggregation Service | ConnectSoft.Factory.Studio.DashboardAggregationService |
Assembles dashboard views from multiple platform sources; caches hot dashboards. | Owns DashboardView; reads Control Plane + Observability |
| Artifact Preview Service | ConnectSoft.Factory.Studio.ArtifactPreviewService |
Produces render-ready artifact previews, diffs, and lineage over Knowledge artifact memory. | Owns ArtifactPreviewSession; reads Knowledge Platform |
| Knowledge Explorer BFF | ConnectSoft.Factory.Studio.KnowledgeExplorerBff |
Serves bounded, access-checked knowledge graph neighbourhoods to the explorer. | Reads Knowledge Platform graph |
| Agent Flow Visualization Service | ConnectSoft.Factory.Studio.AgentFlowVisualizationService |
Projects Agent Mesh task graphs and executions into render-ready flow view models. | Reads Agent Mesh |
flowchart TB
Shell["Blazor MFE Shell"]
Shell -->|"REST/gRPC"| Bff["StudioBff"]
Shell -->|"SignalR"| Rtg["StudioRealtimeGateway"]
Bff --> Dash["DashboardAggregationService"]
Bff --> Review["ReviewCenterService"]
Bff --> Preview["ArtifactPreviewService"]
Bff --> KExp["KnowledgeExplorerBff"]
Bff --> Flow["AgentFlowVisualizationService"]
Bff --> Notif["StudioNotificationService"]
Dash --> CP["Control Plane API"]
Dash --> OBS["Observability API"]
Review --> CP
Review --> GOV["Governance API"]
Preview --> KP["Knowledge API"]
KExp --> KP
Flow --> AM["Agent Mesh API"]
Bff --> MKT["Marketplace API"]
Rtg -->|subscribe| Bus["Azure Service Bus"]
CP -->|events| Bus
AM -->|events| Bus
OBS -->|signals| Bus
Notif -->|notify events| Bus
BFF aggregation patterns¶
The Studio BFF tier applies a consistent set of patterns so the experience stays fast, governed, and decoupled from source-platform internals:
- View-shaped aggregation. A single BFF endpoint fans out to several platform APIs in parallel and returns one response shaped for a specific module/view, eliminating browser-side orchestration and N+1 calls.
- Tenant scoping at the edge. Every BFF resolves
tenantIdfrom the OpenIddict access token (never from the request body) and stamps it on every downstream call and cache key. See Security. - Read-model caching. Hot, expensive aggregations (dashboards, graph neighbourhoods) are cached in Redis keyed by
tenantId+ view + filter hash, with event-driven invalidation from the realtime stream. - Backend service composition.
StudioBffis the only service the shell calls for cross-cutting reads; it composes the specialized BFFs (dashboard, review, preview, graph, flow) rather than the browser calling each directly. - Resilience and degradation. Downstream calls use timeouts, retries with jitter, and circuit breakers; a slow or failing source degrades gracefully (partial view + staleness indicator) rather than failing the whole page.
- Command pass-through with authority. Writes (review decisions, overrides, install intents) are validated, authorized, audited, and forwarded to the owning platform as
VerbNouncommands; the BFF never mutates source-of-truth state itself. - Idempotency. State-changing endpoints accept an idempotency key so retried submissions (e.g. a double-clicked approval) are safe, consistent with the metadata schema idempotency rules.
Real-time updates via SignalR¶
Live awareness is delivered by the StudioRealtimeGateway, a SignalR hub host built on ConnectSoft.Extensions.Saas.AspNetCore.SignalR. It is the bridge between the factory's event stream and the browser.
flowchart LR
Bus["Azure Service Bus<br/>canonical envelope events"] -->|subscribe| Rtg["StudioRealtimeGateway"]
Rtg -->|"authorize + map"| ViewModel["Realtime View Model"]
Rtg -->|"group: tenant:project:role"| Hub["SignalR Hub"]
Hub -->|"push"| Browser["Connected MFE Clients"]
Rtg -->|"connection + group state"| Redis[("Redis backplane")]
- Event-sourced fan-out. The gateway subscribes to factory events (e.g.
AgentTaskCompleted,DeploymentPromoted,ReviewRequested,RuntimeSignalRaised) on Azure Service Bus and translates each into a compact realtime view-model update. - Authorized groups. Clients join SignalR groups keyed by
tenantId,projectId, and role. The gateway only pushes an event to a group whose members are authorized to see it — tenant isolation and RBAC apply to the stream exactly as they apply to REST. - Redis backplane. Connection and group state lives in Redis so the gateway scales out horizontally across instances while preserving delivery.
- Targeted invalidation, not data dumps. Updates are small "what changed" signals carrying
traceId/correlationId; the client either patches its view model or re-requests the affected BFF read model, keeping payloads tiny. - Trace continuity. Every pushed update carries the originating
traceId, so a live tile remains click-through traceable to its source lifecycle in the Knowledge Graph Explorer and Observability.
Request + push sequence¶
sequenceDiagram
participant MFE as Browser MFE
participant BFF as StudioBff
participant DASH as DashboardAggregationService
participant CP as Control Plane API
participant KP as Knowledge API
participant RTG as StudioRealtimeGateway
participant BUS as Azure Service Bus
MFE->>BFF: GET /studio/dashboards/project-overview (Bearer token)
BFF->>BFF: Resolve tenantId + RBAC from token
BFF->>DASH: Compose project overview (tenantId)
par Parallel aggregation
DASH->>CP: GET /projects/{id}/state
and
DASH->>KP: GET /artifacts/summary?project={id}
end
CP-->>DASH: Project + workflow state
KP-->>DASH: Artifact + lineage summary
DASH-->>BFF: DashboardView read model (cached in Redis)
BFF-->>MFE: 200 view model
MFE->>RTG: Connect SignalR + join tenant:project group
CP->>BUS: AgentTaskCompleted (envelope)
BUS->>RTG: deliver event
RTG->>RTG: Authorize for group + map to update
RTG-->>MFE: push DashboardTileUpdated (traceId)
MFE->>BFF: GET /studio/dashboards/project-overview (refresh changed tile)
BFF-->>MFE: 200 patched view model
Representative BFF endpoints¶
All endpoints are versioned (/v1), require an OpenIddict bearer token, resolve tenantId from the token, and follow the REST resource path conventions.
Studio BFF — composed project overview¶
GET /v1/studio/dashboards/project-overview?projectId=proj-booking-saas
Authorization: Bearer <token>
{
"viewId": "dash-projoverview-9f1c",
"tenantId": "connectsoft",
"projectId": "proj-booking-saas",
"generatedAt": "2026-06-11T18:04:00Z",
"traceId": "trace-9f1c2b7d",
"tiles": [
{ "key": "activeAgents", "value": 7, "source": "agent-mesh", "trend": "up" },
{ "key": "pendingReviews", "value": 3, "source": "studio-review", "severity": "warn" },
{ "key": "openIncidents", "value": 0, "source": "observability" },
{ "key": "monthlyCostUsd", "value": 1840.55, "source": "observability-finops" }
],
"staleSources": [],
"links": { "knowledgeGraph": "/studio/knowledge-graph?focus=proj-booking-saas" }
}
Dashboard Aggregation Service — saved dashboard view¶
{
"dashboardViewId": "dash-booking-exec",
"tenantId": "connectsoft",
"ownerUserId": "user-amelia",
"layout": "grid-3col",
"widgets": [
{ "widgetId": "w-runs", "type": "workflow-throughput", "source": "control-plane", "range": "7d" },
{ "widgetId": "w-quality", "type": "quality-gates", "source": "observability" }
],
"refreshPolicy": { "mode": "event-driven", "fallbackIntervalSeconds": 30 }
}
Review Center Service — review queue and decision¶
{
"tenantId": "connectsoft",
"items": [
{
"reviewRequestId": "rev-3a6e1d40",
"subjectType": "Artifact",
"subjectId": "art-domainmodel-7c",
"projectId": "proj-booking-saas",
"raisedBy": "agent:ConnectSoft.Agent.SolutionArchitect",
"priority": "high",
"slaDueAt": "2026-06-11T20:00:00Z",
"status": "Pending",
"traceId": "trace-9f1c2b7d"
}
],
"page": { "size": 25, "next": null }
}
POST /v1/studio/reviews/rev-3a6e1d40/decisions
Idempotency-Key: dec-amelia-rev-3a6e1d40
Content-Type: application/json
{
"decision": "Approved",
"reviewerUserId": "user-amelia",
"rationale": "Domain model matches blueprint; aggregates and invariants correct.",
"conditions": []
}
{
"reviewDecisionId": "decn-7b21",
"reviewRequestId": "rev-3a6e1d40",
"decision": "Approved",
"decidedAt": "2026-06-11T18:42:00Z",
"emittedEvent": "ReviewApproved",
"traceId": "trace-9f1c2b7d"
}
Artifact Preview Service — open a preview session¶
{
"sessionId": "prev-5e10",
"artifactId": "art-domainmodel-7c",
"version": "1.4.0",
"renditions": [
{ "renditionId": "rd-diff-1", "renderKind": "diff", "contentType": "application/x-cs-diff", "redacted": false }
],
"lineage": [
{ "from": "art-domainmodel-7c", "to": "task-4d21", "relationship": "producedBy" }
],
"expiresAt": "2026-06-11T19:00:00Z"
}
Knowledge Explorer BFF — bounded graph neighbourhood¶
{
"viewId": "kg-proj-booking-2",
"focusNodeId": "proj-booking-saas",
"depth": 2,
"nodes": [
{ "nodeId": "proj-booking-saas", "nodeType": "Project", "label": "Booking SaaS", "truncated": false },
{ "nodeId": "module-reservations-api", "nodeType": "Module", "label": "Reservations API", "truncated": false },
{ "nodeId": "art-domainmodel-7c", "nodeType": "Artifact", "label": "Domain Model", "classification": "internal" }
],
"edges": [
{ "edgeId": "e1", "sourceNodeId": "proj-booking-saas", "targetNodeId": "module-reservations-api", "relationship": "contains" },
{ "edgeId": "e2", "sourceNodeId": "module-reservations-api", "targetNodeId": "art-domainmodel-7c", "relationship": "produces" }
]
}
Agent Flow Visualization Service — flow graph¶
{
"workflowId": "wf-booking-build",
"tenantId": "connectsoft",
"status": "Running",
"nodes": [
{ "taskId": "task-4d21", "agent": "ConnectSoft.Agent.SolutionArchitect", "state": "Completed" },
{ "taskId": "task-4d22", "agent": "ConnectSoft.Agent.BackendEngineer", "state": "Running" }
],
"edges": [
{ "from": "task-4d21", "to": "task-4d22", "relationship": "dependsOn" }
],
"traceId": "trace-9f1c2b7d"
}
Studio Notification Service — notifications¶
{
"tenantId": "connectsoft",
"items": [
{
"notificationId": "ntf-88a1",
"kind": "ReviewEscalated",
"subjectId": "rev-3a6e1d40",
"severity": "high",
"createdAt": "2026-06-11T18:55:00Z",
"read": false
}
]
}
Realtime gateway — SignalR hub contract¶
The gateway exposes a SignalR hub rather than REST. Clients invoke and receive:
| Direction | Member | Purpose |
|---|---|---|
| Client → Hub | JoinProject(projectId) |
Join the tenant-scoped project group (server re-checks authorization). |
| Client → Hub | LeaveProject(projectId) |
Leave a project group. |
| Hub → Client | DashboardTileUpdated(update) |
A dashboard tile changed; carries traceId. |
| Hub → Client | ReviewQueueChanged(update) |
A review item was added, escalated, or decided. |
| Hub → Client | AgentFlowAdvanced(update) |
An agent task state changed in a watched flow. |
| Hub → Client | RuntimeSignalRaised(update) |
A runtime/health signal for a watched environment. |
| Hub → Client | NotificationReceived(notification) |
A new in-app notification for the user. |