Skip to content

Insights Domain

The Insights domain (insights_v1) is a bounded context responsible for business intelligence, analytics, and reporting. It manages insight generation, KPI (key performance indicator) calculations, dashboard state, and data export functionality. This domain synthesizes data from across the system to provide actionable intelligence about estate performance, asset utilization, and operational metrics.

The Insights domain sits at the convergence of data analysis and reporting infrastructure. It provides:

  1. Insight Generation: Creation of business intelligence reports with typed data models
  2. KPI Calculation: Computation of performance metrics with target tracking and trend analysis
  3. Insight Lifecycle: Status management (active → acknowledged → resolved/dismissed) with severity-based routing
  4. Data Exports: Multi-format exports (CSV, GeoJSON, PDF) with cross-domain data enrichment
  5. Dashboard Reporting: Estate summaries, drawing exports, media/voice note archives

The root aggregate of this domain, representing a single business intelligence insight with complete lifecycle state.

Identity:

  • InsightId: Unique identifier for the insight
  • AggregateId: Framework ID for event sourcing

Core Attributes:

final InsightId insightId; // Unique insight ID
final InsightName title; // Human-readable title
final InsightDescription description; // Detailed description
final InsightType insightType; // Type: performance, utilization, cost, compliance, etc.
final InsightSeverity severity; // Critical, warning, info, or success
final InsightStatus status; // active, acknowledged, resolved, or dismissed

Temporal Data:

final DateTime generatedAt; // When the insight was created
final DateTime updatedAt; // Last modification timestamp
final DateTime? expiresAt; // Optional expiration time (null = never)
final UserId generatedBy; // User/system that generated the insight

Source Tracking:

final String? sourceEntityId; // ID of related entity (asset, site, etc.)
final String? sourceEntityType; // Type of source (asset, site, estate, etc.)
final List<DomainText> tags; // Categorization tags
final List<KPIId> relatedKPIs; // Related KPI calculations

Typed Data:

final InsightData insightData; // Type-safe insight data (performance, utilization, etc.)
final Map<KPIId, KPIValue> _kpiValues; // Calculated KPI values

Key Methods:

  • isActive, isAcknowledged, isResolved, isDismissed: Status checks
  • isExpired: Check if insight has surpassed expiration
  • requiresImmediateAttention: True if critical and active
  • canBeResolved(), canBeDismissed(): Status transition validation
  • isStale: True if older than 30 days and still active
  • priorityScore: Calculate urgency (0-200+) based on severity, age, and expiry
  • typedData<T>(): Access typed insight data via pattern matching

Represents a calculated key performance indicator with target tracking and trend analysis.

Attributes:

final KPIId kpiId; // Unique KPI ID
final KPIName name; // Display name
final KPIDescription description; // Explanation
final KPIType kpiType; // efficiency, performance, cost, carbonEmissions, etc.
final double value; // Current calculated value
final String unit; // Unit of measurement (%, kW, kg CO2, etc.)
final double? targetValue; // Target/benchmark (null if no target)
final double? previousValue; // Previous period value for trend
final Trend trend; // up, down, or stable
final DateTime calculatedAt; // Calculation timestamp
final String? entityId; // Optional: related entity
final String? entityType; // Optional: entity type
final String? timeframe; // daily, weekly, monthly, quarterly, yearly
final UserId calculatedBy; // User/system that calculated
final KPIMetadata typedMetadata; // Type-safe metadata

Key Methods:

  • hasTarget: Whether a target value exists
  • isMeetingTarget: Compare actual vs. target (direction-aware for cost vs. efficiency)
  • targetVariance: Percentage difference from target
  • changeFromPrevious: Absolute change from previous period
  • percentageChangeFromPrevious: Percentage change from previous
  • isImproving: True if trend is favorable for the KPI type

The domain supports multiple typed insight data models accessed via insightData:

GenericInsightData

  • Fallback container with arbitrary properties map
  • Used when specific type is unknown

PerformanceInsightData

  • efficiencyScore: 0-100
  • utilization: 0-100 percent
  • metricsSnapshot: Key metrics at time of generation

UtilizationInsightData

  • utilizationPercentage: Current utilization
  • capacityUsed: Absolute value
  • capacityAvailable: Total available
  • trend: Over time trend

CostInsightData

  • estimatedCost: Projected cost
  • costUnit: Currency or unit
  • costBreakdown: By category/department
  • savingsOpportunity: Potential reduction

ComplianceInsightData

  • complianceScore: 0-100 percent
  • violations: Count of issues
  • requiredActions: List of remediation tasks

ExportInsightData

  • format: CSV, GeoJSON, PNG, PDF
  • content: Export data
  • sourceType: drawing, site, estate
  • sourceId: Reference to source
  • assetCount: How many assets
  • includesImage: Whether drawing image included
  • assets: Structured asset data
  • generatedAt: Export creation time

The domain produces events capturing all state changes and calculations:

Fired when a key performance indicator is computed for an insight or entity.

KPICalculatedEvent(
kpiId: KPIId.fromString('kpi-hvac-efficiency'),
name: KPIName.fromString('HVAC Efficiency'),
description: KPIDescription.fromString('Overall HVAC system efficiency'),
kpiType: KPIType.efficiency,
value: 87.5,
unit: '%',
targetValue: 90.0,
previousValue: 85.0,
trend: Trend.up,
calculatedAt: DateTime.now(),
entityId: 'asset-123',
entityType: 'hvac_system',
timeframe: 'monthly',
calculatedBy: UserId.fromString('system'),
metadata: {'data_quality': 'high', 'sample_size': 1000},
)

When Fired:

  • KPI calculation services compute new metrics
  • Periodic analytics jobs produce trend data
  • On-demand KPI requests are executed

Fired when a new business insight is created.

InsightGeneratedEvent(
insightId: InsightId.fromString('insight-001'),
title: InsightName.fromString('HVAC Efficiency Below Target'),
description: InsightDescription.fromString('Building A HVAC running at 82%, target is 90%'),
insightType: InsightType.performance,
severity: InsightSeverity.warning,
status: InsightStatus.active,
sourceEntityId: 'hvac-system-1',
sourceEntityType: 'hvac_system',
generatedAt: DateTime.now(),
expiresAt: DateTime.now().add(Duration(days: 30)),
generatedBy: UserId.fromString('analytics-engine'),
tags: [
DomainText.fromString('hvac'),
DomainText.fromString('performance'),
],
insightData: PerformanceInsightData(
efficiencyScore: 82,
utilization: 95,
metricsSnapshot: {...},
),
relatedKPIs: [KPIId.fromString('kpi-hvac-efficiency')],
)

When Fired:

  • Analytics services detect conditions requiring attention
  • Thresholds are breached (efficiency, cost, compliance)
  • Automated reporting generates periodic summaries

Fired when an insight’s status transitions.

InsightStatusChangedEvent(
insightId: InsightId.fromString('insight-001'),
previousStatus: InsightStatus.active,
newStatus: InsightStatus.acknowledged,
changedBy: UserId.fromString('user-42'),
changedAt: DateTime.now(),
reason: 'Maintenance scheduled for next week',
)

Valid Transitions:

active → acknowledged → resolved
dismissed (unless critical)
active → dismissed (except critical)

Fired when a printable estate summary is generated.

EstateSummaryGeneratedEvent(
summaryId: EstateSummaryId.fromString('summary-estate-001'),
summaryType: 'comprehensive',
outputFormat: 'pdf',
estateId: EstateId.fromString('estate-nyc'),
organizationId: OrganizationId.fromString('org-123'),
requestedBy: UserId.fromString('user-42'),
generatedAt: DateTime.now(),
summaryContent: '''
Estate Summary Report
=====================
Estate: NYC Main Building
Generated: 2024-12-15
...
''',
summaryMetrics: {
'userCount': 12,
'assetCount': 145,
'siteCount': 3,
'generationTimeMs': 2500,
},
)

Fired when a floor plan drawing with associated assets is exported.

DrawingExportGeneratedEvent(
exportId: InsightId.fromString('export-drawing-001'),
format: ExportFormat.csv,
content: '''
ID,Name,Type,Status,Location
asset-1,HVAC Unit,hvac_ahu,active,Room 201
...
''',
estateId: EstateId.fromString('estate-nyc'),
siteId: SiteId.fromString('site-main'),
drawingFeatureKey: GeoFeatureKey.fromString('floor_3_plan'),
assetCount: 42,
includesImage: true,
assets: [ExportedAsset(...), ...],
generatedBy: UserId.fromString('user-42'),
generatedAt: DateTime.now(),
)

Supported Export Formats:

  • csv: Detailed or summary mode with grouping options
  • geojson: Geographic feature collection for GIS tools
  • png, pdf: Binary drawing images (server-side generation)

Fired when voice note recordings are collected and archived.

VoiceNoteArchiveGeneratedEvent(
archiveId: InsightId.fromString('archive-voices-001'),
estateId: EstateId.fromString('estate-nyc'),
siteId: SiteId.fromString('site-main'), // optional filter
grouping: VoiceNoteExportGrouping.byFloor,
voiceNoteCount: 87,
totalSizeBytes: 4523000,
includesTranscriptions: true,
voiceNotes: [ExportedVoiceNote(...), ...],
generatedBy: UserId.fromString('user-42'),
generatedAt: DateTime.now(),
)

Fired when multi-media files (photos, videos, voice notes) are collected.

MediaArchiveGeneratedEvent(
archiveId: InsightId.fromString('archive-media-001'),
estateId: EstateId.fromString('estate-nyc'),
siteId: null, // all sites
includedContent: {
MediaArchiveContentType.photos,
MediaArchiveContentType.voiceNotes,
},
grouping: MediaArchiveGrouping.byFloorRoom,
fileCountsByType: {
MediaArchiveContentType.photos: 234,
MediaArchiveContentType.voiceNotes: 87,
MediaArchiveContentType.documents: 12,
},
totalFileCount: 333,
totalSizeBytes: 52348000, // ~50 MB
includesTranscriptions: true,
generatedBy: UserId.fromString('user-42'),
generatedAt: DateTime.now(),
)

Insights progress through a lifecycle managing attention and resolution:

Status Sequence:

active (newly created or unaddressed)
acknowledged (someone is aware and may be handling)
resolved (addressed and closed)

Alternatively:

active
dismissed (not addressing; allowed only for non-critical insights)

Business Rules:

  • Critical insights cannot be dismissed directly; must be resolved
  • Dismissed insights can be re-opened to active
  • Expiration applies independently (insights auto-expire if expiresAt is reached)

Severity Levels:

  • critical: Requires immediate action (facility-impacting issues)
  • warning: Should be addressed soon (performance degradation)
  • info: Informational (performance improvements, general data)
  • success: Positive outcome or achievement

Priority Scoring Algorithm:

base_score = 0
// Severity multiplier
if critical: base_score += 100
if warning: base_score += 50
if info: base_score += 10
if success: base_score += 5
// Age penalty (older = lower priority)
base_score -= age_in_days
// Expiry urgency (expires soon = higher priority)
if expiry_days_remaining <= 7: base_score += 20
result = max(base_score, 0)

Insights track staleness to identify those needing review:

  • Fresh: Generated recently (less than 7 days)
  • Aging: 7-30 days old; actionable but attention needed
  • Stale: 30+ days old while still active; should review for closure

Detailed Mode:

  • One row per asset
  • All custom fields as columns
  • Comments, voice note transcriptions
  • Comprehensive data for analysis

Summary Mode:

  • Aggregated counts by grouping (listing, category, status)
  • High-level overview
  • For dashboards and reporting
  • Asset positions as Point features
  • Properties include ID, name, asset type, listing, category
  • Compatible with GIS tools and mapping libraries
  • Only includes assets with coordinates

Collects related media into organized ZIP structures:

Content Types:

  • photos: Image attachments from assets
  • videos: Video attachments
  • voiceNotes: Audio recordings with optional transcriptions
  • documents: Files and PDFs

Grouping Options:

  • byAsset: Folder per asset
  • byFloorRoom: Organized by floor/room hierarchy
  • bySiteFloor: Organized by site and floor
  • flat: All files in root

GenerateInsightDirective Creates a new insight with full business intelligence data.

GenerateInsightPayload(
insightId: InsightId.generate(),
title: InsightName.fromString('Energy Efficiency Alert'),
description: InsightDescription.fromString('Building exceeding energy baseline'),
insightType: InsightType.performance,
severity: InsightSeverity.warning,
sourceEntityId: 'bldg-a',
sourceEntityType: 'building',
expiresAt: DateTime.now().add(Duration(days: 30)),
generatedBy: UserId.fromString('analytics-service'),
tags: [
DomainText.fromString('energy'),
DomainText.fromString('efficiency'),
],
data: {
'efficiencyScore': 75,
'baselineValue': 85,
'variance': -10,
},
relatedKPIs: [KPIId.fromString('kpi-energy-efficiency')],
)

UpdateInsightStatusDirective Transition insight status (active → acknowledged → resolved/dismissed).

UpdateInsightStatusPayload(
insightId: InsightId.fromString('insight-001'),
previousStatus: InsightStatus.active,
newStatus: InsightStatus.acknowledged,
changedBy: UserId.fromString('user-42'),
reason: 'Maintenance request submitted',
)

CalculateKPIDirective Compute a performance metric and associate with insights.

CalculateKPIPayload(
kpiId: KPIId.generate(),
name: KPIName.fromString('HVAC Efficiency'),
description: KPIDescription.fromString('System-wide efficiency metric'),
kpiType: KPIType.efficiency,
value: 87.5,
unit: '%',
targetValue: 90.0,
previousValue: 85.2,
trend: Trend.up,
relatedEntityId: 'hvac-system-1',
entityType: 'hvac_system',
timeframe: 'monthly',
calculatedBy: UserId.fromString('system'),
metadata: {
'methodology': 'weighted_average',
'sample_count': 1000,
'confidence': 0.95,
},
)

GenerateDrawingExportDirective Export assets from a specific drawing with cross-domain data enrichment.

GenerateDrawingExportPayload(
exportId: InsightId.generate(),
estateId: EstateId.fromString('estate-nyc'),
siteId: SiteId.fromString('site-main'),
drawingFeatureKey: GeoFeatureKey.fromString('floor_3_plan'),
format: ExportFormat.csv,
csvMode: CsvExportMode.detailed,
summaryGrouping: CsvSummaryGrouping.byListing,
includeAssets: true,
includeImage: true,
generatedBy: UserId.fromString('user-42'),
content: '', // populated in plan step
assets: [], // populated in plan step
)

Cross-Domain Data Resolution (in plan step):

  1. Queries all assets in estate workspace
  2. Filters to assets on the specified drawing
  3. Resolves attachment metadata from catalogue workspace
  4. Collects custom field definitions and display names
  5. Resolves taxonomy information (categories, listing names)
  6. Collects voice note transcriptions from asset comments
  7. Generates export content with fully resolved field names

GenerateEstateSummaryDirective Create a comprehensive estate report with selected metrics.

GenerateEstateSummaryPayload(
summaryId: EstateSummaryId.generate(),
summaryType: 'comprehensive',
outputFormat: 'pdf',
estateId: EstateId.fromString('estate-nyc'),
requestedBy: UserId.fromString('user-42'),
includeAssetCounts: true,
includeUserCounts: true,
includeSiteBreakdown: true,
includeGrowthMetrics: true,
includeCollaborationStats: true,
fromDate: DateTime.now().subtract(Duration(days: 90)),
toDate: DateTime.now(),
filterTags: ['active', 'priority'],
formatOptions: {'pageSize': 'A4', 'orientation': 'portrait'},
metadata: {'reportVersion': '1.0'},
)

Included Sections:

  • Estate and organization identification
  • User and asset counts
  • Site breakdown and hierarchy
  • Asset categorization by type/status
  • Collaboration metrics (comments, permissions)
  • Growth trends over timeframe
  • Custom field aggregations

GenerateVoiceNoteArchiveDirective Collect voice notes and organize into downloadable archive.

GenerateVoiceNoteArchivePayload(
archiveId: InsightId.generate(),
estateId: EstateId.fromString('estate-nyc'),
siteId: SiteId.fromString('site-main'), // optional
grouping: VoiceNoteExportGrouping.byFloor,
includeTranscriptions: true,
includeManifestCsv: true,
generatedBy: UserId.fromString('user-42'),
voiceNotes: [], // populated in plan step
)

Plan Step Processing:

  1. Queries all assets (or filtered by site)
  2. Collects voice note IDs from asset comments
  3. Resolves attachment metadata (transcription, duration, blob ref)
  4. Organizes by floor/room hierarchy
  5. Generates manifest CSV for ZIP contents

GenerateMediaArchiveDirective Comprehensive media collection with flexible filtering and organization.

GenerateMediaArchivePayload(
archiveId: InsightId.generate(),
estateId: EstateId.fromString('estate-nyc'),
siteId: null, // all sites
includeContent: {
MediaArchiveContentType.photos,
MediaArchiveContentType.voiceNotes,
},
grouping: MediaArchiveGrouping.byFloorRoom,
includeMetadataCsv: true,
includeTranscriptions: true,
generatedBy: UserId.fromString('user-42'),
mediaFiles: [], // populated in plan step
)

Plan Step Processing:

  1. Queries all assets with attachments
  2. Filters by site (if specified)
  3. Collects attachments by content type
  4. Determines content type from MIME type
  5. Resolves attachment metadata (size, blob ref, transcription)
  6. Organizes by selected grouping strategy
  7. Generates manifest CSV

The domain includes a specialized service for building thermal analysis:

Calculates building peak heat loss using fabric elements and ventilation data.

// Define building elements
final fabricElements = [
FabricElement(
type: 'wall',
area: 120.0, // m²
uValue: 0.25, // W/m²K - well-insulated
description: 'External walls',
),
FabricElement(
type: 'roof',
area: 80.0, // m²
uValue: 0.16, // W/m²K
),
FabricElement(
type: 'window',
area: 30.0, // m²
uValue: 1.4, // W/m²K - double glazing
),
];
// Define ventilation
final ventilation = VentilationData(
airChangeRate: 1.5, // ACH (air changes per hour)
heatRecoveryEfficiency: 0.75, // 75% recovery
buildingVolume: 1200.0, // m³ (calculated from floor area × 3m height)
);
// Define temperature setpoints
final temperatures = TemperatureSetpoints(
internalTemp: 21.0, // °C
externalTemp: -5.0, // °C (design temperature)
);
// Calculate
final inputs = PeakHeatLossInputs(
fabricElements: fabricElements,
ventilation: ventilation,
temperatures: temperatures,
);
final results = PeakHeatLossCalculator.calculate(inputs);
// Access results
print('Fabric heat loss: ${results.fabricHeatLoss} W/K');
print('Ventilation heat loss: ${results.ventilationHeatLoss} W/K');
print('Total heat loss coefficient: ${results.totalHeatLossCoefficient} W/K');
print('Peak heat loss: ${results.peakHeatLoss} kW');
// Analyze breakdown
for (final elementResult in results.fabricBreakdown) {
print('${elementResult.type}: ${elementResult.percentage.toStringAsFixed(1)}%');
}

Calculation Method:

  1. Fabric Heat Loss: Σ(Area × U-value) for all elements
  2. Ventilation Heat Loss:
    • Air flow rate = (ACH × Building Volume) / 3600
    • Heat loss = Air flow × Air density × Specific heat capacity
    • Apply heat recovery: Final = Heat loss × (1 - Recovery efficiency)
  3. Total Heat Loss Coefficient: Fabric + Ventilation (W/K)
  4. Peak Heat Loss: Total × Temperature difference (convert W to kW)

Validation:

final errors = PeakHeatLossCalculator.validateInputs(inputs);
if (errors.isNotEmpty) {
print('Validation errors:');
for (final error in errors) {
print(' - $error');
}
}

Typical U-Values:

final typicalValues = PeakHeatLossCalculator.getTypicalUValues();
// {
// 'wall': 0.25, // W/m²K - well-insulated
// 'roof': 0.16, // W/m²K
// 'floor': 0.22, // W/m²K
// 'window': 1.4, // W/m²K - double glazing
// 'door': 1.8, // W/m²K
// }

Typical Air Change Rates:

final typicalAch = PeakHeatLossCalculator.getTypicalAirChangeRates();
// {
// 'office': 2.0,
// 'residential': 1.0,
// 'retail': 3.0,
// 'warehouse': 1.5,
// 'school': 4.0,
// 'hospital': 6.0,
// }

The domain provides typed selectors for insight queries:

import 'package:insights_v1/insights_v1.dart';
// Stream all active insights for an estate
watchActiveInsights(
app: nomosApp,
workspaceId: workspaceId,
timelineId: timelineId,
sourceEntityType: 'estate',
sourceEntityId: 'estate-nyc',
);
// Stream insights requiring immediate attention
watchCriticalInsights(
app: nomosApp,
workspaceId: workspaceId,
timelineId: timelineId,
).listen((insights) {
for (final insight in insights) {
if (insight.requiresImmediateAttention) {
print('Action required: ${insight.title.value}');
}
}
});
// Stream stale insights (30+ days, still active)
watchStaleInsights(
app: nomosApp,
workspaceId: workspaceId,
timelineId: timelineId,
);
// Stream insights by priority
watchInsightsByPriority(
app: nomosApp,
workspaceId: workspaceId,
timelineId: timelineId,
).listen((insights) {
// Sorted by priorityScore descending
for (final insight in insights) {
print('Priority ${insight.priorityScore}: ${insight.title}');
}
});
// Stream insights expiring soon (7 days or less)
watchExpiringInsights(
app: nomosApp,
workspaceId: workspaceId,
timelineId: timelineId,
);

Access typed insight data using pattern matching:

// Extract performance metrics
if (insight.typedData<PerformanceInsightData>() case final perfData?) {
print('Efficiency: ${perfData.efficiencyScore}%');
print('Utilization: ${perfData.utilization}%');
}
// Extract cost data
if (insight.typedData<CostInsightData>() case final costData?) {
print('Estimated cost: ${costData.estimatedCost} ${costData.costUnit}');
print('Savings opportunity: ${costData.savingsOpportunity}');
}
// Extract export data
if (insight.typedData<ExportInsightData>() case final exportData?) {
print('Export format: ${exportData.format.displayName}');
print('Asset count: ${exportData.assetCount}');
final assets = exportData.assets; // Type-safe asset list
}
// Fallback for generic data
final genericProps = insight.insightData.toJson();
  1. Insight ID cannot be unresolved (unless aggregate is empty)
  2. Title cannot be empty
  3. Severity and status must be valid enum values
  4. Critical Insights Cannot Be Dismissed: Critical severity insights must be resolved before closing (not dismissed)
  5. Expiry date cannot be before generation date
  6. Related KPIs must exist in system
  1. Value must be numeric
  2. Unit must be non-empty
  3. If target exists, unit must be compatible
  4. Trend determination:
    • up: Value increased from previous
    • down: Value decreased from previous
    • stable: Value unchanged or minimal change
  5. Meeting target is direction-aware:
    • For efficiency/performance/count: value >= target
    • For cost/emissions: value <= target
  • contracts_v1: Published language for IDs (InsightId, KPIId), severity/status enums, metadata structures
  • trackable_asset_v1: Assets for export directives, asset metadata
  • estate_structures_v1: Site, building, room definitions for scope
  • attachments_v1: Attachment aggregate for file exports and metadata
  • catalogues_v1: Listing and taxonomy definitions for enrichment
  • nomos_core: Core framework (events, aggregates, directives)
  • Applications & UIs: Display insights, KPI dashboards, trigger exports
  • Policies & Workflows: Route insights by severity, orchestrate responses
  • Analytics Engines: Consume events for trending and alerting
  • Reporting Systems: Generate periodic summary reports
  • External Systems: Integrate via exported data (GeoJSON, CSV)

Export directives perform comprehensive validation:

// Drawing exports validate:
// - Estate/site/drawing exist and are accessible
// - Cross-workspace queries are permitted
// - Asset attachment metadata is resolvable
// - Field names don't exceed CSV limits
// Heat loss calculator validates:
// - Fabric elements have positive area
// - U-values are reasonable (0-10 W/m²K)
// - Air change rates are sensible (0.5-50 ACH)
// - Temperatures make sense (-50 to 50°C)
// - Heat recovery efficiency 0-1.0
// Attachment not found in export
// → Silently skip, use raw ID in export
// Field name not found in taxonomy
// → Fallback to humanized field ID
// Asset has no coordinates for GeoJSON
// → Exclude from feature collection
// Voice note transcription missing
// → Export audio with placeholder note
import 'package:insights_v1/insights_v1.dart';
import 'package:contracts_v1/contracts_v1.dart';
// 1. Generate a performance insight
final generateInsightDirective = GenerateInsightDirective(
payload: GenerateInsightPayload(
insightId: InsightId.generate(),
title: InsightName.fromString('Building A Efficiency Below Target'),
description: InsightDescription.fromString(
'HVAC system efficiency dropped to 82%, 8% below monthly target'
),
insightType: InsightType.performance,
severity: InsightSeverity.warning,
sourceEntityId: 'bldg-a',
sourceEntityType: 'building',
expiresAt: DateTime.now().add(Duration(days: 30)),
generatedBy: UserId.fromString('analytics-service'),
tags: [
DomainText.fromString('hvac'),
DomainText.fromString('efficiency'),
DomainText.fromString('building-a'),
],
data: {
'efficiencyScore': 82,
'targetScore': 90,
'variance': -8,
'trend': 'declining',
'affectedSystems': ['hvac-unit-1', 'hvac-unit-2'],
},
relatedKPIs: [KPIId.fromString('kpi-hvac-efficiency-monthly')],
),
);
// 2. Calculate the related KPI
final calculateKpiDirective = CalculateKPIDirective(
payload: CalculateKPIPayload(
kpiId: KPIId.fromString('kpi-hvac-efficiency-monthly'),
name: KPIName.fromString('HVAC Efficiency - Monthly'),
description: KPIDescription.fromString('Building-wide HVAC efficiency average'),
kpiType: KPIType.efficiency,
value: 82.0,
unit: '%',
targetValue: 90.0,
previousValue: 85.0,
trend: Trend.down,
relatedEntityId: 'bldg-a',
entityType: 'building',
timeframe: 'monthly',
calculatedBy: UserId.fromString('analytics-service'),
metadata: {
'unitCount': 2,
'uptime': 99.8,
'sampleSize': 4320,
'method': 'weighted_average',
},
),
);
// 3. Monitor insight status
watchActiveInsights(
app: nomosApp,
workspaceId: workspaceId,
timelineId: timelineId,
sourceEntityType: 'building',
sourceEntityId: 'bldg-a',
).listen((insights) {
for (final insight in insights) {
print('Building A: ${insight.title.value}');
print('Severity: ${insight.severity.code}');
print('Priority Score: ${insight.priorityScore}');
}
});
// 4. Acknowledge insight when action begins
final acknowledgeDirective = UpdateInsightStatusDirective(
payload: UpdateInsightStatusPayload(
insightId: InsightId.fromString('insight-001'),
previousStatus: InsightStatus.active,
newStatus: InsightStatus.acknowledged,
changedBy: UserId.fromString('maintenance-team'),
reason: 'HVAC filters scheduled for replacement next Tuesday',
),
);
// 5. Export drawing with assets for maintenance crew
final exportDirective = GenerateDrawingExportDirective(
payload: GenerateDrawingExportPayload(
exportId: InsightId.generate(),
estateId: EstateId.fromString('estate-nyc'),
siteId: SiteId.fromString('site-main'),
drawingFeatureKey: GeoFeatureKey.fromString('bldg-a-floor-3-hvac-plan'),
format: ExportFormat.csv,
csvMode: CsvExportMode.detailed,
includeAssets: true,
includeImage: true,
generatedBy: UserId.fromString('maintenance-supervisor'),
content: '', // Populated in plan step
assets: [], // Populated in plan step
),
);
// 6. Resolve insight after action complete
final resolveDirective = UpdateInsightStatusDirective(
payload: UpdateInsightStatusPayload(
insightId: InsightId.fromString('insight-001'),
previousStatus: InsightStatus.acknowledged,
newStatus: InsightStatus.resolved,
changedBy: UserId.fromString('maintenance-team'),
reason: 'HVAC filters replaced, efficiency restored to 88%',
),
);
// 7. Verify via querying resolved insights
watchResolvedInsights(
app: nomosApp,
workspaceId: workspaceId,
timelineId: timelineId,
).listen((resolved) {
print('${resolved.length} insights resolved this period');
});
  • Event Sourcing: All insights are built via event sourcing, with state reconstructed by applying events
  • Snapshot Optimization: Periodic snapshots prevent replaying long event chains
  • Cross-Domain Queries: Export directives perform cross-workspace, cross-domain data enrichment in the plan step
  • Type Safety: Insight data uses sealed unions (GenericInsightData, PerformanceInsightData, etc.) for type-safe access
  • Timeline Awareness: All operations support branched and alternative timelines
  • Transactional Consistency: Directives ensure all related events (insight + KPI) are generated together
  • trackable_asset_v1: Asset data sourced for exports and insight generation
  • estate_structures_v1: Site/building/room structure for scope
  • contracts_v1: Shared enums and IDs
  • attachments_v1: File metadata for export organization
  • catalogues_v1: Listing and taxonomy for field enrichment