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.
Overview
Section titled “Overview”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.
Key Aggregates
Section titled “Key Aggregates”ResponsibilityAggregate
Section titled “ResponsibilityAggregate”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 responsibilitytitle: Human-readable name of the responsibilitydescription: Detailed explanation of what needs to be doneassignedToUserId: User currently accountable for the responsibilityresponsibilityType: Classification of responsibility (compliance, maintenance, review, etc.)status: Current lifecycle state (pending, in_progress, completed, overdue, cancelled)createdAt: Timestamp when the responsibility was createdupdatedAt: Timestamp of last modificationdueDate: Optional deadline for completionsourceContextType: Origin context (asset, proposal, estate, manual)sourceContextId: Reference to the source context entitycreatedBy: User who created the responsibilitychecklistItems: List of sub-tasks to be completedpriority: Urgency level (low, medium, high, critical)
Valid Status Values:
pending: Initial state, not yet startedin_progress: Work has beguncompleted: All work finishedoverdue: Past due date without completioncancelled: 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 accessorsbool 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 detectionbool get isActuallyOverdue { if (dueDate == null || isCompleted || isCancelled) return false; return DateTime.now().isAfter(dueDate!);}
// Checklist progressdouble 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 checksbool canBeCompleted() { if (isCompleted || isCancelled) return false; return areAllChecklistItemsCompleted();}
bool canBeReassigned() => !isCompleted && !isCancelled;
// Urgency assessmentbool 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 calculationsint? 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);}ChecklistItemStatus
Section titled “ChecklistItemStatus”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
Section titled “Domain Events”Domain events represent state changes in the responsibility lifecycle and are the single source of truth for the aggregate’s current state.
ResponsibilityCreatedEvent
Section titled “ResponsibilityCreatedEvent”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"}ResponsibilityAssignedEvent
Section titled “ResponsibilityAssignedEvent”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"}ResponsibilityStatusChangedEvent
Section titled “ResponsibilityStatusChangedEvent”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"}ChecklistItemCompletedEvent
Section titled “ChecklistItemCompletedEvent”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
Section titled “Directives”Directives are commands that express intent to change the responsibility state. They are the primary interface for commanding the domain.
CreateResponsibilityDirective
Section titled “CreateResponsibilityDirective”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 frameworkAssignResponsibilityDirective
Section titled “AssignResponsibilityDirective”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 frameworkConstraints:
- Cannot reassign completed or cancelled responsibilities
- Triggers an audit trail event
UpdateResponsibilityStatusDirective
Section titled “UpdateResponsibilityStatusDirective”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 frameworkValid Status Transitions:
pending→in_progress,cancelledin_progress→completed,overdue,cancelledoverdue→in_progress,completed,cancelledcompleted→ (terminal state)cancelled→ (terminal state)
CompleteChecklistItemDirective
Section titled “CompleteChecklistItemDirective”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 frameworkFeatures:
- Attach supporting evidence (files, reports)
- Add notes explaining the completion
- Automatically updates aggregate’s completion percentage
- Tracks who completed each item and when
Integration Guide
Section titled “Integration Guide”Registration
Section titled “Registration”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
Creating a Responsibility
Section titled “Creating a Responsibility”// 1. Create the payloadfinal 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 directivefinal directive = CreateResponsibilityDirective(payload: createPayload);
// 3. Execute through intent frameworkawait intentBus.execute(CreateResponsibilityIntent(directive: directive));Tracking Responsibility Progress
Section titled “Tracking Responsibility Progress”// Get a responsibility aggregatefinal responsibility = await aggregateStore.getLatest<ResponsibilityAggregate>( aggregateId: AggregateId('resp-123'),);
// Check various statesif (responsibility.requiresUrgentAttention) { alertManager.notifyManager(responsibility);}
// Monitor progressprint('Progress: ${responsibility.completionPercentage}%');print('Remaining items: ${responsibility.remainingChecklistItems.length}');print('Days until due: ${responsibility.daysUntilDue}');print('Estimated time: ${responsibility.estimatedTimeToCompletion}');
// Check if ready for completionif (responsibility.canBeCompleted()) { // User can mark as complete}Completing Checklist Items
Section titled “Completing Checklist Items”// Complete individual items as work progressesfinal 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)Domain Validation Rules
Section titled “Domain Validation Rules”The aggregate enforces these business rules during validation:
- Responsibility ID: Must be resolved (not unresolved placeholder)
- Title: Must be non-empty and resolved
- Assigned User: Must be a resolved user
- Status: Must be one of the valid statuses
- Type: Must be from the predefined list of valid types
- 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
Dependencies
Section titled “Dependencies”The domain depends on:
- nomos_core: NOMOS event sourcing framework and base types
- contracts_v1: Shared domain contracts and value objects
Type Imports
Section titled “Type Imports”Key types from the contracts domain used here:
ResponsibilityId: Unique identifier for responsibilitiesResponsibilityTitle: Typed title stringResponsibilityDescription: Typed description stringResponsibilityType: Typed responsibility categoryPriority: Priority enum (low, medium, high, critical)UserId: User identifier for assignment and auditDomainText: Generic text value objectSourceContextId: Reference to source contextAttachmentId: Reference to supporting evidence
Common Patterns
Section titled “Common Patterns”Handling Overdue Responsibilities
Section titled “Handling Overdue Responsibilities”// Detect overdue itemsif (responsibility.isActuallyOverdue) { // Trigger escalation workflow await escalationService.escalateResponsibility(responsibility);}
// Or automatically mark as overdue if neededif (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) ) );}Bulk Completion of Checklist Items
Section titled “Bulk Completion of Checklist Items”// Complete multiple items efficientlyfinal 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 progressReassignment with Escalation
Section titled “Reassignment with Escalation”// Reassign to escalated user with contextfinal 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 reassignmentSource Code References
Section titled “Source Code References”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