XNCF Extension Contracts and Boundaries
This page is for developers building business modules from the NCF Template or XncfBuilder template. It describes supported extension points without explaining the framework's internal scanning, reflection, or execution code.
Choose the right documentation layer
| Goal | Start here |
|---|---|
| Create, extend, or publish an XNCF module from a Template | This page and the Template-Based Development section |
| Debug why the framework scans, registers, or executes an interface | NcfPackageSources Source Guide |
| Change NCF libraries, synchronize templates, or contribute to official repositories | Project Relationships, Synchronization, and Release |
Most business modules need only the first layer. Framework internals are not a prerequisite.
Required module contract
Every XNCF module needs a register class marked with [XncfRegister], derived from XncfRegisterBase, and implementing IXncfRegister. In normal development, keep the generated Register.cs and customize these members:
| Member or method | Extension contract |
|---|---|
Name | Stable unique module name, normally the full namespace |
Uid | Globally unique and immutable after release; do not regenerate it during upgrades |
Version | Module install/upgrade lifecycle version, not the NuGet package version |
MenuName, Icon, Description | Admin display metadata, optionally backed by localized resources |
InstallOrUpdateAsync() | Initialize data, migrate the database, or run compatible upgrades |
UninstallAsync() | Define removal behavior and an explicit production data-retention policy |
AddXncfModule() | Register module dependencies, mappings, and application services |
UseXncfModule() | Mount static files, middleware, and other runtime behavior |
Do not add the legacy IXncfFunction or Functions list found in old guides. The current Function entry point is an AppServiceBase method.
Function contract
An executable Function normally consists of:
- an application service derived from
AppServiceBase; - an executable method marked with
[FunctionRender(...)]; and - a request model derived from
FunctionAppRequestBase.
Request models can use DataAnnotations, LocalizedDescription, and FunctionParameterUi for validation, localization, and input controls. Use the current NCF public response models. Prefer the pattern generated by the matching template version instead of depending on the internal scanner implementation.
Optional extension interfaces
Not every module needs these capabilities:
| Requirement | Extension point | Typical template location |
|---|---|---|
| Module database and migrations | IXncfDatabase | Register.Database.cs, Domain/Migrations |
| Admin Razor Area | IAreaRegister | Register.Area.cs, Areas/Admin |
| Razor runtime compilation | IXncfRazorRuntimeCompilation | Implement only when runtime Razor compilation is required |
| Module middleware | IXncfMiddleware | Implement on the register class as needed |
| Long-running background work | IHostedService / BackgroundService / IXncfThread | Choose according to the task's host lifecycle management |
| Tenant-aware background work | IBackgroundTenantScopeFactory | Reuse initialized tenant scopes in hosted tasks |
| MCP server | EnableMcpServer => true plus MCP Tool attributes | Register class and Tool classes |
Keep generated capabilities that the business needs; do not implement every interface merely for structural completeness. Database uninstall behavior, background threads, and public MCP endpoints require explicit data-safety, shutdown, authentication, and audit decisions.
Service and Data-Access Boundaries
- Derive Services from
ServiceBase<TEntity>, inject an existingIRepositoryBase<TEntity>, and pass it to the base constructor. Services own business rules, transactions, and cross-service orchestration. - Reuse ServiceBase methods for ordinary CRUD; use the existing
RepositoryBase.GeAll(...)for composed queries and projections. Do not add module repositories merely to wrap ordinary queries. - Ordinary business code does not resolve DbContext directly or replace framework tenant/soft-delete constraints with
IgnoreQueryFilters()or handwrittenTenantId/!Flagpredicates. - Host-wide background tasks inject
IBackgroundTenantScopeFactoryand resolve Services in its initialized, independent scopes. Do not reflect over the tenant cache or repeat tenant enablement/identification. Retain only task metadata outside the scope; the task still owns cancellation and resource cleanup.
For complete examples and constraints, see Multi-Tenant Configuration and Background Work, the Service Guide, and the Repository Guide.
What Template developers do not need first
These are source-analysis topics, not prerequisites for a business module:
- how
StartNcfEngine()scans assemblies and orders modules; - how reflection populates
FunctionRenderCollection; - how the XNCF base class maps MCP routes;
- internal multi-database, repository-base, and runtime-composition details;
- synchronization between the simulated site, NCF repository, and NuGet templates.
When you need to debug or change those behaviors, continue to the NcfPackageSources Source Guide. Otherwise, use the current generated code and public contracts as the authority.
Recommended order
- Create your first XNCF module
- Current XNCF template structure
- Develop an XNCF module
- Create a minimal XNCF module manually (only when you need its minimum composition)