Skip to content

Aggregate Roots

Target Architecture — Final-State Design

This page describes the final-state domain model of the Marketplace Platform. Each aggregate root is a consistency boundary owned by one microservice, persisted with NHibernate on Azure SQL / PostgreSQL, and emitting domain events in the canonical event envelope. Naming follows Naming Conventions: singular PascalCase roots, {Root}Repository repositories.

The Marketplace owns ten aggregate roots across the seven bounded contexts. Each section below specifies the aggregate's purpose, fields, child entities, value objects, invariants, domain events, repository, and persistence.

MarketplaceAsset

Purpose — The catalog entry for a publishable asset of one of the nine asset types. The identity and metadata anchor that all versions, reviews, and installations reference. Owned by MarketplaceCatalogService (Catalog & Search).

FieldsAssetId (identity), Name, AssetType (enum of the nine types), PublisherId, Description, Tags, Status (Draft, PendingQualityScan, Published, Deprecated, Withdrawn), LatestVersionId, PricingPlanId, LicenseRequired, AggregateRating, RatingCount, TenantId, CreatedAt, UpdatedAt.

EntitiesAssetMedia (icons, screenshots), AssetTagAssignment.

Value ObjectsAssetType, AssetCategory, TagSet, RatingSummary, TenantId.

Invariants — An asset must have a verified PublisherId before it can move to Published; AssetType is immutable after creation; LatestVersionId always references a released AssetVersion; a withdrawn asset cannot be installed.

Domain EventsAssetPublished, AssetCatalogued, AssetReviewed (rating projection), AssetDeprecated, AssetWithdrawn.

RepositoryMarketplaceAssetRepository.

Persistence — Azure SQL / PostgreSQL via NHibernate; marketplace_asset table with a child asset_media; read model cached in Redis and projected into the search index.

AssetVersion

Purpose — An immutable, semantically versioned release of a MarketplaceAsset, the unit of compatibility and installation. Owned by AssetVersionService (Publishing & Versioning).

FieldsAssetVersionId (identity), AssetId, SemanticVersion, Status (PendingQualityScan, PendingSigning, Released, Yanked), PackageId, ReleaseNotes, Signed, Signature, QualityVerdict, CompatibilityProfile, ReleasedAt, TenantId.

EntitiesVersionDependencyDeclaration, ReleaseNote.

Value ObjectsSemanticVersion, QualityVerdict, Signature, CompatibilityProfile.

InvariantsSemanticVersion is unique per asset and immutable once Released; a version cannot be Released without a passing QualityVerdict and a valid Signature; a yanked version remains queryable but cannot be newly installed.

Domain EventsAssetVersionReleased, AssetQualityScanned, AssetVersionYanked.

RepositoryAssetVersionRepository.

Persistence — Azure SQL / PostgreSQL via NHibernate; package bytes live in Azure Blob / Azure Artifacts referenced by PackageId.

AssetPackage

Purpose — The distributable binary artifact for an AssetVersion and its location in the distribution store. Owned by AssetVersionService (Publishing & Versioning).

FieldsAssetPackageId (identity), AssetVersionId, PackageType (Zip, NuGet, ContainerImage), StoreLocation, ContentHash, SizeBytes, Manifest, PublishedAt, TenantId.

EntitiesPackageManifestEntry.

Value ObjectsContentHash, StoreLocation (blob URI / Artifacts feed / ACR ref), PackageManifest.

InvariantsContentHash must match the stored bytes; a package is immutable after publish; StoreLocation is consistent with PackageType (NuGet → Azure Artifacts, ContainerImage → ACR, Zip → Blob).

Domain EventsAssetPackagePublished.

RepositoryAssetPackageRepository.

Persistence — metadata in Azure SQL / PostgreSQL via NHibernate; bytes in Azure Blob (Zip), Azure Artifacts (NuGet), or Azure Container Registry (images). See Storage.

AssetDependency

Purpose — A declared dependency of an asset version on another asset (with a version range), and the resolved graph node used at install time. Owned by DependencyResolutionService (Compatibility & Dependencies).

FieldsAssetDependencyId (identity), AssetVersionId, DependsOnAssetId, VersionRange, Scope (Required, Optional), ResolvedVersion, ResolutionStatus, TenantId.

EntitiesResolvedDependencyNode (within a resolution result).

Value ObjectsVersionRange, DependencyScope, ResolutionStatus.

InvariantsVersionRange must be a valid semantic-version range; resolution must produce a single ResolvedVersion satisfying all ranges or fail with a conflict; optional dependencies may remain unresolved without failing the graph.

Domain EventsDependenciesResolved, DependencyConflictDetected.

RepositoryAssetDependencyRepository.

Persistence — Azure SQL / PostgreSQL via NHibernate; resolved graphs cached in Redis keyed by (assetId, version, targetHash).

AssetCompatibility

Purpose — The verdict of evaluating an asset version against a target environment (runtime, project, installed assets). Owned by AssetCompatibilityService (Compatibility & Dependencies).

FieldsAssetCompatibilityId (identity), AssetVersionId, TargetDescriptor, Verdict (Compatible, Incompatible, CompatibleWithWarnings), Findings, EvaluatedAt, EvaluatorVersion, TenantId.

EntitiesCompatibilityFinding.

Value ObjectsTargetDescriptor (runtime, projectId, installed assets), CompatibilityVerdict, Finding (kind, severity, message).

Invariants — A verdict references an existing released AssetVersion; an Incompatible verdict must carry at least one blocking finding; verdicts are cached and invalidated when the version or evaluator changes.

Domain EventsCompatibilityEvaluated.

RepositoryAssetCompatibilityRepository.

Persistence — Azure SQL / PostgreSQL via NHibernate; verdicts cached in Redis with version-keyed invalidation.

AssetInstallation

Purpose — A record of installing an asset version into a target project/tenant, with provenance, status, and rollback information. Owned by AssetInstallationService (Installation).

FieldsInstallationId (identity), AssetId, AssetVersionId, Target (projectId, environment), Status (Requested, Resolving, Evaluating, LicenseCheck, Applying, Installed, Failed, RolledBack), ResolvedDependencies, LicenseId, RequestedBy, InstalledAt, RollbackToken, TenantId.

EntitiesInstallationStep, InstalledArtifactRef.

Value ObjectsInstallationTarget, InstallationStatus, RollbackToken.

Invariants — Installation cannot reach Applying without a Compatible/CompatibleWithWarnings verdict, resolved required dependencies, and a valid License for paid assets; a failed installation must roll back to a clean state; each (assetId, version, target) has at most one active installation.

Domain EventsAssetInstalled, AssetInstallationFailed.

RepositoryAssetInstallationRepository.

Persistence — Azure SQL / PostgreSQL via NHibernate; saga state persisted alongside the aggregate. See the state machine in Workflows.

Publisher

Purpose — An identity that publishes assets, with verification status, trust tier, and portfolio. Owned by PublisherPortalService (Publisher).

FieldsPublisherId (identity), DisplayName, Kind (Internal, Partner, Community), VerificationStatus (Unverified, Pending, Verified, Suspended), TrustTier (T0–T3), ContactEmail, SigningKeyRef, ReputationScore, TenantId, CreatedAt.

EntitiesPublisherContact, PublisherSigningKey.

Value ObjectsTrustTier, VerificationStatus, ReputationScore, SigningKeyRef.

Invariants — Only a Verified publisher may release signed versions; SigningKeyRef must reference a key in the secret store; a Suspended publisher's assets cannot be installed; trust tier governs whether quality gates can be auto-approved.

Domain EventsPublisherRegistered, PublisherVerified, PublisherSuspended.

RepositoryPublisherRepository.

Persistence — Azure SQL / PostgreSQL via NHibernate; signing keys referenced (not stored) via Azure Key Vault. See Security.

License

Purpose — A tenant's entitlement to install and use an asset under a pricing plan. Owned by LicenseService (Commerce); integrates ConnectSoft.Extensions.Saas.Billing through an anti-corruption layer.

FieldsLicenseId (identity), AssetId, TenantId, PricingPlanId, EntitlementRef, Status (Active, Expired, Revoked), GrantedAt, ExpiresAt, Seats, BillingSubscriptionRef.

EntitiesLicenseSeatAssignment.

Value ObjectsEntitlementRef, LicenseStatus, SeatCount, BillingSubscriptionRef.

Invariants — A license is bound to a single (assetId, tenantId); an Active license requires a valid billing entitlement; seat assignments cannot exceed Seats; an expired or revoked license blocks new installs but does not retroactively uninstall.

Domain EventsLicenseGranted, LicenseRevoked, LicenseExpired.

RepositoryLicenseRepository.

Persistence — Azure SQL / PostgreSQL via NHibernate; entitlement truth federated from billing via the ACL.

PricingPlan

Purpose — The pricing definition attached to an asset (free, flat, per-seat, usage-based). Owned by PricingService (Commerce).

FieldsPricingPlanId (identity), AssetId, Name, Model (Free, Flat, PerSeat, Usage), Currency, Amount, MeteredDimensions, BillingPeriod, Status (Draft, Published, Retired), TenantScope, CreatedAt.

EntitiesPriceTier, MeteredDimension.

Value ObjectsPricingModel, Money, BillingPeriod, MeteredDimension.

Invariants — A published plan is immutable (changes create a new plan version); Usage plans must declare at least one MeteredDimension; Currency and Amount are consistent with Model; only one Published plan is the default per asset.

Domain EventsPricingPlanPublished, PriceQuoted, PricingPlanRetired.

RepositoryPricingPlanRepository.

Persistence — Azure SQL / PostgreSQL via NHibernate; metering dimensions reconciled with ConnectSoft.Extensions.Saas.Metering.

AssetReview

Purpose — A user's rating and review of an asset, feeding ranking and publisher reputation. Owned by ReviewRatingService (Reviews).

FieldsAssetReviewId (identity), AssetId, AssetVersionId, ReviewerId, Rating (1–5), Title, Body, Status (Published, Flagged, Removed), HelpfulCount, CreatedAt, TenantId.

EntitiesReviewVote, ReviewModerationNote.

Value ObjectsRating, ReviewStatus.

Invariants — A reviewer may hold at most one active review per asset; Rating is between 1 and 5; only a reviewer who has installed the asset (verified install) may publish a review; removed reviews are excluded from aggregate rating.

Domain EventsAssetReviewed, RatingAggregated, ReviewFlagged.

RepositoryAssetReviewRepository.

Persistence — Azure SQL / PostgreSQL via NHibernate; aggregate ratings projected onto MarketplaceAsset via AssetReviewed.

Consistency and ownership

Each aggregate is a transactional consistency boundary owned by exactly one service; cross-aggregate consistency is eventual, achieved through domain/integration events. For example, AssetReviewed updates AssetReview transactionally, then projects an aggregate rating onto MarketplaceAsset asynchronously. This keeps publishing, commerce, and installation independently scalable while preserving end-to-end traceability through the event envelope.

Continue to Data Model, Storage, and Workflows.