Skip to content

Guide · Add Canonical and Composable Reports ​

This guide shows how to add two different report types in a vertical solution:

  1. a Canonical report;
  2. a Composable report.

The point is not that one replaces the other. The point is knowing when to use which.

Example 1: Canonical report — Accounts Receivable Aging ​

Aging has stable business meaning and often needs dedicated semantics such as:

  • aging buckets;
  • as-of date handling;
  • open-item logic;
  • totals by bucket and customer.

That makes it a strong candidate for a canonical report.

What to create ​

text
NGB.Trade.Definitions/
  Reports/
    TrdReceivablesAgingReportDefinition.cs

NGB.Trade.Runtime/
  Reporting/
    ReceivablesAgingCanonicalReportExecutor.cs

Definition sketch ​

csharp
public sealed class TrdReceivablesAgingReportDefinition : ReportDefinition
{
    public const string Code = "trd.receivables.aging";

    public override string ReportCode => Code;
    public override string DisplayName => "Accounts Receivable Aging";
    public override ReportExecutionMode ExecutionMode => ReportExecutionMode.Canonical;
}

Executor responsibilities ​

The executor should:

  • read open receivable state;
  • calculate bucket assignment;
  • shape totals;
  • return typed cell actions for customer and document drilldowns.

Example 2: Composable report — Sales Analysis ​

Sales analysis is a better candidate for a composable report because users may want to vary:

  • fields;
  • grouping;
  • sorting;
  • time grain.

Typical groupings:

  • customer;
  • item;
  • month;
  • warehouse;
  • sales rep.

What to create ​

text
NGB.Trade.Definitions/
  Reports/
    TrdSalesAnalysisReportDefinition.cs

Definition sketch ​

csharp
public sealed class TrdSalesAnalysisReportDefinition : ReportDefinition
{
    public const string Code = "trd.sales.analysis";

    public override string ReportCode => Code;
    public override string DisplayName => "Sales Analysis";
    public override ReportExecutionMode ExecutionMode => ReportExecutionMode.Composable;
}

The composable definition should expose:

  • filters;
  • available fields;
  • allowed grouping;
  • sorting metadata;
  • time-grain support;
  • typed cell actions.

Report design checklist ​

Before creating a report, answer:

  1. is the meaning fixed or exploratory?
  2. do we need custom balance or running-total semantics?
  3. will users change layout often?
  4. is cursor paging needed?
  5. which cells should drill into documents or reports?

Cell actions ​

Reports in NGB should not return dead strings where navigation matters.

Examples:

  • customer cell → open customer catalog entity;
  • document cell → open document;
  • account cell → open Account Card;
  • property cell → open property.

This keeps reporting integrated with the rest of the platform.

UI expectations ​

A production-ready report should return UI-friendly output that already understands:

  • labels;
  • totals;
  • grouping;
  • sorting metadata;
  • cell actions;
  • export shape.

When to prefer canonical ​

Prefer canonical when:

  • accounting semantics are special;
  • the report has a standard meaning;
  • the row model is not just “select fields and group.”

When to prefer composable ​

Prefer composable when:

  • the dataset is reusable;
  • layout flexibility is important;
  • one engine can serve many report shapes.

Final checklist ​

  • report code and label are stable;
  • filters and fields are user-friendly;
  • drilldowns are wired;
  • performance path matches report semantics;
  • the report is tested with realistic data.

A good reporting subsystem does not force everything into one template. NGB is strongest when canonical and composable reports are used deliberately.

Released under the Apache License 2.0.