Skip to content
bitzorcas
中EN

Reference

Industry Extensions packaging, governance, and runtime boundary

A deep guide to the three optional calculator assemblies, dependency graph, hosted governance marker, API-host absence, LegalCalculators consumption, integration wrappers, and authorization, tenancy, privacy, and audit responsibilities.

Last updated

The isolation unit for Industry Extensions is an assembly, not a runtime module instance. Each project references only Domain, except Auction also references Finance. There is no Application/Contracts/Infrastructure split and no service-registration entry point.

1. Physical projects

ProjectDescription claimActual content
FinanceExplicitly selected finance calculatorsFive calculators, one bracket engine, one error catalog
HRExplicitly selected HR calculatorsLeaveCalculator, LeavePolicySchedule, error catalog
AuctionExplicitly selected auction calculatorAuctionCommissionCalculator and error catalog

All target net10.0 with nullable and implicit usings. They have no PackageId, package version, SourceLink, or NuGet pack metadata. “Optional package” currently means selecting a ProjectReference, not consuming an independently versioned and signed commercial NuGet artifact.

2. Dependency direction

declares identitydoes not reference

Framework Domain

IndustryExtensions.Finance

IndustryExtensions.Hr

IndustryExtensions.Auction

LegalCalculators

Platform.Application
governance marker

BitzOrcas.Api

The old principle “general platform code never depends on an industry package” has an intentional exception: LegalCalculators directly references Finance. A genuinely neutral bracket primitive should move to a neutral calculator assembly, or the exception must be recorded.

3. Hosted governance marker

IndustryExtensionsModule lives in loaded BitzOrcas.Platform.Application, not in a calculator assembly. Its comment explicitly says the child assemblies are absent from the API composition graph and that the marker is hosted centrally so governance can discover it.

That creates a state in which the control plane lists IndustryExtensions but the data plane has no calculator assembly. A Registry, Operations view, or Profile catalog must not equate a contribution with runtime readiness.

4. No runtime enablement

There is no AddIndustryExtensions, IModuleServiceConfigurator, Feature key, Permission catalog, health check, or adapter-readiness probe. Once a consumer references an assembly, any code can call static methods directly.

Profile selection is therefore a build convention rather than an auditable runtime policy. Generated projects, license entitlement, features, composition evidence, and policy availability must form one readiness chain.

5. Consumption pattern

Wrap a pure calculator in the owning module
// The wrapper pins an approved policy version for this business use.
var policy = await policyStore.GetPublishedAsync(
jurisdiction,
scenario: "finance.income-tax",
occurredOn,
cancellationToken);
// Static math handles numbers; the wrapper retains identity, tenant, and evidence.
var raw = IncomeTaxCalculator.CalcIndividualIncomeTax(facts.TaxableIncome);
if (raw.IsFailure)
return raw.Error;
// A result ledger makes replay possible through input hash, policy, lines, and rounding.
return await ledger.RecordAsync(
facts.RedactedSnapshot,
policy.Version,
raw.Value,
cancellationToken);

The policy store and ledger are target integration components, not current library APIs. Keep tenant, database, and authentication concerns in an Application wrapper rather than polluting pure numeric functions.

6. Wrapper responsibilities

  • authorize access to payroll, lending, contract, or auction facts;
  • resolve Tenant, Office, Jurisdiction, and taxpayer/employee/contract category;
  • select one published policy covering the occurrence date;
  • validate units, currency, dates, and supporting evidence;
  • call the pure engine and produce a breakdown;
  • persist an immutable result ledger and audit trail;
  • apply data minimization, encryption, retention, and erasure policy;
  • localize stable Error codes through I18n;
  • declare advisory versus formal use and show the correct disclaimer.

7. Stateless does not mean privacy-free

The libraries do not persist or log data, which is a useful boundary. Their inputs can still include income, deductions, loan principal, loss, maternity facts, and employment history. A wrapper can leak them through logs, trace tags, or exception messages.

Observe scenario, policy version, Error code, bounded numeric bucket, and latency—not raw salary, debt, or health-related values. Result ledgers need domain-specific retention rules.

8. Error boundary

Finance and HR catalogs span several first-segment prefixes; Auction fixes Auction. Descriptions are Chinese sentences, while the code is the stable contract.

Keep stable error codes in machine contracts
var result = MortgageCalculator.CalcEqualInstallmentMonthly(
principal,
annualRate,
years);
// Record only code and calculator, never the sensitive raw operands.
if (result.IsFailure)
{
diagnostics.Count(result.Error.Code, calculator: "mortgage");
return Errors.Localize(result.Error.Code, languageContext.CurrentLanguage);
}
// The owner decides whether and how the successful estimate is persisted.
return result.Value;

Some helpers return bare decimal or int and cannot express invalid input. Public wrappers must validate before calling them.

9. AOT claim

XML comments repeatedly claim zero reflection and Native AOT safety. The implementation does use basic numeric types, spans, lists, dates, and ValueObject without dynamic reflection. That is not evidence that a Native AOT host has published, trimmed, and executed these public APIs.

GA needs AOT publish, trim-warning, call-smoke, and public-API compatibility evidence, especially when policy deserialization is later added.

10. Versioning and distribution

A commercial optional package needs SemVer, compatibility policy, signing, SBOM, entitlement, SourceLink, symbols, scanning, and withdrawal policy. Rule version should remain distinct from engine binary version, so old policies can stay available for historical replay.

Replacing a static array in a DLL destroys historical reproducibility unless every previous binary remains deployable and identified on each result.

11. Runtime readiness probe

A target probe asks whether the assembly is loaded, wrapper is registered, license/feature allows use, a current published policy exists, the source is not expired, golden cases pass, and ledger/outbox is ready.

Assembly

Application wrapper

License + feature

Published policy for date

Golden tests + source review

Ready

A governance contribution alone must not turn readiness green.

12. Test boundary

Pure libraries need unit, property, and golden tests. Wrappers separately need HTTP authorization, tenancy, policy resolution, audit, and idempotency tests. Distribution needs NuGet, AOT, template, and Profile tests. Do not add a database merely to call a pure formula.

The current test tree contains no references to any calculator method.

13. Inspection commands

Terminal window
# Project references and descriptions.
rg -n "ProjectReference|Description" src/Platform/IndustryExtensions -g '*.csproj'
rg -n "IndustryExtensions" src/Hosts -g '*.cs' -g '*.csproj'
# DI, permissions, features, endpoints, and tests should currently have no matches.
rg -n "AddIndustryExtensions|PermissionCatalog|Feature|GenerateEndpoint|\[Fact\]" \
src/Platform/IndustryExtensions tests -g '*.cs'

Industry Extensions overview · Finance calculators · Auction and GA

100%

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