This tutorial creates Acme.ServiceDesk: multi-tenant, Aspire-orchestrated, SqlSugar, with no optional Platform module or industry extension. Output includes API, ServiceDefaults, AppHost, Business Starter Contracts/Application, Unit Tests, Architecture Tests, and composition evidence.
[!NOTE] For new projects, prefer the bitz scaffolder:
bitz newoffers scenario presets, orthogonal dimension composition, SqlSugar and EF Core persistence ORM adapters, andbitz add/remove modulefor existing solutions. This page keeps thedotnet new bitzorcas-hosttemplate-matrix tutorial; both channels share the same verified consumer baseline, and retirement conditions are tracked in0004-template-upgrade-map.json.
The neutral Profile keeps the first-success path focused. Authorization, MasterData, and industry variants add their own packages, configuration, and tests on the same base topology and should be adopted separately after this path works.
Target topology
AppHost waits for the database, then for a successful one-shot schema-migrator, and only then starts API. A schema failure must keep API stopped.
1. Check the environment and Feed
dotnet --versiondotnet nuget list sourcetest -n "$BITZORCAS_COMMERCIAL_FEED_URL"SDK resolution should follow the repository’s 10.0.302 / latestFeature policy. Use the source key BitzOrcasCommercial; supply credentials through a Credential Provider or CI secret. See Prerequisites.
2. Install and inspect the contract
dotnet new install BitzOrcas.Modern.Templates@1.0.0-alpha1dotnet new bitzorcas-host --helpHelp should expose ProfileChoice and RuntimeAdapter, including default-business-multi and sqlsugar. If other business selectors appear, verify the installed version before generation.
3. Generate into an empty directory
# Select Multi Business, Aspire topology, and the SqlSugar provider in one command.dotnet new bitzorcas-host \ --name Acme.ServiceDesk \ --output ./Acme.ServiceDesk \ --ProfileChoice default-business-multi \ --RuntimeAdapter sqlsugar
cd Acme.ServiceDeskCommand completion means native template files have been generated; there is no later materialization stage. Customer output must contain no tools/, Python file, or foundation Framework/Platform source.
The terminal below shows steps 2–4 as actually run (install confirmation, generation, manifest/plan verification; CLI output stays in the tool’s localized form):
4. Verify resolved composition
# The first group proves that caller selections resolved correctly.# The latter fields prove optional extension and physical project closure.jq '{ profileChoice, profileId, runtimeAdapter, tenancy, deployment, platformModule, industryExtension, hosts, projects}' composition-manifest.jsonExpected values are:
| Field | Expected |
|---|---|
profileChoice | default-business-multi |
profileId | default-business |
runtimeAdapter | sqlsugar |
tenancy | multi |
deployment | aspire |
platformModule / industryExtension | none / none |
hosts | api, service-defaults, apphost |
Check the plan and physical output:
# Plan proves which projects and entries were selected.jq '{selectedProjects, selectedEndpoints, selectedJobs}' \ composition-plan.json# Physical assertions prove Starter/AppHost exist and maintenance tools did not leak.test -f src/Modules/Business/Starter/Acme.ServiceDesk.Modules.Business.Starter.Contracts/WorkItem.cstest -f src/Hosts/Acme.ServiceDesk.AppHost/Program.cstest ! -d tools5. Restore commercial packages
dotnet restore Acme.ServiceDesk.slnxfind . -name packages.lock.json -printFirst restore creates lock files. Review package IDs, versions, and sources before committing. Stable failure boundaries:
BITZFEED001: Feed URL not injected;401/403or authentication-relatedNU1301: credential missing, expired, or revoked;NU1101/NU1102: entitlement or channel does not cover the package;NU3000: signature or certificate policy failed;- locked mode: requested closure differs from reviewed locks.
Never append tokens to a Feed URL, command argument, or diagnostic log.
6. Build and test
# Release Build consumes the already-restored locked closure.dotnet build Acme.ServiceDesk.slnx \ --configuration Release \ --no-restore
# Full-solution tests include both Unit and Architecture gates.dotnet test Acme.ServiceDesk.slnx \ --configuration Release \ --no-build \ --no-restoreGenerated tests cover at least:
- Profile, license-feature, and selected-ORM closure;
- Starter Aggregate title and tenant invariants;
- schema adoption;
- no copied Framework/Platform source;
- Application has no ORM or Host reference;
- Handler, Endpoint, Aggregate, and owner boundaries remain local.
Passing establishes a generated baseline. It does not prove later business rules, permissions, or production configuration.
7. Configure AppHost parameters
AppHost injects two values into the long-running API:
# WorkerId is unique per instance within one data center.dotnet user-secrets \ --project src/Hosts/Acme.ServiceDesk.AppHost \ set "Parameters:persistence-worker-id" "1"
# DataCenterId is deployment-assigned and must not be randomized per instance.dotnet user-secrets \ --project src/Hosts/Acme.ServiceDesk.AppHost \ set "Parameters:persistence-data-center-id" "0"WorkerId is 1..31 and unique per concurrent instance in a data center. DataCenterId is 0..31 and allocated per deployment. Missing or invalid values fail startup with Host.Persistence.Identity.Invalid.
Development includes a local JWT example. Production supplies Authentication:Jwt:Issuer, Audience, and a SigningKey of at least 32 UTF-8 bytes through a secret store.
8. Run Aspire
dotnet run --project \ src/Hosts/Acme.ServiceDesk.AppHost/Acme.ServiceDesk.AppHost.csprojIn the Aspire Dashboard, verify:
sqlandPrimaryDatabasebecome ready;schema-migratorexits successfully after--migrate-schema apply;apistarts afterward;- API receives
ConnectionStrings:PrimaryDatabaseand both Persistence values.
AppHost is development orchestration; it does not change the API configuration contract. Production supplies the same keys and runs a controlled schema runbook.
9. Verify health and OpenAPI
Obtain the API URL from the Dashboard:
curl --fail http://localhost:<api-port>/health/livecurl --fail http://localhost:<api-port>/health/readycurl --fail http://localhost:<api-port>/openapi/v1.json| Path | Current meaning |
|---|---|
/health/live | process liveness |
/health/ready | base runtime dependencies; License absence alone does not fail it |
/health/license | Runtime License readiness |
/scalar/v1 | interactive documentation enabled by default in Development |
The template registers JWT Bearer only and has no login page. Scalar requires a token from the customer’s authentication entry.
10. Inspect Business Starter
Starter is not an empty placeholder. It includes a tenant WorkItem aggregate, CreateWorkItemCommand, handler, generated endpoint, unified repository, and stable errors:
rg -n 'WorkItem|CreateWorkItem|BusinessStarterErrors|GenerateEndpoint' \ src/Modules/Business/StarterWorkItem is both the domain and persistence fact model. Metadata drives ORM generation; there is no separate Entity, Mapper, DataPort, or Infrastructure project. Replace example semantics while retaining the vertical-slice shape.
11. Schema operations
Multi AppHost already applies schema during development startup. Production still records the command and exit evidence separately:
dotnet run --project \ src/Hosts/Acme.ServiceDesk.Api/Acme.ServiceDesk.Api.csproj \ -- --migrate-schema applySchema mode loads only ConnectionStrings:PrimaryDatabase; it does not require JWT, tenant, License, or ServiceDefaults. Invalid action returns 2, missing database returns 3, and execution failure returns 1.
12. License and production boundary
Development defaults Licensing:Runtime:Enabled=false. Enabling production licensing requires Product, Version, Environment, TenancyMode, Deployment identity, Cache path, and at least one TrustedPublicKey. Missing values fail as Host.Licensing.Configuration.Invalid.
After template completion, add:
- real aggregates, permission catalogs, and tenant/data-scope tests;
- production JWT issuance and key rotation;
- Runtime License acquisition, cache, revocation, and offline policy;
- schema backup, rollback, exclusion, and production approval;
- proxy, OpenAPI access, monitoring, and alerting;
- frontend, deployment artifacts, and data migration.
Completion criteria
- manifest Profile/ORM exactly match the command;
- output has no maintenance scripts or foundation source copies;
- commercial package locks are reviewed;
- Release Build, Unit Tests, and Architecture Tests pass;
- AppHost orders Database → Migrator → API;
- live, ready, and license health meanings stay separate;
- Starter unified aggregate/repository/Endpoint paths are located;
- secrets, Feed tokens, and License private keys remain outside the repository;
- unfinished production work is captured in backlog and runbooks.