Portal Community

Template Versioning Strategy

Because template catalogue codes are embedded in workflow definitions, expression directives, and external API calls, changing or removing a template code is a breaking change. Use the following strategy to evolve templates safely while maintaining backward compatibility:

// Template JSON with version tracking
{
  "name": "Payroll Department Summary v2",
  "version": 2,
  "isTemplate": true,
  "sql": "SELECT d.ID, d.DeptName, d.CostCentre FROM dbo.Departments d WHERE d.TenantID = @TenantID",
  "collectionName": "departments",
  "children": [ ... ]
}

Adding New SQL Engines

The Multi-Query system is engine-agnostic. The sqlserver engine is the built-in implementation, but additional engines can be registered by implementing ISqlDeriveEngine and registering the implementation in the DI container.

Implement ISqlDeriveEngine

Create a class that implements ISqlDeriveEngine. The interface requires query execution logic appropriate for the target database platform (PostgreSQL, MySQL, SQLite, etc.) and a stable engine name string.

public class PostgreSqlDeriveEngine : ISqlDeriveEngine
{
    public string EngineName => "postgresql";

    public Task<MultiQueryResult> ExecuteAsync(
        MultiQueryTemplate template,
        MultiQueryExecutionContext context)
    {
        // Implement PostgreSQL-specific query tree execution
    }
}
Return a unique EngineName

The EngineName property must return a stable, lowercase string that is unique across all registered engines. This string becomes the engine segment in the directive token ({{multi-query:postgresql.CODE}}) and in the REST path. Once published, changing this string is a breaking change.

Register in the DI container

Register the new engine as part of the IEnumerable<ISqlDeriveEngine> collection in the service registration. The platform resolves engines via enumeration — no central registry or switch statement needs to be modified.

// In your module registration or Startup.cs
services.AddSingleton<ISqlDeriveEngine, SqlServerDeriveEngine>();
services.AddSingleton<ISqlDeriveEngine, PostgreSqlDeriveEngine>();
Author templates and use the new engine name

Store template JSON in dbo.Shared_Configurations as usual. Reference the new engine in directives and REST calls using the EngineName you defined:

{{multi-query:postgresql.ORG_CHART_SUMMARY}}

GET /api/v1/expressions/multiquery/ORG_CHART_SUMMARY
    (engine resolved from template metadata or directive)

Engine Resolution

When a directive or endpoint is evaluated, MultiQueryExpressionDirectiveService resolves the target engine using a simple linear enumeration:

// Engine resolution — simplified illustration
var engine = _engines.FirstOrDefault(e =>
    string.Equals(e.EngineName, engineName, StringComparison.OrdinalIgnoreCase));

if (engine is null)
{
    _logger.LogWarning(
        "Multi-Query directive: no engine registered for '{EngineName}'. Returning empty string.",
        engineName);
    return string.Empty;
}

If no registered engine matches the name parsed from the directive, the service returns an empty string and writes a warning to the platform log. No exception propagates to the caller. This makes it safe to deploy a new engine incrementally — templates referencing the new engine name will silently return empty until the engine implementation is registered.

Schema Evolution

Breaking schema changes are detected at execution time, not at template store time
When an underlying SQL table is altered — a column is renamed, dropped, or has its data type changed — the template JSON stored in dbo.Shared_Configurations is not validated against the live schema. The template continues to store successfully. The failure occurs at execution time when SQL Server rejects the query. This produces an HTTP 500 Internal Server Error on the API and an empty string from the expression directive. Review all active templates that reference affected tables before applying schema changes, and update the SQL in the template JSON as part of the schema migration transaction.

Backward Compatibility Rules

The following rules define the Multi-Query compatibility contract. Violating them constitutes a breaking change and requires a new template code (version suffix) to avoid disrupting existing callers.

Future Directions

The following extensions are under consideration for future releases of the Multi-Query engine:

PostgreSQL Engine

A first-class ISqlDeriveEngine implementation for PostgreSQL, enabling multi-level query trees over PostgreSQL databases alongside the existing SQL Server engine. Would use Npgsql and support the same @parent token syntax.

Template Composition

Allow a template to inherit or embed another template using a "$ref": "PARENT_TEMPLATE_CODE" directive in the JSON. Composed templates would be resolved and merged at load time, reducing duplication across similar reporting structures.

AI-Generated Templates

A natural-language-to-template authoring assistant that accepts a description of the desired data hierarchy and generates a valid Multi-Query JSON template. Draft templates would be presented for human review before being stored in dbo.Shared_Configurations.

Template Test Runner

An administrative tool for executing a template against a known fixture dataset and asserting the output matches an expected JSON snapshot. Enables regression testing of templates before promotion to production, preventing silent schema drift failures.

Changelog

Version Date Changes
v1.0 2026-05-28 Initial release. SQL Server engine (SqlServerDeriveEngine), JSON and HTML output formats, {{multi-query:}} expression directive, Execute / RunStoredScript / RunDirectScript endpoints, @parent token support, [AuthorizeTenantAdminAttribute] enforcement, rate limiting via ApiLimit, BizFirst Observe metrics integration.
Engine name is part of the directive syntax
Because the engine name is encoded directly in the {{multi-query:engineName.CODE}} token, you can introduce an entirely new engine without modifying or invalidating any existing sqlserver templates. Existing templates continue to resolve through SqlServerDeriveEngine unchanged. New templates that target the new engine simply use its EngineName in the directive.