Data Model¶
Target Architecture — Final-State Design
This page describes the final-state logical data model of the Marketplace Platform. Each bounded context owns its schema; there are no cross-context foreign keys. Relational data persists with NHibernate on Azure SQL / PostgreSQL; package bytes live in object/artifact stores (see Storage).
The Marketplace data model is partitioned by bounded context. Within a context, NHibernate maps the aggregate roots to relational tables; across contexts, relationships are logical references (by id) reconciled through events rather than database joins.
Logical model¶
The core entities and their logical relationships:
- A MarketplaceAsset has many AssetVersions; each version has one AssetPackage and declares many AssetDependencies.
- Each AssetVersion can be evaluated into many AssetCompatibility verdicts (one per target).
- An AssetInstallation records the application of one AssetVersion into a target and references the License that authorized it.
- A License binds a tenant and an asset under a PricingPlan.
- A Publisher owns many MarketplaceAssets; an AssetReview rates an asset (and version) by a reviewer.
Entity-relationship diagram¶
erDiagram
MarketplaceAsset ||--o{ AssetVersion : "has versions"
MarketplaceAsset ||--o{ AssetReview : "is reviewed by"
Publisher ||--o{ MarketplaceAsset : "publishes"
MarketplaceAsset ||--o| PricingPlan : "priced by"
AssetVersion ||--o{ AssetInstallation : "installed as"
License ||--o{ AssetInstallation : "authorizes"
MarketplaceAsset ||--o{ License : "licensed by"
MarketplaceAsset {
string AssetId PK
string Name
string AssetType
string PublisherId FK
string Status
string PricingPlanId FK
decimal AggregateRating
int RatingCount
string TenantId
}
AssetVersion {
string AssetVersionId PK
string AssetId FK
string SemanticVersion
string Status
bool Signed
string PackageId
datetime ReleasedAt
string TenantId
}
AssetInstallation {
string InstallationId PK
string AssetId FK
string AssetVersionId FK
string LicenseId FK
string TargetProjectId
string Environment
string Status
string RequestedBy
datetime InstalledAt
string TenantId
}
License {
string LicenseId PK
string AssetId FK
string TenantId
string PricingPlanId FK
string Status
int Seats
datetime GrantedAt
datetime ExpiresAt
}
AssetReview {
string AssetReviewId PK
string AssetId FK
string AssetVersionId FK
string ReviewerId
int Rating
string Status
int HelpfulCount
datetime CreatedAt
string TenantId
}
Cross-context references are logical
The FK annotations above denote logical references by identifier. Because each context has its own database/schema, these are not enforced as physical foreign keys across contexts; referential integrity across boundaries is maintained by domain/integration events and reconciliation, per the Event Envelope.
Schema ownership¶
| Schema / database | Owning context | Core tables |
|---|---|---|
marketplace_catalog |
Catalog & Search | marketplace_asset, asset_media, asset_tag |
marketplace_publishing |
Publishing & Versioning | asset_version, asset_package, version_dependency |
marketplace_compat |
Compatibility & Dependencies | asset_compatibility, compatibility_finding, asset_dependency |
marketplace_install |
Installation | asset_installation, installation_step, installed_artifact_ref |
marketplace_commerce |
Commerce | license, license_seat, pricing_plan, price_tier |
marketplace_publisher |
Publisher | publisher, publisher_contact, publisher_signing_key |
marketplace_reviews |
Reviews | asset_review, review_vote, review_moderation_note |
Multi-tenancy¶
- Tenant column on every table — every row carries a
TenantId; all repositories apply a tenant filter as a global NHibernate filter, and every query asserts the caller's tenant scope. - Isolation model — the default model is a shared schema with row-level
TenantIdpartitioning; high-volume or regulated tenants can be promoted to a dedicated database per context without changing the domain model. - Cross-tenant catalog — public/shared assets (e.g. ConnectSoft-published) are modeled with a platform tenant and surfaced to tenants through catalog visibility rules, never by leaking another tenant's rows.
- Event scoping — every emitted event carries
tenantId; consumers re-assert it before persisting projections.
Retention¶
| Data | Retention policy |
|---|---|
Catalog metadata (marketplace_asset, asset_version) |
Retained for the life of the asset; withdrawn/yanked rows kept (soft-deleted) for audit and reproducibility. |
| Asset packages (Blob/Artifacts/ACR) | Retained while any version referencing them is non-yanked plus a 12-month grace window; immutable once published. |
Installations (asset_installation) |
Retained for the life of the target project plus 24 months for provenance/audit. |
Compatibility verdicts (asset_compatibility) |
Cached short-term (Redis) and persisted 90 days; recomputed on demand thereafter. |
Licenses (license) |
Retained for the life of the entitlement plus the statutory billing retention from ConnectSoft.Extensions.Saas.Billing. |
Reviews (asset_review) |
Retained for the life of the asset; removed reviews soft-deleted with moderation notes. |
| Domain/integration events | Immutable; retained on the bus per topic policy and archived to the Knowledge Platform for replay. |
Provenance and traceability¶
Every installable outcome links back to its source: AssetInstallation → AssetVersion → AssetPackage → Publisher, plus the License that authorized it and the traceId/correlationId/requestedBy (task- id) that initiated it. This is the data backbone that lets Governance and the Knowledge Platform answer "what was installed, from where, by whom, under what license" for any tenant.
Continue to Storage, Aggregate Roots, and the reference Artifact Metadata.