Separate feature contracts from host settings#
A Matrix module represents an application capability. It can contribute endpoints, pages, navigation or a combination; it need not represent a database context. Keep stable feature IDs and transport contracts in a neutral project, with optional ASP.NET Core and Blazor projects for execution and presentation.
The documented project family is:
Modules/<Feature>/
├── <Company>.Matrix.Modules.<Feature>
├── <Company>.Matrix.Modules.<Feature>.AspNetCore
└── <Company>.Matrix.Modules.<Feature>.BlazorThe neutral project references SK.Matrix.Domain. API and Web halves reference their respective Shell packages. A reusable module should reference CRUD only when it genuinely extends CRUD. Deployment URLs, secrets and identity settings remain in the consuming host.
Register the API and Web halves#
Implement IMatrixApiModule to register services, contribute catalog definitions and map endpoints. Define stable resources/actions and enforce their permissions at endpoint execution. The documented endpoint guard is RequireMatrixPermission; hiding the associated menu never replaces it.
Implement IMatrixShellModule for normal Razor pages, module stylesheets and typed API clients. The API target and Web page target IDs must match exactly. AddApiClient<TClient>() uses the host-configured API address and token-forwarding pipeline. It does not move domain authorization into the browser.
The host composition pattern is deliberately small:
var matrixApi = builder.AddMatrixApi(options => { /* identity */ });
matrixApi.AddModule<ReportsApiModule>();
var matrixWeb = builder.AddMatrixWeb(options => { /* branding */ });
matrixWeb.AddModule<ReportsWebModule>();This is the shape from the authoring guide, not a complete standalone application. The module types, permissions and actual host options belong to the Consumer implementation. A single small page may use the documented symmetric API/Web AddPage path instead of wrapper module classes.
Configured module instances can accept typed constructor options. Snapshot mutable options and use ordinary scoped DI for domain services. Module instances are startup definitions: never retain a principal, DbContext or other request-scoped state inside them. One implementation registers once per host; multiple views belong in that module's options.
Keep code and JSON responsibilities explicit#
| Concern | Supported owner/input |
|---|---|
| Modules, routes, targets, permissions and business handlers | Trusted C# composition |
| CRUD profiles, commands and reactive rules | Typed code |
| Existing catalog labels, grouping, order and visibility | Validated catalog presentation sources, including constrained JSON |
| Supported cultures and declared text overrides | Localization configuration |
| Addresses for code-declared external destinations | Validated host deployment configuration |
| Database/service secrets | Protected Consumer configuration |
For example, this documented fragment renames an existing context navigation group. It does not create a context or grant access:
{ "Matrix": { "Catalog": { "Navigation": [
{ "Id": "crud/workspace", "DisplayName": "Workspace", "Order": 10 }
] } } }Do not infer a full CRUD JSON language from presentation overrides. There is no general runtime configuration database, live JSON reload or JSON-to-C# generator in this contract.
Validate lifecycle and precedence#
Catalog and localization definitions, registered profiles and their configuration are startup snapshots. Restart after changing them. Catalog sources compose in order before authorized projection; higher-order sources can override earlier presentation, but presentation cannot override denied permissions. Formatting, localization and normal .NET host configuration each have their own precedence rules.
Unknown IDs, duplicate registrations and invalid references fail at their owning validation boundary. Some API/Web mismatches appear only after Web receives authorized metadata, so deploy compatible definitions together. Module contract-version diagnostics are advisory, not automatic version negotiation.
Finish by testing a direct denied request, a valid page route, localized labels, custom assets and the actual typed client. Continue with CRUD and query experiences or reports and dashboards according to the feature's data model.