StoreProfileService¶
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-42 Data & Event Foundations |
| Platform / plane | Data & Event Platform / Data & Event |
| Deployment granularity | module of host · host DataEventFoundations |
| Status | target · ConnectSoft.Factory.DataEvent.Stores |
| Built from | connectsoft-microservice · libraries: connectsoft-extensions-persistence-model-nhibernate, connectsoft-extensions-persistence-model, connectsoft-extensions-saas-abstractions, connectsoft-extensions-messaging-model |
| Decisions | ADR-0006, ADR-0012, ADR-0013, ADR-0017 |
| Traceability | FR-12 FUX-11 NFR-05 NFR-06 NFR-16 XL-09 |
Purpose and responsibilities¶
StoreProfileService keeps the original design's §54 separation — operational relational, worker durable, cache, artifact, vector, graph, event transport, audit, analytics, secrets — as store classes, the engine profile each class runs on each topology (NHibernate on SQL Server or PostgreSQL per ADR-0006, Qdrant or Azure AI Search per ADR-0013, Azure Blob, Redis, the messaging transport per ADR-0012), and the binding of every logical store in the stores catalog to its owning service, tenant-selected region, customer-managed key and backup class. It answers ResolveBinding for hosts at start-up and for provisioning, and reconciles bindings against observed state every six hours.
Context and boundaries¶
flowchart LR
S["StoreProfileService"]
ControlPlaneApi["ControlPlaneApi"] -->|grpc| S
StudioBff["StudioBff"] -->|sync-http| S
S -->|grpc| SecretBrokerService["SecretBrokerService"]
S -. "StoreProfileActivated" .-> E_StoreProfileActivated(("event"))
S -. "StoreBindingChanged" .-> E_StoreBindingChanged(("event"))
Two rules are enforced here, not just documented: one owning service per store (other services go through its API or events), and two services may share a server but never a schema. A binding whose region violates the tenant's residency rule is refused through RetentionResidencyService.EvaluateResidency.
Domain model¶
StoreProfile — One engine profile for one store class on one topology: engine, adapter library, migration convention, backup class (RPO, RTO), encryption, and whether it is authoritative or derived. (catalog entry)
| Field | Type | Required | Description |
|---|---|---|---|
profileId |
string |
yes | e.g. nhibernate-sqlserver, vector-qdrant, azure-blob-immutable |
storeClass |
enum:StoreClass |
yes | |
topology |
string |
yes | |
engine |
string |
yes | |
adapterLibrary |
string |
yes | Library id from the libraries catalog. |
migrationConvention |
string |
yes | FluentMigrator per service schema; TenantId column by default |
rpoMinutes |
int |
yes | |
rtoMinutes |
int |
yes | |
encryption |
string |
yes | |
authoritative |
bool |
yes | False for derived stores (vector, cache, projections). |
Invariants:
- A derived store (cache, vector, projection) is never the authoritative state of anything and must be rebuildable from an authoritative one.
- The pilot backup class (D30: RPO 15 min, RTO 4 h) is the minimum for authoritative stores; derived stores may be looser.
Events: StoreProfileActivated · Tables: StoreProfiles · Repository: StoreProfileRepository
StoreBinding — The binding of one logical store (the stores catalog id) to its owning service, profile, tenant-selected region, encryption key and state; the runtime truth of where data lives. (catalog entry)
| Field | Type | Required | Description |
|---|---|---|---|
storeId |
string |
yes | Logical store id from the stores catalog. |
tenantId |
string |
yes | Platform tenant for shared stores; a tenant id for per-tenant stores in the on-prem profile. |
ownerService |
string |
yes | |
profileId |
string |
yes | |
region |
string |
yes | Tenant-selected region (D31): westeurope default, israelcentral available |
keyReference |
string |
yes | Customer-managed key reference in the secret store. |
state |
enum:BindingState |
yes | |
schemas |
list:string |
yes | Database schemas owned per service on a shared server. |
Invariants:
- Exactly one owning service per store; other services reach its data through that service's API or events, never the store (ownership rule).
- Two services may share a server but never a schema.
- A binding's region must satisfy the tenant's residency rule.
stateDiagram-v2
Planned --> Provisioned : Provisioned
Provisioned --> Active : MigrationsApplied
Active --> Draining : DecommissionRequested
Draining --> Decommissioned : DataMovedOrDeleted
Events: StoreBindingChanged · Tables: StoreBindings · Repository: StoreBindingRepository
erDiagram
StoreProfile {
string profileId PK
enum_StoreClass storeClass
string topology
string engine
string adapterLibrary
string migrationConvention
int rpoMinutes
int rtoMinutes
string encryption
bool authoritative
}
StoreBinding {
string storeId PK
string tenantId
string ownerService
string profileId
string region
string keyReference
enum_BindingState state
list_string schemas
}
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 |
|---|---|---|---|
| Data & Event foundations store | primary | azure-sql / nhibernate-sqlserver | StoreProfiles, StoreBindings |
| Table | Owner | Columns | Indexes |
|---|---|---|---|
dbo.StoreProfiles |
StoreProfile | 11 | 0 |
dbo.StoreBindings |
StoreBinding | 8 | 2 |
Full DDL: appendix.
APIs¶
| Method | Path | Operation | Scope | Side effects | Idempotency | Gateway | Summary |
|---|---|---|---|---|---|---|---|
GET |
/factory/data/stores |
ListStoreBindings | factory.data.read |
read | natural | GW-03 | Logical stores with owner, profile, region, backup class and state (the storage catalog at runtime). |
GET |
/factory/data/store-profiles |
ListStoreProfiles | factory.data.read |
read | natural | GW-03 | Engine profiles per store class and topology. |
PUT |
/factory/data/stores/{storeId} |
SetStoreBinding | factory.data.admin |
production | natural | GW-03 | Bind a logical store to a profile, region and key (Platform admin; applied by provisioning). |
| gRPC service | Method | Streaming | Visibility | Side effects | Summary |
|---|---|---|---|---|---|
Stores |
ResolveBinding |
none | internal | read | Resolve a logical store to its connection reference and region for a tenant (used by hosts at start-up and by provisioning). |
Messages¶
| Event | Topic | Scope / v | Aggregate | Consumers | Summary |
|---|---|---|---|---|---|
| StoreProfileActivated | data-foundations.stores.v1.store-profile-activated |
integration / 1 | StoreProfile | StudioBff, AuditService | An engine profile is active for a class and topology. |
| StoreBindingChanged | data-foundations.stores.v1.store-binding-changed |
integration / 1 | StoreBinding | StudioBff, AuditService, RetentionResidencyService | A logical store's binding or state changed. |
StoreProfileActivated payload:
| Field | Type | Required | Description |
|---|---|---|---|
profileId |
string |
yes | |
storeClass |
enum:StoreClass |
yes | |
topology |
string |
yes |
StoreBindingChanged payload:
| Field | Type | Required | Description |
|---|---|---|---|
storeId |
string |
yes | |
tenantId |
string |
yes | |
ownerService |
string |
yes | |
region |
string |
yes | |
state |
enum:BindingState |
yes |
| Command | Handled by | Result events | Summary |
|---|---|---|---|
SetStoreBinding |
StoreBinding | StoreBindingChanged |
None modelled
No consumed events are modelled for this item yet.
Workers and jobs¶
| Worker | Trigger | Output events | Retry | Idempotency key | Summary |
|---|---|---|---|---|---|
| BindingReconciler | schedule 0 */6 * * * |
StoreBindingChanged | fixed | storeId + window |
Compares bindings with the observed state from provisioning and flags drift (missing schema, wrong region, key rotation overdue). |
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.
stateDiagram-v2
[*] --> Planned
Planned --> Provisioned: Provisioned (Runtime Cloud)
Provisioned --> Active: MigrationsApplied
Active --> Draining: DecommissionRequested
Draining --> Decommissioned: DataMovedOrDeleted
Decommissioned --> [*]
Options and configuration¶
| Section | Key | Type | Default | Reloadable | Secret | Summary |
|---|---|---|---|---|---|---|
DataEvent:Stores |
DefaultRegion |
string | westeurope |
no | no | Region for tenants that did not select one (D31). |
DataEvent:Stores |
AllowedRegions |
string[] | [westeurope, israelcentral] |
yes | no | Regions a tenant may select. |
Metrics and observability¶
| Metric | Instrument | Unit | Dimensions | Summary |
|---|---|---|---|---|
connectsoft.factory.data_event.store_bindings |
updown | {store} | storeClass, region, state | Bindings by class, region and state. |
connectsoft.factory.data_event.binding_drift |
counter | {store} | kind | Drift findings from reconciliation. |
Security¶
| Authentication | client-credentials |
| Scopes | factory.data.read (Read stores and profiles.); factory.data.admin (Bind stores (Platform admin, provisioning).) |
| Roles | PlatformAdmin, Operator, Observer |
| Tenant isolation | row-filter |
| Data classification | internal |
| Max side-effect class | production |
| Secrets | — |
Dependencies¶
Outbound
| Target | Kind | Purpose | Failure mode | Required |
|---|---|---|---|---|
| SecretBrokerService | grpc | Key and connection references; never the secrets. | fail-fast | yes |
Inbound
| Target | Kind | Purpose | Failure mode | Required |
|---|---|---|---|---|
| ControlPlaneApi | grpc | ResolveBinding at tenant creation and host start-up. | — | yes |
| StudioBff | sync-http | Storage views. | — | yes |
Failure and recovery¶
| Scenario | Detection | Recovery | Data impact | RTO (min) |
|---|---|---|---|---|
| Binding violates residency | residency rule check on SetStoreBinding | refused with 422 and the rule id | none | 0 |
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 | module of host · host DataEventFoundations |
| Topologies | saas-default, hybrid, on-prem, hybrid-compute |
| Runtime | —; replicas —–— |
| Ports | — |
Current implementation¶
The storage catalog and model/stores.yaml list the logical stores; templates ship SQL Server, MySQL and PostgreSQL migrations, Orleans AdoNet and Azure Table storage, Audit.NET SQL and an in-memory vector store. No binding registry or reconciliation exists.
Later capabilities¶
Analytics warehouse profile (usage, cost, BI projections); graph store profile selection; multi-region placement with tracked moves.
Decisions and traceability¶
| Family | Ids |
|---|---|
| FR | FR-12 |
| FUX | FUX-11 |
| NFR | NFR-05, NFR-06, NFR-16 |
| XL | XL-09 |