Define the business question before its presentation#
Reporting is an optional module family for typed read queries. It does not require CRUD, EF or a Matrix database. A report defines parameter, row and totals types, a stable identity/version, grouping grain, allowed ordering and reviewed measure semantics. The Consumer implements its SQL or service query.
Choose query-backed CRUD instead when each result is a worklist row for one authoritative entity. Reporting can return grouped or aggregated results whose identity is independent of an editable record.
Compose a report#
- Define immutable parameter, row and totals records and a
ReportDefinition<TParameters, TRow, TTotals>. - Declare safe scalar columns, stable row/group identity, units, allowed sorts, validation and optional normalization.
- Implement
IReportQueryHandler<TParameters, TRow, TTotals>with ordinary scoped DI. - Register the definition and handler through
ReportingApiModule. - Register the page through
ReportingWebModuleand renderReportViewwith typed parameters, totals and optional details content.
Fields and review context remain ordinary Consumer Razor. The module supplies shared loading, empty, paging, error and freshness behavior. Trusted C# owns definitions and handlers; there is no executable report JSON engine.
| Boundary | Required behavior |
|---|---|
| Authorization | Apply tenant/record policy before counts, aggregation, paging and drill resolution |
| Query construction | Bind values, allowlist identifiers and include a stable ordering tie-breaker |
| Transport | Return only safe DTO properties; hidden display columns do not remove DTO fields from API serialization |
| Calculation | Define grain, currency/units, complete filtered totals and join fan-out behavior |
| Resource limits | Bound source work and honor cancellation, not only displayed page size |
The documented Reporting defaults are 50 rows per page, 1,000 export rows, 2 MB CSV and a 10-second query budget. Definitions may choose supported bounds. These limits differ from query-backed CRUD export limits and do not prove workload capacity.
Export and navigate through authorized boundaries#
An export re-runs the normalized, authorized query from page one and requires both view and export permissions. It returns all matching rows within the declared budget or fails; it must not silently truncate. Large asynchronous exports require an application-owned durable operation and protected download.
Registered ReportDrill definitions identify a permitted local destination and trusted key encoder. The source re-resolves the authorized row; the destination reauthorizes its actual record or query. Sensitive context should not be placed in route values.
Browser Print/PDF shares export authorization and bounds. It renders a dedicated printable document and relies on the browser's Save as PDF workflow. It is not a server PDF service or unattended export facility. Validate the saved output where the application relies on it.
Compose a fixed dashboard#
SK.Matrix.Modules.Dashboards.Blazor is a UI layer over Reporting, not another query server. Register the dashboard page and ensure every report it uses is independently registered and authorized on the API.
Create one DashboardReport<TParameters, TRow, TTotals> binding per distinct query in the mounted page. Widgets sharing the exact binding share its request and result; independent bindings can fail and refresh independently. Use metrics, charts or trusted Razor tables. Chart inputs are bounded and accompanied by a data table.
Bindings belong to the page, never a singleton or cross-user cache. Dispose them on navigation or identity replacement. The documented refresh interval defaults to 30 seconds within a 5–300 second range; it limits manual refresh and retry. It does not enable automatic polling or replace API rate limits.
Optional layout preferences reorder or hide trusted widget IDs. They do not create queries, components or permissions. Preserve each source's freshness when some widgets fail. Use complete server totals for cards; do not calculate a portfolio total from a displayed page or sum incompatible currencies.
Verify consistency and release scope#
Summary, details, drills and exports are separate reads unless the application implements explicit snapshot semantics. Timestamps describe generation, not a transaction snapshot. Reporting audit delivery can withhold a result on failure, but the default sink stores nothing.
Adopt compatible API/Web packages and verify their matching kit. Historical source-only wording in the feature guides must be reconciled with publication records. Continue with audit and recovery for retention and operational choices.