LicenseService¶
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-28 Marketplace Acquisition & Licensing |
| Platform / plane | Marketplace / Capability & Tools |
| Deployment granularity | module of host · host MarketplaceHost |
| Status | target · ConnectSoft.Factory.Marketplace.Licensing |
| Built from | connectsoft-saas-billing · libraries: connectsoft-extensions-saas-abstractions, connectsoft-extensions-messaging-model, connectsoft-extensions-persistence-model-nhibernate |
| Decisions | ADR-0015, ADR-0020 |
| Traceability | FR-04 FUX-08 NFR-13 NFR-15 XL-03 XL-13 |
Purpose and responsibilities¶
LicenseService is the commercial side of the marketplace, built on the same SaaS foundations as the Factory's own commercial context (BC-34, D34). A pricing plan says how a listing is priced (free, included in an edition, per seat, per usage metered through the Factory's metering, one-time) and what the publisher's revenue share is; an entitlement is a tenant's acquisition against its active Factory subscription — spend above the threshold needs an approval of class spend (D21) — with validity, seats and license terms, following the subscription's state (suspended, restored, expired); a publisher is certified by Platform admin with an agreement before it may list publicly, and collects reviews from entitled tenants. Billing lines land on the tenant's Factory invoice; the marketplace never bills separately.
It absorbs the June-2026 pricing, publisher-portal and review-rating rows.
Context and boundaries¶
flowchart LR
S["LicenseService"]
MarketplaceCatalogService["MarketplaceCatalogService"] -->|grpc| S
StudioBff["StudioBff"] -->|sync-http| S
S -->|grpc| FactoryCommercialService["FactoryCommercialService"]
S -->|grpc| RunControlService["RunControlService"]
S -->|sync-http| ext_saas_templates["ext-saas-templates"]
S -->|grpc| ArtifactService["ArtifactService"]
S -. "PricingPlanChanged" .-> E_PricingPlanChanged(("event"))
S -. "EntitlementGranted" .-> E_EntitlementGranted(("event"))
S -. "EntitlementStateChanged" .-> E_EntitlementStateChanged(("event"))
S -. "PublisherCertified" .-> E_PublisherCertified(("event"))
Entitlement state is enforced by the registry and policy: a suspended entitlement leaves installations in place but refuses new runs using the asset until restored.
Domain model¶
PricingPlan — The price of a listing: model, amount and currency, included editions, metering dimension for usage pricing, trial terms, and the publisher's revenue share. (catalog entry)
| Field | Type | Required | Description |
|---|---|---|---|
pricingId |
guid |
yes | |
tenantId |
string |
yes | |
listingId |
guid |
yes | |
model |
enum:PricingModel |
yes | |
amountUsd |
decimal |
no | |
includedEditions |
list:string |
yes | |
meteringDimension |
string |
no | e.g. runs, tokens, instances |
trialDays |
int |
no | |
revenueSharePct |
decimal |
yes | |
active |
bool |
yes |
Invariants:
- Usage pricing meters through the Factory's metering (BC-34) and is billed on the tenant's Factory invoice; the marketplace never bills separately.
Events: PricingPlanChanged · Tables: PricingPlans · Repository: PricingPlanRepository
Entitlement — A tenant's right to install and use a listing: the acquisition (plan, seats or usage terms), state, validity, the Factory subscription it bills to, and the license record with terms and provenance. (catalog entry)
| Field | Type | Required | Description |
|---|---|---|---|
entitlementId |
guid |
yes | |
tenantId |
string |
yes | |
listingId |
guid |
yes | |
pricingId |
guid |
yes | |
subscriptionId |
guid |
yes | Factory subscription (BC-34). |
seats |
int |
no | |
state |
enum:EntitlementState |
yes | |
validFrom |
datetime |
yes | |
validTo |
datetime |
no | |
licenseTermsRef |
string |
yes | |
acquiredBy |
string |
yes |
Invariants:
- An entitlement is granted only against an active Factory subscription whose edition allows the listing kind (D34).
- Suspension of the subscription suspends entitlements; installations stay but new runs using the asset are refused by policy.
stateDiagram-v2
Pending --> Active : AcquisitionConfirmed
Active --> Suspended : SubscriptionSuspendedOrPaymentFailed
Suspended --> Active : Restored
Active --> Expired : ValidToPassed
Active --> Revoked : ListingRevokedOrViolation
Events: EntitlementGranted, EntitlementStateChanged · Tables: Entitlements · Repository: EntitlementRepository
Publisher — A publisher (ConnectSoft, a partner organization or a tenant) with certification state, identity, payout details by reference, agreements, and the ratings and reviews of its listings. (catalog entry)
| Field | Type | Required | Description |
|---|---|---|---|
publisherId |
guid |
yes | |
tenantId |
string |
yes | |
name |
string |
yes | |
state |
enum:PublisherState |
yes | |
agreementRef |
string |
yes | |
payoutRef |
string |
no | |
ratingAverage |
decimal |
no | |
certifiedAt |
datetime |
no |
Invariants:
- Only Certified publishers may list publicly; certification is a Platform admin decision with evidence.
- Reviews come from tenants with an active or past entitlement; one review per tenant per listing.
Events: PublisherCertified · Tables: Publishers, Reviews · Repository: PublisherRepository
erDiagram
PricingPlan {
guid pricingId PK
string tenantId
guid listingId
enum_PricingModel model
decimal amountUsd
list_string includedEditions
string meteringDimension
int trialDays
decimal revenueSharePct
bool active
}
Entitlement {
guid entitlementId PK
string tenantId
guid listingId
guid pricingId
guid subscriptionId
int seats
enum_EntitlementState state
datetime validFrom
datetime validTo
string licenseTermsRef
string acquiredBy
}
Publisher {
guid publisherId PK
string tenantId
string name
enum_PublisherState state
string agreementRef
string payoutRef
decimal ratingAverage
datetime certifiedAt
}
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 |
|---|---|---|---|
| Marketplace store | primary | azure-sql / nhibernate-sqlserver | PricingPlans, Entitlements, Publishers, Reviews |
| Table | Owner | Columns | Indexes |
|---|---|---|---|
dbo.PricingPlans |
PricingPlan | 10 | 1 |
dbo.Entitlements |
Entitlement | 11 | 1 |
dbo.Publishers |
Publisher | 8 | 0 |
dbo.Reviews |
Publisher | 6 | 0 |
Full DDL: appendix.
APIs¶
| Method | Path | Operation | Scope | Side effects | Idempotency | Gateway | Summary |
|---|---|---|---|---|---|---|---|
GET |
/factory/marketplace/listings/{listingId}/pricing |
GetPricing | factory.marketplace.read |
read | natural | GW-03 | Pricing plans of a listing with the caller's edition applied. |
PUT |
/factory/marketplace/listings/{listingId}/pricing |
SetPricing | factory.marketplace.publish |
shared-write | natural | GW-03 | Set a pricing plan (publisher; public plans reviewed by Platform admin). |
POST |
/factory/marketplace/listings/{listingId}/acquire |
AcquireListing | factory.marketplace.acquire |
spend | key-header | GW-03 | Acquire an entitlement against the tenant's Factory subscription (spend class above the threshold needs an approval). |
GET |
/factory/marketplace/entitlements |
ListEntitlements | factory.marketplace.read |
read | natural | GW-03 | The tenant's entitlements with state, validity and terms. |
POST |
/factory/marketplace/publishers |
ApplyAsPublisher | factory.marketplace.publish |
shared-write | key-header | GW-03 | Apply for publisher certification (agreement, identity). |
POST |
/factory/marketplace/publishers/{publisherId}/certify |
CertifyPublisher | factory.marketplace.admin |
publication | natural | GW-03 | Certify or suspend a publisher (Platform admin; publication class). |
POST |
/factory/marketplace/listings/{listingId}/reviews |
ReviewListing | factory.marketplace.read |
shared-write | natural | GW-03 | Rate and review a listing the tenant is or was entitled to. |
| gRPC service | Method | Streaming | Visibility | Side effects | Summary |
|---|---|---|---|---|---|
Licensing |
CheckEntitlement |
none | internal | read | Is the tenant entitled to install or use a listing now? |
Messages¶
| Event | Topic | Scope / v | Aggregate | Consumers | Summary |
|---|---|---|---|---|---|
| PricingPlanChanged | licensing.pricing.v1.pricing-plan-changed |
integration / 1 | PricingPlan | MarketplaceCatalogService, StudioBff, AuditService | A pricing plan was set or changed. |
| EntitlementGranted | licensing.entitlements.v1.entitlement-granted |
integration / 1 | Entitlement | MarketplaceCatalogService, FactoryCommercialService, StudioBff, AuditService | A tenant is entitled to a listing. |
| EntitlementStateChanged | licensing.entitlements.v1.entitlement-state-changed |
integration / 1 | Entitlement | MarketplaceCatalogService, AgentRegistryService, StudioBff, AuditService | Suspended, restored, expired or revoked. |
| PublisherCertified | licensing.publishers.v1.publisher-certified |
integration / 1 | Publisher | MarketplaceCatalogService, StudioBff, AuditService | A publisher was certified or suspended. |
PricingPlanChanged payload:
| Field | Type | Required | Description |
|---|---|---|---|
pricingId |
guid |
yes | |
listingId |
guid |
yes | |
model |
enum:PricingModel |
yes |
EntitlementGranted payload:
| Field | Type | Required | Description |
|---|---|---|---|
entitlementId |
guid |
yes | |
tenantId |
string |
yes | |
listingId |
guid |
yes | |
subscriptionId |
guid |
yes | |
validTo |
datetime |
no |
EntitlementStateChanged payload:
| Field | Type | Required | Description |
|---|---|---|---|
entitlementId |
guid |
yes | |
tenantId |
string |
yes | |
listingId |
guid |
yes | |
fromState |
enum:EntitlementState |
yes | |
toState |
enum:EntitlementState |
yes |
PublisherCertified payload:
| Field | Type | Required | Description |
|---|---|---|---|
publisherId |
guid |
yes | |
tenantId |
string |
yes | |
state |
enum:PublisherState |
yes |
| Command | Handled by | Result events | Summary |
|---|---|---|---|
SetPricing |
PricingPlan | PricingPlanChanged | |
AcquireListing |
Entitlement | EntitlementGranted | |
CertifyPublisher |
Publisher | PublisherCertified | |
ReviewListing |
Publisher | — |
| Event | Producer | Handler | Purpose |
|---|---|---|---|
| SubscriptionChanged | FactoryCommercialService | FollowSubscription |
Suspend, restore or expire entitlements with the Factory subscription. |
| ApprovalDecided | RunControlService | ApplySpendApproval |
Acquisitions above the spend threshold wait for approval. |
| ListingStateChanged | MarketplaceCatalogService | FollowListing |
Delisted or suspended listings suspend or revoke entitlements. |
| ListingPublished | MarketplaceCatalogService | TrackListing |
Pricing plans attach to Listed listings. |
| ListingInstalled | MarketplaceCatalogService | CountSeats |
Seat consumption. |
| ListingRemoved | MarketplaceCatalogService | ReleaseSeats |
Seat release. |
Workers and jobs¶
| Worker | Trigger | Output events | Retry | Idempotency key | Summary |
|---|---|---|---|---|---|
| ExpiryAndSeatSweeper | schedule 0 1 * * * |
EntitlementStateChanged | fixed | entitlementId + day |
Expires entitlements past validity and reconciles seat counts with installations. |
| RevenueShareReporter | schedule 0 2 1 * * |
— | fixed | publisherId + month |
Produces publisher revenue-share statements from the Factory's reconciled usage and invoices. |
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
[*] --> Pending
Pending --> Active: AcquisitionConfirmed (spend approval above threshold)
Active --> Suspended: subscription suspended or payment failed
Suspended --> Active: Restored
Active --> Expired: validTo passed
Active --> Revoked: listing revoked or violation
Expired --> [*]
Revoked --> [*]
Options and configuration¶
| Section | Key | Type | Default | Reloadable | Secret | Summary |
|---|---|---|---|---|---|---|
Marketplace:Licensing |
SpendApprovalThresholdUsd |
decimal | 500 |
yes | no | Acquisitions above this need an approval of class spend (D21). |
Marketplace:Licensing |
DefaultRevenueSharePct |
decimal | 70 |
yes | no | Publisher share for partner listings. |
Metrics and observability¶
| Metric | Instrument | Unit | Dimensions | Summary |
|---|---|---|---|---|
connectsoft.factory.marketplace.entitlements |
updown | {entitlement} | tenant, state | Entitlements by state. |
connectsoft.factory.marketplace.acquisitions |
counter | {acquisition} | model, outcome | Acquisitions by pricing model and outcome. |
connectsoft.factory.marketplace.publishers |
updown | {publisher} | state | Publishers by state. |
Security¶
| Authentication | client-credentials |
| Scopes | factory.marketplace.read (Pricing, own entitlements, reviews.); factory.marketplace.acquire (Acquire (Workspace admin; spend approval above threshold).); factory.marketplace.publish (Pricing and publisher application.); factory.marketplace.admin (Certification (Platform admin).) |
| Roles | WorkspaceAdmin, AssetMaintainer, PlatformAdmin, Observer |
| Tenant isolation | row-filter |
| Data classification | confidential |
| Max side-effect class | spend |
| Secrets | — |
Dependencies¶
Outbound
| Target | Kind | Purpose | Failure mode | Required |
|---|---|---|---|---|
| FactoryCommercialService | grpc | Subscription and edition; billing lines; metering. | fail-fast | yes |
| RunControlService | grpc | Spend approvals. | fail-fast | yes |
| ext-saas-templates | sync-http | ProductsCatalog, Entitlements and Billing foundations. | fail-fast | yes |
| ArtifactService | grpc | License terms and agreements. | degrade | yes |
Inbound
| Target | Kind | Purpose | Failure mode | Required |
|---|---|---|---|---|
| MarketplaceCatalogService | grpc | CheckEntitlement on install. | — | yes |
| StudioBff | sync-http | Pricing, entitlement and publisher views. | — | yes |
Failure and recovery¶
| Scenario | Detection | Recovery | Data impact | RTO (min) |
|---|---|---|---|---|
| Subscription suspended | SubscriptionChanged | entitlements Suspended; installations stay; new runs refused by policy until restored | none | 0 |
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 MarketplaceHost |
| Topologies | saas-default |
| Runtime | —; replicas —–— |
| Ports | — |
Current implementation¶
The SaaS Billing, Entitlements and Metering templates exist (scaffold-only usage in the SaaS Control Plane prototype); nothing marketplace-specific exists.
Later capabilities¶
Partner payouts through a payment connector; enterprise agreements covering many tenants; usage-based pricing for operated products per customer tenant.
Decisions and traceability¶
| Family | Ids |
|---|---|
| FR | FR-04 |
| FUX | FUX-08 |
| NFR | NFR-13, NFR-15 |
| XL | XL-03, XL-13 |