StudioBff¶
Target
This page describes intended behaviour or structure. No implementation is claimed unless a section is explicitly marked Current implementation or Evidence.
| Bounded context | BC-29 Experience Composition |
| Platform / plane | Factory Studio / Experience |
| Deployment granularity | standalone host |
| Status | target · ConnectSoft.Factory.Studio |
| Built from | connectsoft-apigateway · libraries: connectsoft-extensions-saas-abstractions, connectsoft-extensions-saas-asp-net-core, connectsoft-extensions-messaging-model, connectsoft-extensions-persistence-model-nhibernate, connectsoft-extensions-service-model-signal-r, connectsoft-extensions-observability, connectsoft-extensions-query-model |
| Decisions | ADR-0014, ADR-0015, ADR-0016 |
| Traceability | FR-01 FR-05 FR-10 FUX-01 FUX-02 FUX-03 FUX-04 FUX-06 FUX-07 FUX-08 FUX-09 FUX-11 FUX-12 NFR-01 NFR-02 NFR-03 NFR-10 XL-01 XL-05 XL-06 XL-12 |
Purpose and responsibilities¶
StudioBff is gateway role GW-02: the one service the Studio's Blazor shell, microfrontends, CLI and VS Code extension talk to. For every screen in the UX model it builds a composition — runs from Run Control and the operations projections, approvals, assets and releases, evaluation verdicts, alerts, fleet, delivery, provisioning, knowledge — each source read under the session's execution scope and each marked with its projection freshness, so a stale or degraded source is visible rather than hidden. For every action it forwards a command to the owning platform with an idempotency key, the session's scope and, for gated classes, the approval binding; the class and gates are the owning service's, never the BFF's. It keeps the session projection (scope switchers that grant nothing, permissions snapshot, locale, theme, reduced motion, work area), saved views and preferences, and hosts StudioNotificationService.
It absorbs the June-2026 AgentFlowVisualizationService, DashboardAggregationService, KnowledgeExplorerBff and KnowledgeStudioBff: they are compositions over other platforms' projections, not services with state.
Context and boundaries¶
flowchart LR
S["StudioBff"]
StudioRealtimeGateway["StudioRealtimeGateway"] -->|grpc| S
S -->|sync-http| ext_identity_platform["ext-identity-platform"]
S -->|grpc| ControlPlaneApi["ControlPlaneApi"]
S -->|grpc| ObservabilityHost["ObservabilityHost"]
S -->|grpc| KnowledgeHost["KnowledgeHost"]
S -->|grpc| GovernanceHost["GovernanceHost"]
S -->|grpc| ModelGatewayHost["ModelGatewayHost"]
S -->|grpc| ToolExecutorHost["ToolExecutorHost"]
S -->|grpc| IntegrationHost["IntegrationHost"]
S -->|grpc| DeliveryHost["DeliveryHost"]
S -->|grpc| RuntimeCloudHost["RuntimeCloudHost"]
S -->|grpc| FleetOperationsHost["FleetOperationsHost"]
S -->|grpc| DataEventFoundations["DataEventFoundations"]
S -->|sync-http| ext_config_platform["ext-config-platform"]
S -. "StudioSessionStarted" .-> E_StudioSessionStarted(("event"))
S -. "SavedViewChanged" .-> E_SavedViewChanged(("event"))
The BFF is built on the ConnectSoft API gateway template (GW-01 fronts it) with the Blazor shell, microfrontend library and UIKit as its clients. The seven work areas and the screen inventory come from the UX model (model/ux/, design plan U1–U2); this service is the contract they compose against.
Domain model¶
StudioSession — The per-user session projection: the resolved execution scope (organization, tenant, project, environment switchers that grant nothing by themselves), roles and effective permissions snapshot, locale, theme, reduced-motion preference, current work area and the realtime subscriptions the session holds. (catalog entry)
| Field | Type | Required | Description |
|---|---|---|---|
sessionId |
guid |
yes | |
tenantId |
string |
yes | |
userId |
string |
yes | |
scope |
json |
yes | execution-scope-v1: tenant, project, environment |
permissionsSnapshot |
json |
yes | |
locale |
enum:Locale |
yes | |
theme |
string |
yes | light |
reducedMotion |
bool |
yes | |
workArea |
enum:WorkArea |
yes | |
subscriptions |
list:string |
yes | |
startedAt |
datetime |
yes | |
lastSeenAt |
datetime |
yes |
Invariants:
- A switcher changes the scope the session reads under; it never grants a permission the identity platform and the Control plane did not grant (ADR-0015).
- Every composed response is filtered by the owning platform under the session's scope; the BFF never widens a result set.
Events: StudioSessionStarted · Tables: StudioSessions · Repository: StudioSessionRepository
SavedView — A user- or team-saved filter and layout for a list screen (runs, approvals, assets, alerts, fleet) with sharing scope. (catalog entry)
| Field | Type | Required | Description |
|---|---|---|---|
viewId |
guid |
yes | |
tenantId |
string |
yes | |
ownerUserId |
string |
yes | |
screenId |
string |
yes | |
name |
string |
yes | |
definition |
json |
yes | |
sharedWith |
string |
yes | private |
updatedAt |
datetime |
yes |
Invariants:
- A shared view carries no data, only a filter definition; results are still scoped per viewer.
Events: SavedViewChanged · Tables: SavedViews · Repository: SavedViewRepository
erDiagram
StudioSession {
guid sessionId PK
string tenantId
string userId
json scope
json permissionsSnapshot
enum_Locale locale
string theme
bool reducedMotion
enum_WorkArea workArea
list_string subscriptions
datetime startedAt
datetime lastSeenAt
}
SavedView {
guid viewId PK
string tenantId
string ownerUserId
string screenId
string name
json definition
string sharedWith
datetime updatedAt
}
Entities and value objects¶
None modelled
No entities or value objects are modelled for this item yet.
Data model and storage¶
| Store | Role | Engine / profile | Tables |
|---|---|---|---|
| Studio store | primary | azure-sql / nhibernate-sqlserver | StudioSessions, SavedViews |
| Cache and coordination | cache | redis / azure-cache | — |
| Table | Owner | Columns | Indexes |
|---|---|---|---|
dbo.StudioSessions |
StudioSession | 12 | 1 |
dbo.SavedViews |
SavedView | 8 | 1 |
Full DDL: appendix.
APIs¶
| Method | Path | Operation | Scope | Side effects | Idempotency | Gateway | Summary |
|---|---|---|---|---|---|---|---|
GET |
/studio/session |
GetStudioSession | factory.studio.use |
read | natural | GW-02 | The session projection: scope, permissions snapshot, locale, theme, work area, feature flags from the Config Platform. |
PUT |
/studio/session/scope |
SetStudioScope | factory.studio.use |
shared-write | natural | GW-02 | Switch tenant, project or environment (validated against the identity platform and Control; grants nothing). |
PUT |
/studio/session/preferences |
SetStudioPreferences | factory.studio.use |
shared-write | natural | GW-02 | Locale (en, he, ru), theme, reduced motion, default work area. |
GET |
/studio/screens/{screenId} |
GetScreenModel | factory.studio.use |
read | natural | GW-02 | A screen-shaped composition for one of the UX model's screens (runs, approvals, assets, evaluation, operations, fleet, …) built from the owning platforms' read models under the session's scope, with projection freshness per source. |
POST |
/studio/commands/{commandId} |
ForwardCommand | factory.studio.use |
production | key-header | GW-02 | Forward a Studio command (submit run, decide approval, acknowledge alert, request promotion, …) to the owning platform with an idempotency key, the session's scope and, for gated classes, the approval binding; the class and the owning service's gates apply, never the BFF's. |
GET |
/studio/views |
ListSavedViews | factory.studio.use |
read | natural | GW-02 | Saved views visible to the user for a screen. |
PUT |
/studio/views/{viewId} |
SaveView | factory.studio.use |
shared-write | natural | GW-02 | Create or update a saved view. |
GET |
/studio/search |
StudioSearch | factory.studio.use |
read | natural | GW-02 | Command-palette search across runs, assets, projects, environments, alerts and screens under the session's scope. |
GET |
/studio/health |
StudioHealth | factory.health.read |
read | natural | GW-01 | Liveness and readiness with per-source dependency health. |
None modelled
No gRPC methods are modelled for this item yet.
Messages¶
| Event | Topic | Scope / v | Aggregate | Consumers | Summary |
|---|---|---|---|---|---|
| StudioSessionStarted | studio.sessions.v1.studio-session-started |
integration / 1 | StudioSession | AuditService, DashboardService | A Studio session started (audit of scope and locale). |
| SavedViewChanged | studio.views.v1.saved-view-changed |
integration / 1 | SavedView | StudioRealtimeGateway | A saved view was created, changed or deleted. |
StudioSessionStarted payload:
| Field | Type | Required | Description |
|---|---|---|---|
sessionId |
guid |
yes | |
tenantId |
string |
yes | |
userId |
string |
yes | |
locale |
enum:Locale |
yes |
SavedViewChanged payload:
| Field | Type | Required | Description |
|---|---|---|---|
viewId |
guid |
yes | |
tenantId |
string |
yes | |
screenId |
string |
yes | |
sharedWith |
string |
yes |
| Command | Handled by | Result events | Summary |
|---|---|---|---|
SetStudioScope |
StudioSession | — | |
SaveView |
SavedView | SavedViewChanged |
None modelled
No consumed events are modelled for this item yet.
Workers and jobs¶
| Worker | Trigger | Output events | Retry | Idempotency key | Summary |
|---|---|---|---|---|---|
| SessionSweeper | schedule */15 * * * * |
— | none | sessionId |
Expires idle sessions and releases their realtime subscriptions. |
Actors and grains¶
None modelled
No grains are modelled for this item yet.
Sagas and long-running flows¶
None modelled
No sagas are modelled for this item yet.
sequenceDiagram
participant UI as Studio shell / MFE
participant B as StudioBff
participant RC as RunControlService
participant D as DashboardService
participant Q as QualityScoreService
UI->>B: GET /studio/screens/run-detail?runId
par compose under the session's scope
B->>RC: run, steps, approvals (scope)
B->>D: timeline projection (scope) + freshness
B->>Q: evaluation verdicts for the agent release
end
B-->>UI: composition with per-source freshness and permissions per action
UI->>B: POST /studio/commands/decide-approval (idempotency key)
B->>RC: DecideApproval (user token, scope, approval id, version)
RC-->>B: 200 / 403 / 409 / 412
B-->>UI: outcome, never retried by the BFF
Options and configuration¶
| Section | Key | Type | Default | Reloadable | Secret | Summary |
|---|---|---|---|---|---|---|
Studio:Bff |
ScreenCacheSeconds |
int | 5 |
yes | no | Short cache per screen composition keyed by scope. |
Studio:Bff |
SupportedLocales |
string[] | [en, he, ru] |
no | no | Locales offered; he is right-to-left. |
Studio:Bff |
SessionIdleMinutes |
int | 120 |
yes | no | Idle timeout. |
Metrics and observability¶
| Metric | Instrument | Unit | Dimensions | Summary |
|---|---|---|---|---|
connectsoft.factory.studio.screen_latency |
histogram | ms | screenId | Screen composition latency (NFR interactive 2 s p95). |
connectsoft.factory.studio.commands_forwarded |
counter | {command} | commandId, outcome | Commands forwarded by outcome. |
connectsoft.factory.studio.sessions_active |
updown | {session} | tenant, locale | Active sessions. |
connectsoft.factory.studio.source_degraded |
updown | {source} | source | Sources currently degraded in compositions. |
Security¶
| Authentication | oauth2-jwt |
| Scopes | factory.studio.use (Use the Studio under the user's own grants (every forwarded call carries the user's token and scope).); factory.health.read (Health probes.) |
| Roles | Requester, Reviewer, AssetMaintainer, Operator, WorkspaceAdmin, PlatformAdmin, Observer |
| Tenant isolation | per-request-claim |
| Data classification | internal |
| Max side-effect class | production |
| Secrets | studio-sql (key-vault, rotate 90 d); studio-signing (key-vault, rotate 90 d) |
Dependencies¶
Outbound
| Target | Kind | Purpose | Failure mode | Required |
|---|---|---|---|---|
| ext-identity-platform | sync-http | User authentication (OpenIddict) and token exchange for downstream calls. | fail-fast | yes |
| ControlPlaneApi | grpc | Runs, approvals, assets, projects, environments, usage, commercial; command forwarding. | degrade | yes |
| ObservabilityHost | grpc | Operations, SLO, alert, evaluation and feedback views. | degrade | yes |
| KnowledgeHost | grpc | Knowledge, artifacts and memory views. | degrade | yes |
| GovernanceHost | grpc | Policy, audit and secret-reference views. | degrade | yes |
| ModelGatewayHost | grpc | Model profile and deployment views. | degrade | yes |
| ToolExecutorHost | grpc | Tool and MCP registry views. | degrade | yes |
| IntegrationHost | grpc | Connection and connector views. | degrade | yes |
| DeliveryHost | grpc | Delivery views. | degrade | yes |
| RuntimeCloudHost | grpc | Provisioning, inventory and capacity views. | degrade | yes |
| FleetOperationsHost | grpc | Fleet views. | degrade | yes |
| DataEventFoundations | grpc | Registry, store, retention views. | degrade | yes |
| ext-config-platform | sync-http | Feature flags and Studio configuration. | degrade | no |
Inbound
| Target | Kind | Purpose | Failure mode | Required |
|---|---|---|---|---|
| StudioRealtimeGateway | grpc | Session and subscription resolution. | — | yes |
Failure and recovery¶
| Scenario | Detection | Recovery | Data impact | RTO (min) |
|---|---|---|---|---|
| One source platform down | dependency health | the composition renders the other sources and marks the missing one degraded with its last freshness; commands to that platform are refused with 503, never queued by the BFF | none | 0 |
| Identity platform down | token validation failures | existing sessions continue until token expiry; new sign-ins fail; status page shows it | none | 30 |
NFR targets¶
| Target | Class value (pilot-interactive) | Override |
|---|---|---|
| latency_p95_ms | 2000 | — |
| availability_pct | 99.5 | — |
| throughput_rps | 50 | — |
| rpo_minutes | 15 | — |
| rto_minutes | 240 | — |
| cost_class | low | — |
Deployment¶
| Granularity | standalone host |
| Topologies | saas-default, hybrid, on-prem |
| Runtime | aks; replicas 2–8 |
| Ports | https 8200, grpc 8202, silo 12200, gateway 31200 |
Current implementation¶
ConnectSoft.ApiGatewayTemplate, the Blazor shell and microfrontend library, ConnectSoft.Blazor.UIKit and OpenIddict exist as template foundations; the 41 design prototypes show screens with hard-coded data. No BFF, composition, session projection or command forwarding exists.
Later capabilities¶
Per-screen GraphQL-style field selection to cut payloads for the VS Code extension; offline-tolerant command queue in the CLI with server-side idempotency; tenant-branded Studio themes.
Decisions and traceability¶
| Family | Ids |
|---|---|
| FR | FR-01, FR-05, FR-10 |
| FUX | FUX-01, FUX-02, FUX-03, FUX-04, FUX-06, FUX-07, FUX-08, FUX-09, FUX-11, FUX-12 |
| NFR | NFR-01, NFR-02, NFR-03, NFR-10 |
| XL | XL-01, XL-05, XL-06, XL-12 |