Feedback Domain (feedback_v1)
The Feedback domain (feedback_v1) is a bounded context that manages user feedback, issue reporting, and support communication. It handles the complete lifecycle of user-submitted feedback, including bug reports, feature requests, general feedback, and issues. This domain enables organizations to collect structured user input, track its progress through resolution workflows, and maintain a dialogue with users through response management.
Overview
Section titled “Overview”The Feedback domain provides a comprehensive framework for feedback collection and management. It supports multiple feedback types and workflows, tracks feedback status through multiple states, manages responses and communication with users, and maintains rich metadata about each feedback item.
Key responsibilities:
- Feedback Lifecycle Management: Track feedback from submission through review, triage, response, and resolution
- Feedback Classification: Categorize feedback by type, priority, and category for effective routing
- Response Management: Support both public and internal (private) responses to user feedback
- Status Tracking: Monitor feedback progress through distinct workflow states
- Audit Trail & Metadata: Capture submission context including browser/device info and custom metadata
- Attachment Support: Associate files and attachments with feedback and responses
Package Location
Section titled “Package Location”dart_packages/co2/domains/feedback_v1/├── lib/│ ├── feedback_v1.dart # Main export and registration│ └── src/│ ├── aggregates/│ │ └── feedback_aggregate.dart # FeedbackAggregate root entity│ ├── events/│ │ └── feedback_events.dart # Domain events│ └── directives/│ └── feedback_directives.dart # Command handlers├── pubspec.yaml└── test/ └── feedback_v1_test.dartCore Concepts
Section titled “Core Concepts”Feedback Types
Section titled “Feedback Types”The domain supports four primary feedback types through the FeedbackType value object:
| Type | Code | Purpose |
|---|---|---|
| Bug Report | bug_report | Reports of functional defects or issues |
| Feature Request | feature_request | Requests for new functionality |
| General Feedback | general_feedback | Comments and suggestions |
| Issue | issue | General problem reports |
Feedback Priority Levels
Section titled “Feedback Priority Levels”Feedback can be assigned one of four priority levels using FeedbackPriority:
| Priority | Code | Impact |
|---|---|---|
| Low | low | Nice-to-have improvements |
| Medium | medium | Should be addressed in normal workflow |
| High | high | Significant impact, prioritize soon |
| Urgent | urgent | Critical issues requiring immediate attention |
Feedback Categories
Section titled “Feedback Categories”Feedback is organized into five categories using FeedbackCategory:
| Category | Code | Description |
|---|---|---|
| UI/UX | ui_ux | User interface and experience issues |
| Performance | performance | Speed, responsiveness, scalability concerns |
| Functionality | functionality | Feature behavior and logic issues |
| Data | data | Data accuracy, integrity, or handling |
| Other | other | General or uncategorized feedback |
Feedback Status Workflow
Section titled “Feedback Status Workflow”Feedback progresses through six distinct states:
┌─────────┐│ new │──────────────────────┐└────┬────┘ │ │ │ ↓ ↓┌──────────┐ ┌─────────┐│ in_review│ │ rejected│└────┬─────┘ └─────────┘ │ ↓┌────────────┐│in_progress │└────┬───────┘ │ ↓┌──────────┐│ resolved │└────┬─────┘ │ ↓┌────────┐│ closed │└────────┘Status Descriptions
Section titled “Status Descriptions”- new: Initial state when feedback is first submitted; ready for triage
- in_review: Feedback is being assessed and categorized; may collect additional information
- in_progress: Work is underway to address the feedback; at least one response has been provided
- resolved: Issue or request has been addressed; user has received a solution or response
- closed: Feedback lifecycle is complete; no further action expected
- rejected: Feedback has been declined (e.g., duplicate, out of scope, not reproducible)
Business Rules
Section titled “Business Rules”- Feedback title and description required: Both must be non-empty strings
- Valid status transitions: Only permitted transitions between states are allowed
- Valid feedback types: Only recognized type values are accepted
- Valid priorities and categories: Must match enumerated values
- Resolved feedback must have responses: Feedback cannot move to “resolved” status without at least one response (business rule enforced in
validate()) - Context capture: Browser info and device info are captured for bug reports to aid reproduction
Key Aggregates
Section titled “Key Aggregates”FeedbackAggregate
Section titled “FeedbackAggregate”The root aggregate representing a single piece of user feedback with complete lifecycle management.
Identity & Core Properties:
final FeedbackId feedbackId; // Unique feedback identifierfinal FeedbackTitle title; // User-visible titlefinal FeedbackDescription description; // Detailed descriptionfinal FeedbackType feedbackType; // Category: bug_report, feature_request, etc.final FeedbackPriority priority; // Priority level: low, medium, high, urgentfinal FeedbackCategory category; // Category: ui_ux, performance, etc.final UserId submittedBy; // User who submitted the feedbackfinal DateTime submittedAt; // Submission timestampfinal DateTime updatedAt; // Last modification timefinal FeedbackStatus status; // Current workflow stateContact & Context Information:
final EmailAddress? contactEmail; // Optional contact email (can be different from submitter)final BrowserInfo? browserInfo; // Browser version/type for bug contextfinal DeviceInfo? deviceInfo; // Device information (OS, model, etc.)final List<AttachmentId> attachmentIds; // File attachments to the feedbackfinal Map<String, Object?> metadata; // Arbitrary custom key-value dataResponses:
final List<FeedbackResponse> _responses; // Internal collection of responsesKey Methods
Section titled “Key Methods”Status & Lifecycle Queries:
isNew,isInReview,isInProgress,isResolved,isClosed,isRejected: Status checkingisOpen: True if feedback is still being worked on (not closed/resolved/rejected)canBeResolved(): Check if feedback can transition to resolved (has responses)canBeClosed(): Check if feedback can be closed (must be resolved or rejected)canBeReopened(): Check if feedback can be reopened (was closed or resolved)
Response Management:
responses: Immutable list of all responsespublicResponses: List of responses visible to the userprivateResponses: List of internal/staff-only responseshasResponses: Boolean check for response existencehasPublicResponses: Check for user-visible responseslatestResponse: Get the most recent response by timestampresponseCount: Total number of responses
Aging & Activity:
ageInDays: Days since feedback was submittedisStale: True if older than 30 days and still opendaysSinceLastActivity: Days since last response (or submission if no responses)
Metadata & Analysis:
requiresUrgentAttention: True if urgent priority and still opentotalAttachmentCount: Count of attachments on feedback and all responsesmetadata: Custom key-value data for extensibility
Event Application
Section titled “Event Application”The aggregate applies events through the apply() method:
feedback = feedback.apply(FeedbackSubmittedEvent( feedbackId: feedbackId, title: FeedbackTitle('Login button unresponsive'), description: FeedbackDescription('Cannot click login button on mobile'), feedbackType: FeedbackType('bug_report'), priority: FeedbackPriority('high'), category: FeedbackCategory('ui_ux'), submittedBy: userId, submittedAt: DateTime.now(), contactEmail: EmailAddress('user@example.com'), browserInfo: BrowserInfo('Safari 17.1'), deviceInfo: DeviceInfo('iPhone 14 Pro'), attachmentIds: [attachmentId1], metadata: {'screenResolution': '1170x2532'},));FeedbackResponse (Value Object)
Section titled “FeedbackResponse (Value Object)”Represents a single response to feedback, which may be public (visible to user) or private (internal only).
class FeedbackResponse { final FeedbackResponseId responseId; // Unique response identifier final FeedbackResponseText responseText; // Response content final UserId respondedBy; // Staff member or system final DateTime respondedAt; // Response timestamp final bool isPublic; // Whether visible to user final List<AttachmentId> attachmentIds; // Files attached to response}Query Methods:
hasAttachments: Check if response includes filesageInDays: Days since response was createdtoJson(): Serialize to JSONfromJson(): Deserialize from JSON
Domain Events
Section titled “Domain Events”The domain publishes events capturing all significant state changes:
FeedbackSubmittedEvent
Section titled “FeedbackSubmittedEvent”Fired when a user submits feedback (new → in_review or direct submission).
FeedbackSubmittedEvent( feedbackId: FeedbackId.fromString('feedback-2024-001'), title: FeedbackTitle('Export button missing in dashboard'), description: FeedbackDescription( 'When viewing the analytics dashboard, I cannot find the export to CSV button ' 'that was present in the previous version.' ), feedbackType: FeedbackType('bug_report'), priority: FeedbackPriority('medium'), category: FeedbackCategory('functionality'), submittedBy: UserId.fromString('user-alice'), submittedAt: DateTime.now(), contactEmail: EmailAddress('alice@example.com'), browserInfo: BrowserInfo('Chrome 121.0'), deviceInfo: DeviceInfo('Windows 10 Desktop'), attachmentIds: [AttachmentId.fromString('screenshot-001')], metadata: { 'url': '/analytics/dashboard', 'viewport': '1920x1080', 'timestamp': DateTime.now().toIso8601String(), },)Payload:
feedbackId: Unique identifiertitle: Brief summarydescription: Detailed explanationfeedbackType: Classification (bug_report, feature_request, etc.)priority: Priority levelcategory: Category classificationsubmittedBy: User identifiersubmittedAt: Submission timestampcontactEmail: Optional contact emailbrowserInfo: Optional browser contextdeviceInfo: Optional device contextattachmentIds: Optional file attachmentsmetadata: Custom context data
FeedbackStatusChangedEvent
Section titled “FeedbackStatusChangedEvent”Fired when feedback status transitions (e.g., in_review → in_progress).
FeedbackStatusChangedEvent( feedbackId: FeedbackId.fromString('feedback-2024-001'), previousStatus: FeedbackStatus('in_review'), newStatus: FeedbackStatus('in_progress'), changedBy: UserId.fromString('user-bob'), changedAt: DateTime.now(), response: 'Our team has confirmed the issue and is working on a fix.', reason: 'Bug confirmed in development environment',)Payload:
feedbackId: Target feedbackpreviousStatus: Former statenewStatus: New statechangedBy: User making the changechangedAt: Timestamp of changeresponse: Optional public message to userreason: Optional internal reason/notes
FeedbackResponseAddedEvent
Section titled “FeedbackResponseAddedEvent”Fired when support staff responds to feedback.
FeedbackResponseAddedEvent( feedbackId: FeedbackId.fromString('feedback-2024-001'), responseId: FeedbackResponseId.fromString('response-001'), responseText: FeedbackResponseText( 'We have identified the root cause of the export button issue. ' 'A fix has been deployed to the staging environment and will be ' 'released in our next production update scheduled for March 15th.' ), respondedBy: UserId.fromString('support-team'), respondedAt: DateTime.now(), isPublic: true, attachmentIds: [AttachmentId.fromString('release-notes-123')],)Payload:
feedbackId: Target feedbackresponseId: Unique response identifierresponseText: Response contentrespondedBy: Responder user IDrespondedAt: Response timestampisPublic: Whether visible to userattachmentIds: Optional attachments (release notes, screenshots, etc.)
Domain Directives
Section titled “Domain Directives”Directives are command handlers that validate inputs and produce events. The feedback domain implements directives for each major operation:
SubmitFeedbackDirective
Section titled “SubmitFeedbackDirective”Creates and submits new feedback.
Payload:
class SubmitFeedbackPayload { final FeedbackId feedbackId; final FeedbackTitle title; final FeedbackDescription description; final FeedbackType feedbackType; final FeedbackPriority priority; final FeedbackCategory category; final UserId submittedBy; final EmailAddress? contactEmail; final BrowserInfo? browserInfo; final DeviceInfo? deviceInfo; final List<AttachmentId> attachmentIds; final Metadata metadata;}Output Events:
FeedbackSubmittedEventwith complete feedback details
Validation:
- Title must not be empty
- Description must not be empty
- Feedback type must be valid
- Priority must be valid
- Category must be valid
Usage:
final payload = SubmitFeedbackPayload( feedbackId: FeedbackId.fromString('feedback-2024-100'), title: FeedbackTitle('Mobile app crashes on login'), description: FeedbackDescription( 'The mobile app consistently crashes when attempting to log in with two-factor authentication.' ), feedbackType: FeedbackType('bug_report'), priority: FeedbackPriority('high'), category: FeedbackCategory('functionality'), submittedBy: UserId.fromString('user-charlie'), contactEmail: EmailAddress('charlie@example.com'), browserInfo: null, deviceInfo: DeviceInfo('iOS 17.2 iPhone 15'), attachmentIds: [crashLogId], metadata: { 'appVersion': '2.1.0', 'buildNumber': '2024.001', },);
final directive = SubmitFeedbackDirective(payload: payload);UpdateFeedbackStatusDirective
Section titled “UpdateFeedbackStatusDirective”Changes feedback status and optionally adds a response message.
Payload:
class UpdateFeedbackStatusPayload { final FeedbackId feedbackId; final FeedbackStatus previousStatus; final FeedbackStatus newStatus; final UserId changedBy; final String? response; final String? reason;}Output Events:
FeedbackStatusChangedEventwith status transition details
Business Rule Enforcement:
- New status must be valid
- Status transition must follow workflow rules
- Resolved status requires at least one response to exist
Usage:
final payload = UpdateFeedbackStatusPayload( feedbackId: FeedbackId.fromString('feedback-2024-100'), previousStatus: FeedbackStatus('in_review'), newStatus: FeedbackStatus('in_progress'), changedBy: UserId.fromString('user-bob'), response: 'Our team has confirmed this issue and is working on a patch.', reason: 'Bug reproduced in QA environment',);
final directive = UpdateFeedbackStatusDirective(payload: payload);AddFeedbackResponseDirective
Section titled “AddFeedbackResponseDirective”Adds a response from support staff (public or internal).
Payload:
class AddFeedbackResponsePayload { final FeedbackId feedbackId; final FeedbackResponseId responseId; final FeedbackResponseText responseText; final UserId respondedBy; final bool isPublic; final List<AttachmentId> attachmentIds;}Output Events:
FeedbackResponseAddedEventwith response details
Validation:
- Response text must not be empty
- Response ID must be unique
- User making response must be valid
Usage:
final payload = AddFeedbackResponsePayload( feedbackId: FeedbackId.fromString('feedback-2024-100'), responseId: FeedbackResponseId.fromString('response-100-001'), responseText: FeedbackResponseText( 'Thank you for reporting this issue. We have deployed a hotfix in version 2.1.1 ' 'which addresses the two-factor authentication crash. Please update and let us know ' 'if the issue persists.' ), respondedBy: UserId.fromString('support-engineer'), isPublic: true, attachmentIds: [releaseNotesId],);
final directive = AddFeedbackResponseDirective(payload: payload);Usage Examples
Section titled “Usage Examples”Submitting Bug Report with Context
Section titled “Submitting Bug Report with Context”import 'package:feedback_v1/feedback_v1.dart';import 'package:contracts_v1/contracts_v1.dart';
// User submits a bug report with browser and device contextfinal reportPayload = SubmitFeedbackPayload( feedbackId: FeedbackId.fromString('fb-2024-bug-001'), title: FeedbackTitle('Sidebar navigation not scrolling'), description: FeedbackDescription( 'When viewing a site with many sub-sections in the sidebar, the navigation ' 'menu cannot be scrolled. This makes it impossible to access items below the fold.' ), feedbackType: FeedbackType('bug_report'), priority: FeedbackPriority('medium'), category: FeedbackCategory('ui_ux'), submittedBy: UserId.fromString('user-dave'), contactEmail: EmailAddress('dave.smith@company.com'), browserInfo: BrowserInfo('Firefox 123.0 on macOS Sonoma'), deviceInfo: DeviceInfo('MacBook Pro 16-inch M3'), attachmentIds: [screenshotId, videoId], metadata: { 'viewportSize': '1440x900', 'screenResolution': '3456x2234', 'operatingSystem': 'macOS 14.2', 'timestamp': DateTime.now().toIso8601String(), },);
final directive = SubmitFeedbackDirective(payload: reportPayload);Feature Request Workflow
Section titled “Feature Request Workflow”// User submits a feature requestfinal featurePayload = SubmitFeedbackPayload( feedbackId: FeedbackId.fromString('fb-2024-feature-001'), title: FeedbackTitle('Bulk asset status update capability'), description: FeedbackDescription( 'It would be helpful to select multiple assets and update their status ' 'in one operation. Currently I have to update each asset individually, ' 'which is time-consuming when managing hundreds of assets.' ), feedbackType: FeedbackType('feature_request'), priority: FeedbackPriority('high'), category: FeedbackCategory('functionality'), submittedBy: UserId.fromString('user-eve'), contactEmail: null, browserInfo: null, deviceInfo: null, attachmentIds: [], metadata: { 'estimatedAssetCount': '500+', 'useFrequency': 'daily', },);
// After review, update status and provide feedbackfinal statusPayload = UpdateFeedbackStatusPayload( feedbackId: FeedbackId.fromString('fb-2024-feature-001'), previousStatus: FeedbackStatus('new'), newStatus: FeedbackStatus('in_review'), changedBy: UserId.fromString('product-manager'), reason: 'Prioritized for Q2 development', response: null,);Complete Response Workflow
Section titled “Complete Response Workflow”// Support team acknowledges and provides initial statusfinal firstResponsePayload = AddFeedbackResponsePayload( feedbackId: FeedbackId.fromString('fb-2024-bug-001'), responseId: FeedbackResponseId.fromString('resp-001'), responseText: FeedbackResponseText( 'We have received your report about sidebar scrolling. ' 'Our team is investigating this issue. We will update you within 48 hours.' ), respondedBy: UserId.fromString('support-l1'), isPublic: true, attachmentIds: [],);
// Later, provide solutionfinal solutionPayload = AddFeedbackResponsePayload( feedbackId: FeedbackId.fromString('fb-2024-bug-001'), responseId: FeedbackResponseId.fromString('resp-002'), responseText: FeedbackResponseText( 'We have identified the root cause: a CSS issue with the sidebar overflow. ' 'A fix has been applied and is available in version 2.2.0, released today. ' 'Please download the latest version and confirm the sidebar scrolls correctly.' ), respondedBy: UserId.fromString('engineering-team'), isPublic: true, attachmentIds: [releaseNotesId],);
// Update status to resolved after confirmationfinal resolvePayload = UpdateFeedbackStatusPayload( feedbackId: FeedbackId.fromString('fb-2024-bug-001'), previousStatus: FeedbackStatus('in_progress'), newStatus: FeedbackStatus('resolved'), changedBy: UserId.fromString('engineering-team'), reason: 'Fix verified in production', response: null,);Querying Feedback State
Section titled “Querying Feedback State”// After applying events, query aggregate statefinal feedback = /* hydrated from event store */;
// Check lifecycle stateif (feedback.isInProgress && feedback.hasPublicResponses) { print('User has been informed of progress');}
// Analyze agingif (feedback.isStale) { print('Feedback is ${feedback.ageInDays} days old and still open - consider closing');}
// Check urgencyif (feedback.requiresUrgentAttention) { print('URGENT: ${feedback.title.value}');}
// Review responsesprint('Total responses: ${feedback.responseCount}');for (final response in feedback.publicResponses) { print(' [${response.respondedAt}] ${response.respondedBy.value}');}
// Get latest activityfinal latest = feedback.latestResponse;if (latest != null) { print('Last activity: ${feedback.daysSinceLastActivity} days ago');}Feedback Lifecycle Examples
Section titled “Feedback Lifecycle Examples”Typical Bug Fix Workflow
Section titled “Typical Bug Fix Workflow”User submits bug report (FeedbackSubmitted) ↓Support team triages (StatusChanged: new → in_review) ↓Engineering confirms issue (Response added: public) ↓Work begins on fix (StatusChanged: in_review → in_progress) ↓Fix deployed (Response added: public with release notes) ↓User confirms resolution (Response added: public acknowledgement) ↓Marked resolved (StatusChanged: in_progress → resolved) ↓Closed after 30 days inactivity (StatusChanged: resolved → closed)Feature Request Evaluation Workflow
Section titled “Feature Request Evaluation Workflow”User submits feature request (FeedbackSubmitted) ↓Product team reviews (StatusChanged: new → in_review) ↓Internal discussion (Response added: private/internal notes) ↓Decision: Either approved or declined ↓If approved: - Update status (StatusChanged: in_review → in_progress) - Provide user feedback (Response added: public)
If declined: - Update status (StatusChanged: new → rejected) - Explain rationale (Response added: public or internal)Integration Points
Section titled “Integration Points”With Contracts Domain
Section titled “With Contracts Domain”Cross-domain communication uses value objects from contracts:
FeedbackId,FeedbackTitle,FeedbackDescription: Core value objectsFeedbackType,FeedbackPriority,FeedbackCategory,FeedbackStatus: EnumerationsUserId,EmailAddress: User referencesAttachmentId: References to attachments from attachments domainBrowserInfo,DeviceInfo: Context information
With Attachments Domain
Section titled “With Attachments Domain”Feedback supports attachments at two levels:
- Feedback-level attachments: Files submitted with the initial feedback (e.g., screenshots, logs)
- Response-level attachments: Files attached to responses (e.g., patches, release notes)
With Identity Domain
Section titled “With Identity Domain”User tracking through:
submittedBy: User who submitted feedbackrespondedBy: Staff member providing responsechangedBy: User making status updates
Registration and Initialization
Section titled “Registration and Initialization”When using the feedback domain, register types for serialization:
import 'package:feedback_v1/feedback_v1.dart';
// Register all feedback domain typesFeedbackV1().registerDomainTypes();
// Or explicitly:registerFeedbackV1();This registers:
- Aggregates:
FeedbackAggregate - Events:
FeedbackSubmittedEvent,FeedbackStatusChangedEvent,FeedbackResponseAddedEvent - Directives:
SubmitFeedbackDirective,UpdateFeedbackStatusDirective,AddFeedbackResponseDirective - Payloads:
SubmitFeedbackPayload,UpdateFeedbackStatusPayload,AddFeedbackResponsePayload
Error Handling and Validation
Section titled “Error Handling and Validation”Aggregate Validation
Section titled “Aggregate Validation”The FeedbackAggregate.validate() method enforces business rules:
void validate() { // Empty aggregates are valid (used during snapshot creation) if (feedbackId == FeedbackId.unresolved && title == FeedbackTitle.unresolved) { return; }
// ID must be resolved if (feedbackId == FeedbackId.unresolved) { throw StateError('Feedback ID cannot be unresolved'); }
// Title must not be empty if (title.value.trim().isEmpty) { throw StateError('Feedback title cannot be empty'); }
// Description must not be empty if (description.value.trim().isEmpty) { throw StateError('Feedback description cannot be empty'); }
// Status must be valid const validStatuses = [ 'new', 'in_review', 'in_progress', 'resolved', 'closed', 'rejected' ]; if (!validStatuses.contains(status.value)) { throw StateError('Invalid feedback status: ${status.value}'); }
// Type must be valid const validTypes = [ 'bug_report', 'feature_request', 'general_feedback', 'issue' ]; if (!validTypes.contains(feedbackType.value)) { throw StateError('Invalid feedback type: ${feedbackType.value}'); }
// Priority must be valid const validPriorities = ['low', 'medium', 'high', 'urgent']; if (!validPriorities.contains(priority.value)) { throw StateError('Invalid priority: ${priority.value}'); }
// Category must be valid const validCategories = [ 'ui_ux', 'performance', 'functionality', 'data', 'other' ]; if (!validCategories.contains(category.value)) { throw StateError('Invalid category: ${category.value}'); }
// Resolved feedback must have at least one response if (status.value == 'resolved' && _responses.isEmpty) { throw StateError('Resolved feedback must have at least one response'); }}Common Validation Errors
Section titled “Common Validation Errors”| Error | Cause | Resolution |
|---|---|---|
| Title cannot be empty | Feedback submitted without title | Require non-empty title in UI |
| Invalid feedback type | Type not in enumerated list | Validate type against allowed values |
| Resolved feedback must have responses | Attempting to mark resolved without response | Add response before resolving |
| Invalid status transition | Status change violates workflow | Check current status before transitioning |
Testing
Section titled “Testing”The domain includes tests for lifecycle and serialization:
import 'package:test/test.dart';import 'package:feedback_v1/feedback_v1.dart';
group('FeedbackAggregate', () { test('lifecycle and status transitions', () { final id = AggregateId('fb-1'); final now = DateTime(2024);
final feedback = FeedbackAggregate( feedbackId: FeedbackId('FB-1'), title: FeedbackTitle('Test Feedback'), description: FeedbackDescription('Test Description'), feedbackType: FeedbackType('bug_report'), priority: FeedbackPriority('medium'), category: FeedbackCategory('functionality'), submittedBy: UserId('USR-1'), submittedAt: now, updatedAt: now, status: FeedbackStatus('new'), )..setIdForFramework(id);
expect(feedback.isNew, isTrue); expect(feedback.isOpen, isTrue);
// Submit for review final inReview = feedback.apply(FeedbackStatusChangedEvent( feedbackId: FeedbackId('FB-1'), previousStatus: FeedbackStatus('new'), newStatus: FeedbackStatus('in_review'), changedBy: UserId('USR-2'), changedAt: now.add(Duration(hours: 1)), ));
expect(inReview.isInReview, isTrue); expect(inReview.isOpen, isTrue);
// Add response final withResponse = inReview.apply(FeedbackResponseAddedEvent( feedbackId: FeedbackId('FB-1'), responseId: FeedbackResponseId('RESP-1'), responseText: FeedbackResponseText('We are investigating this issue'), respondedBy: UserId('SUPPORT-1'), respondedAt: now.add(Duration(hours: 2)), isPublic: true, ));
expect(withResponse.hasResponses, isTrue); expect(withResponse.responseCount, 1); expect(withResponse.canBeResolved(), isTrue);
// Mark resolved final resolved = withResponse.apply(FeedbackStatusChangedEvent( feedbackId: FeedbackId('FB-1'), previousStatus: FeedbackStatus('in_review'), newStatus: FeedbackStatus('resolved'), changedBy: UserId('SUPPORT-1'), changedAt: now.add(Duration(hours: 24)), response: 'Fix deployed in version 2.0.1', ));
expect(resolved.isResolved, isTrue); expect(resolved.isOpen, isFalse); });
test('serialization and deserialization', () { final json = { 'feedbackId': 'FB-1', 'title': 'Test Feedback', 'description': 'Test Description', 'feedbackType': 'bug_report', 'priority': 'medium', 'category': 'functionality', 'submittedBy': 'USR-1', 'submittedAt': '2024-01-01T00:00:00Z', 'updatedAt': '2024-01-01T00:00:00Z', 'status': 'new', 'responses': [], 'attachmentIds': [], 'metadata': {}, };
final feedback = FeedbackAggregate.fromJson( json, AggregateId('fb-1'), );
expect(feedback.feedbackId.value, 'FB-1'); expect(feedback.title.value, 'Test Feedback'); expect(feedback.isNew, isTrue); expect(feedback.responseCount, 0); });});Package Dependencies
Section titled “Package Dependencies”The feedback domain depends on:
-
nomos_core: Core event sourcing framework
Aggregate,Event,Directive,Payloadbase classes- Registry mechanisms for type serialization
AggregateId,AggregateBaseinfrastructure
-
contracts_v1: Cross-domain value objects
FeedbackId,FeedbackTitle,FeedbackDescription,FeedbackType,FeedbackStatusFeedbackPriority,FeedbackCategoryUserId,EmailAddress,AttachmentIdBrowserInfo,DeviceInfo
No domain-specific dependencies—designed for loose coupling and cross-domain communication.
Best Practices
Section titled “Best Practices”1. Always Validate Status Transitions
Section titled “1. Always Validate Status Transitions”// Good: Check preconditionsif (feedback.isOpen) { // Execute status update}
// Avoid: Assume state// Execute status update directly2. Provide User-Visible Responses When Resolving
Section titled “2. Provide User-Visible Responses When Resolving”// Good: User gets meaningful updatefinal response = AddFeedbackResponsePayload( feedbackId: feedbackId, responseId: responseId, responseText: FeedbackResponseText( 'Your issue has been fixed in version 2.1.0. Please update and let us know ' 'if the problem persists.' ), respondedBy: engineerUserId, isPublic: true, attachmentIds: [releaseNotesId],);
// Avoid: Resolving without user communication// Status changed to resolved without response3. Capture Context for Bug Reports
Section titled “3. Capture Context for Bug Reports”// Good: Rich context for reproductionfinal bugReport = SubmitFeedbackPayload( // ... required fields ... browserInfo: BrowserInfo('Chrome 121.0 on Windows 11'), deviceInfo: DeviceInfo('Desktop, 1920x1080'), attachmentIds: [screenshotId, errorLogId], metadata: { 'timestamp': DateTime.now().toIso8601String(), 'appVersion': '2.1.0', 'userAgent': userAgent, },);
// Avoid: Minimal context// Only title and description, no environment details4. Use Appropriate Priority Levels
Section titled “4. Use Appropriate Priority Levels”// Good: Priority matches impactif (affectsLogin) { priority = FeedbackPriority('urgent');} else if (affectsMultipleUsers) { priority = FeedbackPriority('high');} else if (workaround) { priority = FeedbackPriority('medium');}
// Avoid: All feedback marked high/urgent// Dilutes actual priorities5. Review Stale Feedback Regularly
Section titled “5. Review Stale Feedback Regularly”// Good: Close resolved feedback after periodif (feedback.isResolved && feedback.ageInDays > 30) { // Mark as closed}
// Monitor stale open feedbackif (feedback.isStale) { // Escalate or resolve}
// Avoid: Accumulating open feedback indefinitelyRelated Documentation
Section titled “Related Documentation”- Contracts Domain: Value object definitions and cross-domain types
- Attachments Domain: File attachment management and storage
- Identity Domain: User authentication and authorization
- Intents Domain: High-level user intentions that may trigger feedback workflows