FactoryCommercialService¶
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-34 Factory Commercial |
| Platform / plane | Control Plane / Control |
| Deployment granularity | module of host · host ControlPlaneApi |
| Status | target · ConnectSoft.Factory.Commercial |
| Built from | connectsoft-saas-billing · libraries: connectsoft-extensions-saas-abstractions, connectsoft-extensions-saas-asp-net-core, connectsoft-extensions-saas-billing, connectsoft-extensions-saas-metering, connectsoft-extensions-messaging-model, connectsoft-extensions-persistence-model-nhibernate |
| Decisions | ADR-0020, ADR-0015 |
| Traceability | FR-09 FUX-08 NFR-12 XL-12 |
Purpose and responsibilities¶
The Factory is itself a ConnectSoft SaaS product, so it is sold and metered with the same templates that generated products use (D34, commercial model, ADR-0020). FactoryCommercialService is the Factory's instance of those templates: editions as Published assets (BC-04), a subscription per tenant with seats and billing model, period meters accumulated from Usage Accounting, invoices issued at period close, and the entitlement answers that BC-33 checks read.
It supports all three commercial types the user asked for: internal budget controls (an internal-budget billing model that never invoices but enforces budgets), usage-based billing, and seat or edition plans, including the ManagedProduct edition for tenants whose generated products ConnectSoft operates (D33).
Context and boundaries¶
flowchart LR
S["FactoryCommercialService"]
StudioBff["StudioBff"] -->|sync-http| S
CostUsageService["CostUsageService"] -->|event| S
S -->|library| ext_saas_templates["ext-saas-templates"]
S -->|grpc| AgentRegistryService["AgentRegistryService"]
S -. "SubscriptionChanged" .-> E_SubscriptionChanged(("event"))
S -. "InvoiceIssued" .-> E_InvoiceIssued(("event"))
The service does not price usage (pricing revisions live in BC-05) and does not define what an edition contains (the edition definition is a Published asset). It owns the contract between a tenant and ConnectSoft and the money that results.
Domain model¶
FactorySubscription — A tenant's subscription to an edition with seats, quotas and billing terms; the Factory's own instance of the SaaS Billing Subscription aggregate. (catalog entry)
| Field | Type | Required | Description |
|---|---|---|---|
subscriptionId |
guid |
yes | |
tenantId |
string |
yes | |
edition |
enum:EditionCode |
yes | |
editionReleaseId |
string |
yes | Published edition definition (BC-04). |
seats |
int |
yes | |
state |
enum:SubscriptionState |
yes | |
billingModel |
string |
yes | internal-budget |
currentPeriodStart |
datetime |
yes | |
currentPeriodEnd |
datetime |
yes | |
version |
long |
yes |
Invariants:
- A tenant has at most one active subscription; edition changes are recorded as a new period.
- Entitlement checks (BC-33) read the subscription's edition release, never a mutable edition definition.
stateDiagram-v2
Trial --> Active : Convert
Active --> PastDue : InvoiceOverdue
PastDue --> Active : Paid
PastDue --> Suspended : GracePeriodEnded
Suspended --> Active : Paid
Active --> Cancelled : Cancel
Events: SubscriptionChanged, InvoiceIssued · Tables: FactorySubscriptions, Invoices · Repository: FactorySubscriptionRepository
erDiagram
FactorySubscription {
guid subscriptionId PK
string tenantId
enum_EditionCode edition
string editionReleaseId
int seats
enum_SubscriptionState state
string billingModel
datetime currentPeriodStart
datetime currentPeriodEnd
long version
}
Invoice {
guid invoiceId
datetime periodStart
datetime periodEnd
decimal totalUsd
string state
string usageSnapshotRef
}
FactorySubscription ||--o{ Invoice : contains
stateDiagram-v2
[*] --> Trial
Trial --> Active: Convert
Active --> PastDue: InvoiceOverdue
PastDue --> Active: Paid
PastDue --> Suspended: GracePeriodEnded
Suspended --> Active: Paid
Active --> Cancelled: Cancel
Cancelled --> [*]
A suspended tenant keeps read access to its data and its export rights (D31); runs are refused at submission with a commercial reason.
Entities and value objects¶
Invoice (entity, in FactorySubscription) — Period invoice built from reconciled usage records and seat counts.
| Field | Type | Required | Description |
|---|---|---|---|
invoiceId |
guid |
yes | |
periodStart |
datetime |
yes | |
periodEnd |
datetime |
yes | |
totalUsd |
decimal |
yes | |
state |
string |
yes | |
usageSnapshotRef |
string |
yes |
Data model and storage¶
| Store | Role | Engine / profile | Tables |
|---|---|---|---|
| Control plane relational store | primary | azure-sql / nhibernate-sqlserver | FactorySubscriptions, Invoices |
| Table | Owner | Columns | Indexes |
|---|---|---|---|
dbo.FactorySubscriptions |
FactorySubscription | 10 | 1 |
dbo.Invoices |
Invoice | 8 | 0 |
Full DDL: appendix.
APIs¶
| Method | Path | Operation | Scope | Side effects | Idempotency | Gateway | Summary |
|---|---|---|---|---|---|---|---|
GET |
/factory/subscription |
GetSubscription | factory.commercial.read |
read | natural | GW-03 | The tenant's subscription, edition, seats and current period. |
PUT |
/factory/subscription |
ChangeSubscription | factory.commercial.admin |
spend | key-header | GW-03 | Change edition or seats (Platform admin of the tenant); takes effect at the next period unless upgraded. |
GET |
/factory/invoices |
ListInvoices | factory.commercial.read |
read | natural | GW-03 | Invoices with usage snapshots. |
| gRPC service | Method | Streaming | Visibility | Side effects | Summary |
|---|---|---|---|---|---|
Commercial |
GetEntitlements |
none | internal | read | Edition entitlements and quotas for BC-33 checks. |
Messages¶
| Event | Topic | Scope / v | Aggregate | Consumers | Summary |
|---|---|---|---|---|---|
| SubscriptionChanged | factory-commercial.subscriptions.v1.subscription-changed |
integration / 1 | FactorySubscription | LicenseService, FleetInventoryService, StudioBff, AuditService, CostUsageService | Edition, seats or state changed. |
| InvoiceIssued | factory-commercial.invoices.v1.invoice-issued |
integration / 1 | FactorySubscription | StudioBff, StudioNotificationService | A period invoice was issued from reconciled usage. |
SubscriptionChanged payload:
| Field | Type | Required | Description |
|---|---|---|---|
subscriptionId |
guid |
yes | |
tenantId |
string |
yes | |
edition |
enum:EditionCode |
yes | |
state |
enum:SubscriptionState |
yes |
InvoiceIssued payload:
| Field | Type | Required | Description |
|---|---|---|---|
invoiceId |
guid |
yes | |
tenantId |
string |
yes | |
totalUsd |
decimal |
yes |
| Command | Handled by | Result events | Summary |
|---|---|---|---|
ChangeSubscription |
FactorySubscription | SubscriptionChanged |
| Event | Producer | Handler | Purpose |
|---|---|---|---|
| UsageRecorded | CostUsageService | AccumulateMeter |
Accumulate meters for the current period. |
| UsageReconciled | CostUsageService | AdjustMeter |
Adjust meters when estimates are confirmed. |
| ReservationSettled | CostUsageService | AccumulateRunCost |
Period cost per run. |
Workers and jobs¶
| Worker | Trigger | Output events | Retry | Idempotency key | Summary |
|---|---|---|---|---|---|
| InvoicingWorker | schedule 0 2 1 * * |
InvoiceIssued | exponential | subscriptionId + periodEnd |
Closes a period, snapshots reconciled usage and seat counts, issues the invoice through the SaaS Billing template. |
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.
Options and configuration¶
| Section | Key | Type | Default | Reloadable | Secret | Summary |
|---|---|---|---|---|---|---|
Commercial:Trial |
TrialDays |
int | 30 |
yes | no | Trial length for new tenants. |
Commercial:Dunning |
GraceDays |
int | 14 |
yes | no | Days in PastDue before suspension. |
Metrics and observability¶
| Metric | Instrument | Unit | Dimensions | Summary |
|---|---|---|---|---|
connectsoft.factory.commercial.subscriptions |
updown | {subscription} | edition, state | Subscriptions by edition and state. |
connectsoft.factory.commercial.invoiced_usd |
counter | USD | edition | Invoiced amounts. |
Security¶
| Authentication | oauth2-jwt |
| Scopes | factory.commercial.read (Read subscription and invoices.); factory.commercial.admin (Change subscription.) |
| Roles | PlatformAdmin, Observer |
| Tenant isolation | row-filter |
| Data classification | confidential |
| Max side-effect class | spend |
| Secrets | payment-provider (key-vault, rotate 180 d) |
Subscription and invoice data is classified confidential; only the tenant's Platform admin and Observers with the commercial scope read it, and payment credentials never enter the Factory (the payment provider integration of the SaaS Billing template holds them).
Dependencies¶
Outbound
| Target | Kind | Purpose | Failure mode | Required |
|---|---|---|---|---|
| ext-saas-templates | library | Billing, Metering, Entitlements and ProductsCatalog templates. | fail-fast | yes |
| AgentRegistryService | grpc | Published edition definitions. | fail-fast | yes |
Inbound
| Target | Kind | Purpose | Failure mode | Required |
|---|---|---|---|---|
| StudioBff | sync-http | Subscription and invoice views. | — | yes |
| CostUsageService | event | Usage and settlement feed. | — | yes |
Failure and recovery¶
| Scenario | Detection | Recovery | Data impact | RTO (min) |
|---|---|---|---|---|
| Payment provider unreachable at invoicing | invoicing worker failure | invoice stays Draft; retry with backoff; no suspension without an issued invoice | delayed invoice | 1440 |
NFR targets¶
| Target | Class value (pilot-batch) | Override |
|---|---|---|
| latency_p95_ms | 0 | — |
| availability_pct | 99.5 | — |
| throughput_rps | 10 | — |
| rpo_minutes | 15 | — |
| rto_minutes | 240 | — |
| cost_class | medium | — |
Deployment¶
| Granularity | module of host · host ControlPlaneApi |
| Topologies | saas-default, hybrid |
| Runtime | —; replicas —–— |
| Ports | — |
Runs in the saas-default and hybrid profiles only; an on-prem installation has no subscription and uses the internal-budget model through CostUsageService alone.
Current implementation¶
Nothing exists for the Factory. The five SaaS templates it extends exist (Tenants, ProductsCatalog, Entitlements, Billing, Metering) and are listed in the ecosystem composition with status "extend".
Later capabilities¶
Marketplace revenue share for partner-published assets (PF-07); reseller and multi-organization billing; per-product managed-operations invoicing lines from the Product Fleet Operations platform.
Decisions and traceability¶
| Family | Ids |
|---|---|
| FR | FR-09 |
| FUX | FUX-08 |
| NFR | NFR-12 |
| XL | XL-12 |