Skip to content

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"))
Hold "Alt" / "Option" to enable pan & zoom

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
Hold "Alt" / "Option" to enable pan & zoom

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
Hold "Alt" / "Option" to enable pan & zoom
stateDiagram-v2
    [*] --> Trial
    Trial --> Active: Convert
    Active --> PastDue: InvoiceOverdue
    PastDue --> Active: Paid
    PastDue --> Suspended: GracePeriodEnded
    Suspended --> Active: Paid
    Active --> Cancelled: Cancel
    Cancelled --> [*]
Hold "Alt" / "Option" to enable pan & zoom

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