Skip to content
bitzorcas
中EN

Concept

Release Management

Owns tenant release notes plus global client-version catalogs, deterministic staged rollout, force-update floors, and upgrade checks.

Last updated

Owns tenant release notes plus global client-version catalogs, deterministic staged rollout, force-update floors, and upgrade checks.

Primary path at a glance

Client submits platform and version

Parse supported semantic version

Select published candidate

Apply stable rollout hash

Return optional or forced upgrade

The coral node marks the module’s central decision or state boundary.

Capability boundary

  • Draft, published, and archived release-note lifecycle
  • Draft, published, and deprecated app-version lifecycle
  • Semantic-version comparison, stable rollout, and force-update decisions

The module explicitly does not own the following: ReleaseManagement publishes metadata and upgrade decisions. It does not build artifacts, upload binaries, operate an app store, or replace package distribution and deployment pipelines.

Code map

ProjectPrimary responsibility
BitzOrcas.Platform.ReleaseManagement.ContractsPublic contracts, ports, DTOs, and events
BitzOrcas.Platform.ReleaseManagement.ApplicationCommands, queries, handlers, and application policy
BitzOrcas.Platform.ReleaseManagement.InfrastructurePersistence, connectors, and framework adapters

Source location: src/Platform/ReleaseManagement

Core use cases

  • CreateReleaseNoteCommand
  • PublishReleaseNoteCommand
  • ListPublicReleaseNotesQuery
  • CreateAppVersionCommand
  • PublishAppVersionCommand
  • CheckUpgradeQuery

When reading a use case, start with its request contract and permission declaration, follow how the handler coordinates an aggregate or port, then inspect post-commit events, cache changes, and audit.

Key models and contracts

Key typeWhat to inspect
ReleaseNoteDtoTenant release-note projection
AppVersionDtoGlobal client-version projection
IRolloutEvaluatorDeterministic rollout decision seam
UpgradeCheckResultClient upgrade decision

Use-case boundary example

FieldValue
Representative contractCheckUpgradeQuery
ResultResult<UpgradeCheckResult>
Review focusParse the current version, select only published candidates, bind rollout to a stable client identifier, and evaluate the force-update floor independently of optional rollout.

Pseudocode

CheckUpgradeQuery pseudocode
// ① Reject immediately with a stable validation error when parsing fails.
if (!SemanticVersion.TryParse(query.CurrentVersion, out var current))
return Result.Failure<UpgradeCheckResult>(ReleaseManagementErrors.AppVersionVersionInvalid);
// ② Candidates filter by platform and channel over published rows, sorted high to low.
var published = await store.ListPublishedByPlatformAsync(
query.Platform, query.Channel, cancellationToken);
if (published.Count == 0) return NoUpdate;
var latest = published[0];
// ③ Force-update floors and optional rollout stay independent decisions.
var forceUpdate = !string.IsNullOrWhiteSpace(latest.MinForceUpdateVersion)
&& SemanticVersion.TryParse(latest.MinForceUpdateVersion, out var minRequired)
&& current.IsLowerThan(minRequired);
var rolloutHit = evaluator.IsHit(request.ActorKey, latest.RolloutPercentage);
// ④ A stable ActorKey hash pins each device or user to one rollout cohort.
return forceUpdate || rolloutHit
? Result.Success(new UpgradeCheckResult(forceUpdate, latest.Version,
latest.BuildNumber, rolloutHit, latest.DownloadUrl, latest.ReleaseNoteKey))
: NoUpdate;

NoUpdate is not a static factory on the type; it is a private success result held inside CheckUpgradeQueryHandler representing the neutral “nothing to install” outcome.

Integration relationships

Release notes are tenant-scoped, while AppVersionCatalogRecord is global. Upgrade checks use a stable client identifier for percentage rollout and return metadata and an HTTPS download URL; artifact integrity and hosting remain external concerns.

Cross-module collaboration follows these directions:

  • Callers depend on this module’s public Contracts or a narrow port.
  • State changes propagate through versioned integration events.
  • Database, messaging, cache, file, and vendor SDK details stay in Infrastructure.
  • The composition root registers fail-closed defaults before production adapters.

Security, tenancy, and privacy

Management routes require release permissions and the default-off release.manage feature. Public release-note and upgrade-check projections exclude draft records. Production download URLs require HTTPS, with local/private exceptions reserved for restricted development environments.

Every tenant-scoped write persists TenantId, and list/detail paths share one authorization boundary. Logs, events, and audit retain only the minimum fields needed for diagnosis.

Failure semantics, idempotency, and observability

Rollout is deterministic for the same version and client identifier, so clients do not oscillate between cohorts. Version parsing accepts v-prefixed three-part versions and date-style releases but rejects prerelease/build suffixes.

SituationRequired behavior
Validation, not found, conflict, or forbiddenReturn typed Result/Error data and let the shared mapper produce Problem Details
Adapter or critical state unavailableFail closed for security/high-value work; mark read-only degradation explicitly
Duplicate request or eventUse a stable idempotency key with a database uniqueness constraint as the final guard
External timeoutPropagate cancellation, record redacted diagnostics, and retry only safe operations with bounds

Test matrix

#Required scenarioSuggested level
1semantic-version accepted and rejected formsunit + integration
2zero, full, and stable partial rolloutintegration/contract
3force-update floor and deprecated versionsintegration/contract
4tenant release notes versus global app versionsintegration/contract

Keep one universal red line under test: no identifier, cache key, event, or query condition from tenant A may let tenant B read or mutate data.

Source navigation and change checks

Run from the BitzOrcasVNext repository root:

Terminal window
# List public types and confirm that new contracts belong to the module boundary.
rg -n "^public (sealed |abstract |static |partial )*(record|class|interface|enum)" src/Platform/ReleaseManagement -g '*.cs'
# Inspect cross-layer references; callers must not depend on another module's Infrastructure.
rg -n "ProjectReference|PackageReference" src/Platform/ReleaseManagement -g '*.csproj'
# Find unfinished or elided implementation; expect no matches.
rg -n "TODO|FIXME|// \.\.\." src/Platform/ReleaseManagement -g '*.cs'

Extension and release checklist

  1. Is the new capability expressed through explicit Contracts, permissions, and a feature key?
  2. Can persistence, connectors, and providers be replaced through narrow adapters?
  3. Does production still resolve a Null/Unavailable default, and could health checks report a false green?
  4. Is there evidence for transaction, event, cache-invalidation, audit, and background-job failure/retry behavior?
  5. Did bilingual docs, diagrams, contract tests, and runbooks change with the code?

Back to module catalog · Module dependency diagram · Add a module guide

100%

Scroll or use controls to zoom · drag when enlarged · double-click for 100% / 200%