Skip to content

Responsibilities Domain (v1)

The Responsibilities domain handles accountability assignment, task management, and responsibility tracking within the CO2 ecosystem. It provides the core business logic for creating, assigning, and tracking responsibilities with support for checklist-based completion tracking and priority-based urgency management.

The Responsibilities domain (responsibilities_v1) manages:

  • Responsibility Lifecycle: Creation, assignment, status transitions, and completion
  • Task Tracking: Checklist-based progress tracking with individual item completion
  • Accountability: Assignment to users, reassignment capability, and audit trails
  • Urgency Management: Priority levels and overdue detection with deadline awareness
  • Context Association: Link responsibilities to source contexts (assets, proposals, estates)

This is an event-sourced domain built on the NOMOS framework, following clean architecture principles with clear separation of aggregates, events, and directives.

The primary aggregate managing a single responsibility throughout its lifecycle.

Structure:

class ResponsibilityAggregate extends Aggregate<ResponsibilityAggregate> {
final ResponsibilityId responsibilityId;
final ResponsibilityTitle title;
final ResponsibilityDescription description;
final UserId assignedToUserId;
final ResponsibilityType responsibilityType;
final String status;
final DateTime createdAt;
final DateTime updatedAt;
final DateTime? dueDate;
final DomainText? sourceContextType;
final SourceContextId? sourceContextId;
final UserId createdBy;
final List<String> checklistItems;
final Priority priority;
}

Attributes:

  • responsibilityId: Unique identifier for the responsibility
  • title: Human-readable name of the responsibility
  • description: Detailed explanation of what needs to be done
  • assignedToUserId: User currently accountable for the responsibility
  • responsibilityType: Classification of responsibility (compliance, maintenance, review, etc.)
  • status: Current lifecycle state (pending, in_progress, completed, overdue, cancelled)
  • createdAt: Timestamp when the responsibility was created
  • updatedAt: Timestamp of last modification
  • dueDate: Optional deadline for completion
  • sourceContextType: Origin context (asset, proposal, estate, manual)
  • sourceContextId: Reference to the source context entity
  • createdBy: User who created the responsibility
  • checklistItems: List of sub-tasks to be completed
  • priority: Urgency level (low, medium, high, critical)

Valid Status Values:

  • pending: Initial state, not yet started
  • in_progress: Work has begun
  • completed: All work finished
  • overdue: Past due date without completion
  • cancelled: No longer applicable

Valid Responsibility Types:

The system supports extensive responsibility types for different operational scenarios:

  • Operational: compliance, maintenance, review, approval, general, technical_review
  • Approval workflows: proposal_approval, budget_approval
  • Compliance: compliance_monitoring
  • Emergency management: emergency_response, emergency_it_response, emergency_facilities_response, supply_chain_emergency, emergency_resource_allocation, emergency_financial_assessment
  • Cybersecurity: cybersecurity_emergency, cybersecurity_technical_response, cybersecurity_financial_response, cybersecurity_personnel_response
  • Systems management: iot_system_management, mobile_operations_management
  • Strategic: sustainability_management, project_management

Business Logic Methods:

// Status accessors
bool get isPending => status == 'pending';
bool get isInProgress => status == 'in_progress';
bool get isCompleted => status == 'completed';
bool get isOverdue => status == 'overdue';
bool get isCancelled => status == 'cancelled';
// Overdue detection
bool get isActuallyOverdue {
if (dueDate == null || isCompleted || isCancelled) return false;
return DateTime.now().isAfter(dueDate!);
}
// Checklist progress
double get completionPercentage {
if (checklistItems.isEmpty) return isCompleted ? 100.0 : 0.0;
final completedCount = checklistItems
.where((item) => _itemCompletionStatus[item]?.isCompleted == true)
.length;
return (completedCount / checklistItems.length) * 100;
}
bool areAllChecklistItemsCompleted() {
if (checklistItems.isEmpty) return true;
return checklistItems
.every((item) => _itemCompletionStatus[item]?.isCompleted == true);
}
List<String> get remainingChecklistItems {
return checklistItems
.where((item) => _itemCompletionStatus[item]?.isCompleted != true)
.toList();
}
List<String> get completedChecklistItems {
return checklistItems
.where((item) => _itemCompletionStatus[item]?.isCompleted == true)
.toList();
}
// Transition checks
bool canBeCompleted() {
if (isCompleted || isCancelled) return false;
return areAllChecklistItemsCompleted();
}
bool canBeReassigned() => !isCompleted && !isCancelled;
// Urgency assessment
bool get requiresUrgentAttention {
if (priority == Priority.critical) return true;
if (isActuallyOverdue) return true;
if (dueDate != null) {
final hoursToDue = dueDate!.difference(DateTime.now()).inHours;
return hoursToDue <= 24 && hoursToDue > 0;
}
return false;
}
// Time calculations
int? get daysUntilDue {
if (dueDate == null) return null;
return dueDate!.difference(DateTime.now()).inDays;
}
Duration? get estimatedTimeToCompletion {
if (checklistItems.isEmpty) return null;
final remainingItems = remainingChecklistItems.length;
if (remainingItems == 0) return Duration.zero;
// 30 min per compliance item, 60 min per maintenance, 15 min per review/approval
final minutesPerItem = switch (responsibilityType.value) {
'compliance' => 30,
'maintenance' => 60,
'review' || 'approval' => 15,
_ => 30,
};
return Duration(minutes: remainingItems * minutesPerItem);
}

Value object representing the completion status of individual checklist items.

class ChecklistItemStatus {
final bool isCompleted;
final UserId? completedBy;
final DateTime? completedAt;
final AttachmentId? attachmentId;
final String? notes;
}

Use Cases:

  • Track which user completed an item and when
  • Attach evidence (attachments) to completed items
  • Provide notes or context for completion
  • Calculate progress percentage

Domain events represent state changes in the responsibility lifecycle and are the single source of truth for the aggregate’s current state.

Emitted when a new responsibility is established.

class ResponsibilityCreatedEvent implements Event {
final ResponsibilityId responsibilityId;
final ResponsibilityTitle title;
final ResponsibilityDescription description;
final UserId assignedToUserId;
final ResponsibilityType responsibilityType;
final String status; // 'pending'
final DateTime createdAt;
final DateTime? dueDate;
final DomainText? sourceContextType; // 'asset', 'proposal', 'estate', 'manual'
final SourceContextId? sourceContextId;
final UserId createdBy;
final List<String> checklistItems;
final Priority priority;
}

Payload Example:

{
"responsibilityId": "resp-123",
"title": "Conduct Annual Safety Audit",
"description": "Complete comprehensive safety assessment of facility",
"assignedToUserId": "user-456",
"responsibilityType": "compliance",
"status": "pending",
"createdAt": "2026-01-18T10:30:00Z",
"dueDate": "2026-03-18T23:59:59Z",
"sourceContextType": "estate",
"sourceContextId": "estate-789",
"createdBy": "user-admin",
"checklistItems": [
"Document facility layout",
"Inspect electrical systems",
"Review emergency procedures"
],
"priority": "high"
}

Emitted when a responsibility is reassigned to a different user.

class ResponsibilityAssignedEvent implements Event {
final ResponsibilityId responsibilityId;
final UserId previousAssigneeId;
final UserId newAssigneeId;
final UserId assignedBy;
final DateTime assignedAt;
final String? reason;
}

Payload Example:

{
"responsibilityId": "resp-123",
"previousAssigneeId": "user-456",
"newAssigneeId": "user-789",
"assignedBy": "user-manager",
"assignedAt": "2026-01-20T14:15:00Z",
"reason": "Original assignee unavailable; reassigning to senior auditor"
}

Emitted when the responsibility transitions through its lifecycle states.

class ResponsibilityStatusChangedEvent implements Event {
final ResponsibilityId responsibilityId;
final String previousStatus;
final String newStatus;
final UserId changedBy;
final DateTime changedAt;
final String? statusReason;
}

Payload Example:

{
"responsibilityId": "resp-123",
"previousStatus": "pending",
"newStatus": "in_progress",
"changedBy": "user-456",
"changedAt": "2026-01-21T09:00:00Z",
"statusReason": "Beginning audit activities"
}

Emitted when an individual checklist item is marked as done.

class ChecklistItemCompletedEvent implements Event {
final ResponsibilityId responsibilityId;
final String itemDescription;
final UserId completedBy;
final DateTime completedAt;
final AttachmentId? attachmentId;
final String? notes;
}

Payload Example:

{
"responsibilityId": "resp-123",
"itemDescription": "Inspect electrical systems",
"completedBy": "user-456",
"completedAt": "2026-01-21T16:30:00Z",
"attachmentId": "attach-111",
"notes": "All systems functioning normally. Panel replaced in 2024."
}

Directives are commands that express intent to change the responsibility state. They are the primary interface for commanding the domain.

Creates a new responsibility and assigns it to a user.

class CreateResponsibilityDirective extends Directive<
ResponsibilityAggregate, CreateResponsibilityPayload> {
// Executes with payload to generate ResponsibilityCreatedEvent
}
class CreateResponsibilityPayload extends Payload<Map<String, dynamic>> {
final ResponsibilityId responsibilityId;
final ResponsibilityTitle title;
final ResponsibilityDescription description;
final UserId assignedToUserId;
final ResponsibilityType responsibilityType;
final DateTime? dueDate;
final DomainText? sourceContextType;
final SourceContextId? sourceContextId;
final UserId createdBy;
final List<String> checklistItems;
final Priority priority;
}

Usage:

final payload = CreateResponsibilityPayload(
responsibilityId: ResponsibilityId.fromString('resp-new'),
title: ResponsibilityTitle.fromString('Review CO2 Emissions Report'),
description: ResponsibilityDescription.fromString(
'Complete review of Q1 2026 emissions data and prepare summary'
),
assignedToUserId: UserId.fromString('user-analyst'),
responsibilityType: ResponsibilityType.fromString('review'),
dueDate: DateTime.now().add(Duration(days: 7)),
sourceContextType: DomainText.fromString('asset'),
sourceContextId: SourceContextId.fromString('asset-456'),
createdBy: UserId.fromString('user-admin'),
checklistItems: [
'Verify data completeness',
'Calculate emission totals',
'Prepare executive summary',
],
priority: Priority.high,
);
final directive = CreateResponsibilityDirective(payload: payload);
// Execute through the NOMOS intent framework

Reassigns an existing responsibility to a different user.

class AssignResponsibilityDirective extends Directive<
ResponsibilityAggregate, AssignResponsibilityPayload> {
// Executes with payload to generate ResponsibilityAssignedEvent
}
class AssignResponsibilityPayload extends Payload<Map<String, dynamic>> {
final ResponsibilityId responsibilityId;
final UserId previousAssigneeId;
final UserId newAssigneeId;
final UserId assignedBy;
final String? reason;
}

Usage:

final payload = AssignResponsibilityPayload(
responsibilityId: ResponsibilityId.fromString('resp-123'),
previousAssigneeId: UserId.fromString('user-analyst'),
newAssigneeId: UserId.fromString('user-senior-analyst'),
assignedBy: UserId.fromString('user-manager'),
reason: 'Escalating to senior analyst for complex review',
);
final directive = AssignResponsibilityDirective(payload: payload);
// Execute through the NOMOS intent framework

Constraints:

  • Cannot reassign completed or cancelled responsibilities
  • Triggers an audit trail event

Transitions a responsibility through its lifecycle states.

class UpdateResponsibilityStatusDirective extends Directive<
ResponsibilityAggregate, UpdateResponsibilityStatusPayload> {
// Executes with payload to generate ResponsibilityStatusChangedEvent
}
class UpdateResponsibilityStatusPayload extends Payload<Map<String, dynamic>> {
final ResponsibilityId responsibilityId;
final String previousStatus;
final String newStatus;
final UserId changedBy;
final String? statusReason;
}

Usage:

final payload = UpdateResponsibilityStatusPayload(
responsibilityId: ResponsibilityId.fromString('resp-123'),
previousStatus: 'pending',
newStatus: 'in_progress',
changedBy: UserId.fromString('user-analyst'),
statusReason: 'Beginning data collection phase',
);
final directive = UpdateResponsibilityStatusDirective(payload: payload);
// Execute through the NOMOS intent framework

Valid Status Transitions:

  • pendingin_progress, cancelled
  • in_progresscompleted, overdue, cancelled
  • overduein_progress, completed, cancelled
  • completed → (terminal state)
  • cancelled → (terminal state)

Marks an individual checklist item as completed with optional evidence.

class CompleteChecklistItemDirective extends Directive<
ResponsibilityAggregate, CompleteChecklistItemPayload> {
// Executes with payload to generate ChecklistItemCompletedEvent
}
class CompleteChecklistItemPayload extends Payload<Map<String, dynamic>> {
final ResponsibilityId responsibilityId;
final String itemDescription;
final UserId completedBy;
final AttachmentId? attachmentId;
final String? notes;
}

Usage:

final payload = CompleteChecklistItemPayload(
responsibilityId: ResponsibilityId.fromString('resp-123'),
itemDescription: 'Verify data completeness',
completedBy: UserId.fromString('user-analyst'),
attachmentId: AttachmentId.fromString('attach-verification-report'),
notes: 'All required fields present in source data. No anomalies detected.',
);
final directive = CompleteChecklistItemDirective(payload: payload);
// Execute through the NOMOS intent framework

Features:

  • Attach supporting evidence (files, reports)
  • Add notes explaining the completion
  • Automatically updates aggregate’s completion percentage
  • Tracks who completed each item and when

To use the Responsibilities domain in your application, register its types:

import 'package:responsibilities_v1/responsibilities_v1.dart';
void main() {
// Register all responsibilities domain types
ResponsibilitiesV1().registerDomainTypes();
// Now you can use the domain...
}

The registration process handles:

  • Aggregate factory registration for deserialization
  • Event type registration and factory setup
  • Directive type registration and factory setup
  • TypeRegistry and FactoryRegistry configuration
// 1. Create the payload
final createPayload = CreateResponsibilityPayload(
responsibilityId: ResponsibilityId.fromString('resp-${uuid.v4()}'),
title: ResponsibilityTitle.fromString('Monthly Compliance Check'),
description: ResponsibilityDescription.fromString(
'Execute monthly compliance verification against regulatory requirements'
),
assignedToUserId: UserId.fromString('user-compliance-officer'),
responsibilityType: ResponsibilityType.fromString('compliance'),
dueDate: DateTime.now().add(Duration(days: 30)),
sourceContextType: DomainText.fromString('estate'),
sourceContextId: SourceContextId.fromString('estate-main-campus'),
createdBy: UserId.fromString('user-admin'),
checklistItems: [
'Review policy documentation',
'Audit system configurations',
'Document findings',
'Prepare report'
],
priority: Priority.high,
);
// 2. Create the directive
final directive = CreateResponsibilityDirective(payload: createPayload);
// 3. Execute through intent framework
await intentBus.execute(CreateResponsibilityIntent(directive: directive));
// Get a responsibility aggregate
final responsibility = await aggregateStore.getLatest<ResponsibilityAggregate>(
aggregateId: AggregateId('resp-123'),
);
// Check various states
if (responsibility.requiresUrgentAttention) {
alertManager.notifyManager(responsibility);
}
// Monitor progress
print('Progress: ${responsibility.completionPercentage}%');
print('Remaining items: ${responsibility.remainingChecklistItems.length}');
print('Days until due: ${responsibility.daysUntilDue}');
print('Estimated time: ${responsibility.estimatedTimeToCompletion}');
// Check if ready for completion
if (responsibility.canBeCompleted()) {
// User can mark as complete
}
// Complete individual items as work progresses
final completePayload = CompleteChecklistItemPayload(
responsibilityId: ResponsibilityId.fromString('resp-123'),
itemDescription: 'Review policy documentation',
completedBy: UserId.fromString('user-officer'),
notes: 'All current policies reviewed and documented',
);
final directive = CompleteChecklistItemDirective(payload: completePayload);
await intentBus.execute(CompleteChecklistItemIntent(directive: directive));
// Aggregate now reflects:
// - Updated completionPercentage
// - Marked item in completedChecklistItems
// - Completion metadata (who, when, notes)

The aggregate enforces these business rules during validation:

  1. Responsibility ID: Must be resolved (not unresolved placeholder)
  2. Title: Must be non-empty and resolved
  3. Assigned User: Must be a resolved user
  4. Status: Must be one of the valid statuses
  5. Type: Must be from the predefined list of valid types
  6. Priority: Handled by enum-like value object

Relaxed Rules for Testing:

  • Due date validation is disabled to support testing scenarios
  • Completion with incomplete checklist items is allowed for demo flows
  • In production, these can be re-enabled for strict enforcement

The domain depends on:

  • nomos_core: NOMOS event sourcing framework and base types
  • contracts_v1: Shared domain contracts and value objects

Key types from the contracts domain used here:

  • ResponsibilityId: Unique identifier for responsibilities
  • ResponsibilityTitle: Typed title string
  • ResponsibilityDescription: Typed description string
  • ResponsibilityType: Typed responsibility category
  • Priority: Priority enum (low, medium, high, critical)
  • UserId: User identifier for assignment and audit
  • DomainText: Generic text value object
  • SourceContextId: Reference to source context
  • AttachmentId: Reference to supporting evidence
// Detect overdue items
if (responsibility.isActuallyOverdue) {
// Trigger escalation workflow
await escalationService.escalateResponsibility(responsibility);
}
// Or automatically mark as overdue if needed
if (responsibility.daysUntilDue != null && responsibility.daysUntilDue! < 0) {
final statusPayload = UpdateResponsibilityStatusPayload(
responsibilityId: responsibility.responsibilityId,
previousStatus: responsibility.status,
newStatus: 'overdue',
changedBy: UserId.system,
statusReason: 'Automatically marked overdue past deadline',
);
await intentBus.execute(
UpdateResponsibilityStatusIntent(
directive: UpdateResponsibilityStatusDirective(payload: statusPayload)
)
);
}
// Complete multiple items efficiently
final responsibilityId = ResponsibilityId.fromString('resp-123');
for (final item in checklist) {
final payload = CompleteChecklistItemPayload(
responsibilityId: responsibilityId,
itemDescription: item.description,
completedBy: currentUser,
attachmentId: item.evidence,
);
await intentBus.execute(
CompleteChecklistItemIntent(
directive: CompleteChecklistItemDirective(payload: payload)
)
);
}
// Responsibility aggregate is automatically updated
// with cumulative progress
// Reassign to escalated user with context
final assignPayload = AssignResponsibilityPayload(
responsibilityId: ResponsibilityId.fromString('resp-123'),
previousAssigneeId: currentAssignee,
newAssigneeId: escalatedUser,
assignedBy: manager,
reason: 'Escalated due to complexity. Original assignee transferred.',
);
final directive = AssignResponsibilityDirective(payload: assignPayload);
await intentBus.execute(
AssignResponsibilityIntent(directive: directive)
);
// Event provides audit trail of reassignment

The implementation is located at:

  • Aggregate: /Users/crew/Documents/co2-target-asset-management-melos/dart_packages/co2/domains/responsibilities_v1/lib/src/aggregates/responsibility_aggregate.dart
  • Events: /Users/crew/Documents/co2-target-asset-management-melos/dart_packages/co2/domains/responsibilities_v1/lib/src/events/responsibility_events.dart
  • Directives: /Users/crew/Documents/co2-target-asset-management-melos/dart_packages/co2/domains/responsibilities_v1/lib/src/directives/responsibility_directives.dart
  • Public API: /Users/crew/Documents/co2-target-asset-management-melos/dart_packages/co2/domains/responsibilities_v1/lib/responsibilities_v1.dart
  • The domain uses event sourcing for complete audit trails of all responsibility changes
  • Checklist item completion includes rich metadata (who, when, supporting evidence, notes)
  • The aggregate supports fine-grained progress tracking via completion percentages
  • Urgency assessment combines priority levels with deadline awareness
  • Time estimates are calculated based on responsibility type and remaining work