26.1 Three Constructs
ADL provides three declarative analytics constructs, each serving a different purpose:
| Construct | Purpose | Output | Think of It As |
|---|---|---|---|
| View | Named, parameterized query with computed columns | Tabular dataset (JSON, CSV) | A saved query with access control |
| Report | Formatted, multi-section document with totals | JSON, CSV, or PDF | A deliverable for stakeholders |
| Dashboard | Collection of real-time metric widgets | Widget values with thresholds | A KPI summary for executives |
Views are the building block. Reports reference views and add formatting. Dashboards aggregate metrics across the domain. All three are declared in YAML, executed by the runtime, and accessible through the API.
26.2 Views
A view is a named query on a resource with computed columns, filtering, sorting, and access control.
views:
activeGrants:
description: "All active grants with budget utilization"
source: Award
filter: "status == 'active'"
permissions: [spoOfficer, grantAccountant, admin]
columns:
- field: awardNumber
label: "Award #"
- field: title
label: "Project Title"
- field: totalAmount
label: "Total Award"
format: money
- name: expendedToDate
label: "Expended"
computed: "sum(Expenditure, 'amount', awardId == $record.id && status == 'processed')"
format: money
- name: availableBalance
label: "Available"
computed: "totalAmount - expendedToDate"
format: money
- name: utilizationRate
label: "Utilization %"
computed: "expendedToDate / totalAmount * 100"
format: percentage
- field: endDate
label: "End Date"
format: date
- name: daysRemaining
label: "Days Left"
computed: "daysUntil(endDate)"
highlight:
red: "< 30"
yellow: "< 90"
green: ">= 90"
sort: daysRemaining asc
defaultLimit: 50
View Properties
| Property | Type | Required | Description |
|---|---|---|---|
description | string | no | Human-readable description |
source | string | yes | Resource to query |
filter | string | no | AEL filter expression |
permissions | string[] | no | Roles that can access this view (default: [admin]) |
columns | array | yes | Column definitions |
sort | string | no | Default sort: fieldName asc or fieldName desc |
defaultLimit | integer | no | Default page size (default: 50) |
Column Types
Field column — references an existing field on the resource:
- field: awardNumber
label: "Award #"
Computed column — calculated using AEL expressions:
- name: utilizationRate
label: "Utilization %"
computed: "expendedToDate / totalAmount * 100"
format: percentage
Computed columns can reference other columns by name, resource fields, and cross-resource aggregations.
Column Properties
| Property | Type | Required | Description |
|---|---|---|---|
field | string | if not computed | Resource field name |
name | string | if computed | Column identifier (for computed columns) |
label | string | yes | Display label |
computed | string | if no field | AEL expression |
format | string | no | Output format (see §26.5) |
highlight | object | no | Conditional formatting thresholds |
View API
GET /api/v1/{domain}/views/{name}
?format=json # json (default) or csv
?sort=-utilizationRate # override default sort
?page=1&limit=25 # pagination
The response includes the formatted data with computed columns evaluated:
{
"data": {
"view": "activeGrants",
"columns": [
{ "name": "awardNumber", "label": "Award #", "format": null },
{ "name": "utilizationRate", "label": "Utilization %", "format": "percentage" }
],
"items": [
{
"awardNumber": "AWD-2026-001",
"title": "Genomic Analysis Platform",
"totalAmount": 500000,
"expendedToDate": 312500,
"availableBalance": 187500,
"utilizationRate": 62.5,
"daysRemaining": 45,
"_highlights": { "daysRemaining": "yellow" }
}
],
"count": 1,
"totalCount": 12
}
}
26.3 Reports
A report is a multi-section document with parameters, metrics, tables, grouping, totals, and multi-format output.
reports:
budgetStatusReport:
description: "Grant budget status by category"
permissions: [grantAccountant, spoOfficer, admin]
header:
title: "Budget Status Report"
subtitle: "Fiscal Year {$params.fiscalYear}"
parameters:
- name: fiscalYear
type: integer
default: "$currentYear"
label: "Fiscal Year"
- name: sponsor
type: string
optional: true
label: "Sponsor"
sections:
- name: summary
type: metrics
metrics:
- label: "Total Awards"
value: "count(Award, status == 'active')"
format: integer
- label: "Total Budget"
value: "sum(Award, 'totalAmount', status == 'active')"
format: money
- label: "Total Expended"
value: "sum(Expenditure, 'amount', status == 'processed')"
format: money
- label: "Overall Utilization"
value: "sum(Expenditure, 'amount', status == 'processed') / sum(Award, 'totalAmount', status == 'active') * 100"
format: percentage
- name: byCategory
type: table
view: activeGrants
groupBy: sponsor
totals: [totalAmount, expendedToDate, availableBalance]
emptyMessage: "No active grants for the selected criteria"
footer:
text: "Generated {$now} by {$actor.name}"
confidentiality: "INTERNAL — Do not distribute outside the institution"
Report Properties
| Property | Type | Required | Description |
|---|---|---|---|
description | string | no | Human-readable description |
permissions | string[] | no | Roles that can access (default: [admin]) |
header | object | no | Report title and subtitle |
parameters | array | no | User-supplied query parameters |
sections | array | yes | Report content sections |
footer | object | no | Footer text and confidentiality notice |
Section Types
Metrics — aggregate KPI values:
- name: summary
type: metrics
metrics:
- label: "Total Orders"
value: "count(Order, status != 'cancelled')"
format: integer
Table — tabular data from a view or inline query:
- name: orderDetails
type: table
view: activeOrders
groupBy: status
totals: [orderTotal]
Parameters
Reports accept parameters at query time. Parameters are substituted as $params.{name} in filters and computations:
parameters:
- name: tradeDate
type: date
default: "$today"
label: "Trade Date"
- name: venue
type: string
optional: true
label: "Venue"
enum: "distinct(Trade, 'venue')"
The enum property populates a dropdown from live data.
Report API
GET /api/v1/{domain}/reports/{name}
?format=json # json, csv, pdf
?fiscalYear=2026 # declared parameter
PDF Generation
When format=pdf is requested:
headerrenders as the title page or header blockmetricssections render as KPI cardstablesections render as formatted tables with totals rowshighlightconditions drive cell background colorsfooterrenders at the bottom of each pageconfidentialityrenders as a watermark or footer stamp
The PDF generator is a Kalos plugin. The default implementation produces a clean text-based layout. Production deployments MAY use a rich PDF library for advanced formatting.
26.4 Dashboards
A dashboard is a collection of widgets that aggregate metrics from across the domain.
dashboards:
operationsOverview:
description: "Real-time operational status"
permissions: [operationsManager, plantManager, admin]
refresh: 5m
layout:
columns: 3
widgets:
- name: assetAvailability
type: gauge
position: { row: 1, col: 1 }
query:
value: "count(Asset, status == 'operational') / count(Asset, status != 'decommissioned') * 100"
label: "Asset Availability"
format: percentage
thresholds:
green: ">= 95"
yellow: ">= 85"
red: "< 85"
target: 98
- name: overdueInspections
type: counter
position: { row: 1, col: 2 }
query:
value: "count(Asset, status == 'operational' && daysUntil(nextInspectionDue) < 0)"
label: "Overdue Inspections"
icon: lucide:alert-triangle
thresholds:
green: "== 0"
yellow: "<= 3"
red: "> 3"
- name: openWorkOrders
type: breakdown
position: { row: 1, col: 3 }
query:
resource: WorkOrder
filter: "status != 'closed' && status != 'rejected'"
groupBy: priority
metric: count
label: "Open Work Orders"
colorMap:
emergency: "#ef4444"
urgent: "#f97316"
high: "#f59e0b"
normal: "#3b82f6"
low: "#94a3b8"
- name: workOrderTrend
type: timeSeries
position: { row: 2, col: 1, span: 2 }
query:
resource: WorkOrder
metric: count
groupBy: "$dateGroup(createdAt, 'week')"
filter: "createdAt > dateAdd(now(), -90, 'day')"
label: "Work Orders — Last 90 Days"
- name: recentActivity
type: timeline
position: { row: 2, col: 3 }
query:
resource: "*"
history: true
filter: "severity == 'error' || severity == 'warning'"
limit: 10
label: "Recent Alerts"
Widget Types
| Type | Description | Output |
|---|---|---|
counter | Single numeric value | Number with threshold coloring |
gauge | Percentage with target | Value with green/yellow/red zones |
scorecard | Value with trend comparison | Number with delta indicator |
breakdown | Grouped counts or sums | Categorized values with colors |
timeSeries | Metrics over time | Grouped values by date period |
table | Tabular data from a view | Formatted data table |
timeline | Recent history events | Event feed with icons and severity |
Dashboard API
GET /api/v1/{domain}/dashboards/{name}
Returns all widget values pre-computed:
{
"data": {
"dashboard": "operationsOverview",
"computedAt": "2026-05-17T14:30:00Z",
"widgets": {
"assetAvailability": {
"type": "gauge",
"value": 96.3,
"format": "percentage",
"threshold": "green",
"target": 98
},
"overdueInspections": {
"type": "counter",
"value": 4,
"threshold": "red"
},
"openWorkOrders": {
"type": "breakdown",
"groups": [
{ "label": "emergency", "value": 1, "color": "#ef4444" },
{ "label": "urgent", "value": 3, "color": "#f97316" },
{ "label": "high", "value": 7, "color": "#f59e0b" },
{ "label": "normal", "value": 12, "color": "#3b82f6" }
]
}
}
}
}
The frontend renders directly from the response — no business logic on the client. The widget definition carries the value, the type, the threshold state, the formatting, and the colors.
Per-Widget Refresh
GET /api/v1/{domain}/dashboards/{name}/widgets/{widget}
Returns a single widget value for individual refresh without recomputing the entire dashboard.
Caching
refresh: 5m
The runtime caches the computed dashboard result and returns the cached version for requests within the refresh window. computedAt in the response shows when the data was last computed.
| Value | Behavior |
|---|---|
none | Compute on every request |
1m | Near-real-time (one-minute cache) |
5m | Standard operational dashboard |
1h | Hourly summary |
26.5 Format System
Built-In Formats
| Format | Description | Example Input | Example Output |
|---|---|---|---|
money | Currency with symbol | 1234567.89 | $1,234,567.89 |
percentage | Percentage with 1 decimal | 96.3 | 96.3% |
integer | Whole number with thousands | 1234567 | 1,234,567 |
decimal2 | Two decimal places | 3.14159 | 3.14 |
decimal4 | Four decimal places | 0.12345 | 0.1235 |
date | Locale date | 2026-03-07 | Mar 7, 2026 |
datetime | Locale date and time | 2026-03-07T14:30Z | Mar 7, 2026 2:30 PM |
duration | Human-readable duration | 172800 (seconds) | 2 days |
boolean | Yes/No | true | Yes |
Highlight Conditions
highlight:
red: "< 30"
yellow: "< 90"
green: ">= 90"
Highlights are AEL conditions evaluated against the column value. The API response includes a _highlights object mapping column names to threshold states. The frontend renders conditional formatting — identical to Excel conditional formatting but declared in the domain model.
Locale Configuration
Currency symbol and number formatting follow the domain’s locale:
domain:
locale:
currency: USD
dateFormat: "MMM d, yyyy"
numberFormat:
thousandsSeparator: ","
decimalSeparator: "."
26.6 Integration Points
Reports + Schedules
Scheduled tasks (§24) can generate and deliver reports:
schedules:
weeklyComplianceReport:
recurrence: weekly on Monday at 06:00
action: report
report: complianceWeeklySummary
recipients:
roles: [complianceOfficer, admin]
channels: [email]
format: pdf
Reports + Notifications
Reports can be attached to notification emails. When a scheduled report generates a PDF, it is delivered as an email attachment through the notification channel.
Dashboards + History
The timeline widget type queries history events directly, providing a real-time activity feed within the dashboard. The timeSeries widget can aggregate history event counts over time for trend analysis.
Views in Reports and Dashboards
Reports reference views in their table sections. Dashboards reference views in their table widgets. Views are reusable across multiple reports and dashboards.
End of Chapter 26.
Next: Chapter 27 — Templates & Documents