@appiq/flutter-workflow 1.2.0 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,431 @@
1
+ # Additional Domain Requirements - Business Logic Extensions
2
+
3
+ This template defines additional domain layer requirements, complex business rules, and domain service integrations.
4
+
5
+ ## Advanced Business Rules
6
+
7
+ ### Complex Validation Logic
8
+ ```dart
9
+ class {Entity} extends Equatable {
10
+ final String id;
11
+ final String name;
12
+ final {EntityStatus} status;
13
+ final DateTime createdAt;
14
+ final DateTime? updatedAt;
15
+
16
+ const {Entity}({
17
+ required this.id,
18
+ required this.name,
19
+ required this.status,
20
+ required this.createdAt,
21
+ this.updatedAt,
22
+ });
23
+
24
+ // Advanced business rule validation
25
+ ValidationResult validateBusinessRules() {
26
+ final errors = <String>[];
27
+
28
+ // Custom business validation logic
29
+ if (name.trim().length < 3) {
30
+ errors.add('Name must be at least 3 characters long');
31
+ }
32
+
33
+ if (status == {EntityStatus}.archived && updatedAt == null) {
34
+ errors.add('Archived entities must have an updated timestamp');
35
+ }
36
+
37
+ // Time-based business rules
38
+ if (createdAt.isAfter(DateTime.now())) {
39
+ errors.add('Creation date cannot be in the future');
40
+ }
41
+
42
+ return ValidationResult(
43
+ isValid: errors.isEmpty,
44
+ errors: errors,
45
+ );
46
+ }
47
+
48
+ // Business logic for state transitions
49
+ {Entity} markAsCompleted() {
50
+ if (status == {EntityStatus}.completed) {
51
+ throw DomainException('Entity is already completed');
52
+ }
53
+
54
+ if (status == {EntityStatus}.archived) {
55
+ throw DomainException('Cannot complete archived entity');
56
+ }
57
+
58
+ return copyWith(
59
+ status: {EntityStatus}.completed,
60
+ updatedAt: DateTime.now(),
61
+ );
62
+ }
63
+ }
64
+ ```
65
+
66
+ ### Cross-Entity Business Rules
67
+ ```dart
68
+ class {Feature}DomainService {
69
+ // Complex business logic involving multiple entities
70
+ Future<Either<Failure, {ResultEntity}>> processComplexOperation({
71
+ required {Entity1} entity1,
72
+ required {Entity2} entity2,
73
+ required List<{Entity3}> entities3,
74
+ }) async {
75
+ // Validate cross-entity business rules
76
+ final validation = _validateCrossEntityRules(entity1, entity2, entities3);
77
+ if (!validation.isValid) {
78
+ return Left(ValidationFailure(validation.errors.join(', ')));
79
+ }
80
+
81
+ // Execute complex business logic
82
+ try {
83
+ final result = _executeBusinessLogic(entity1, entity2, entities3);
84
+ return Right(result);
85
+ } catch (e) {
86
+ return Left(DomainFailure('Business operation failed: ${e.toString()}'));
87
+ }
88
+ }
89
+
90
+ ValidationResult _validateCrossEntityRules(
91
+ {Entity1} entity1,
92
+ {Entity2} entity2,
93
+ List<{Entity3}> entities3,
94
+ ) {
95
+ final errors = <String>[];
96
+
97
+ // Example: Entity1 and Entity2 compatibility check
98
+ if (entity1.type != entity2.compatibleType) {
99
+ errors.add('Entity1 type is not compatible with Entity2');
100
+ }
101
+
102
+ // Example: Quantity validation across entities
103
+ final totalQuantity = entities3.fold(0, (sum, e) => sum + e.quantity);
104
+ if (totalQuantity > entity1.maxCapacity) {
105
+ errors.add('Total quantity exceeds maximum capacity');
106
+ }
107
+
108
+ // Example: Status consistency check
109
+ if (entity1.isActive && entities3.any((e) => !e.isValid)) {
110
+ errors.add('Active Entity1 cannot contain invalid Entity3 items');
111
+ }
112
+
113
+ return ValidationResult(
114
+ isValid: errors.isEmpty,
115
+ errors: errors,
116
+ );
117
+ }
118
+ }
119
+ ```
120
+
121
+ ## Advanced Use Cases
122
+
123
+ ### Workflow Management Use Case
124
+ ```dart
125
+ class Manage{Feature}WorkflowUseCase {
126
+ final {Feature}Repository repository;
127
+ final {Feature}DomainService domainService;
128
+ final NotificationService notificationService;
129
+
130
+ const Manage{Feature}WorkflowUseCase({
131
+ required this.repository,
132
+ required this.domainService,
133
+ required this.notificationService,
134
+ });
135
+
136
+ Future<Either<Failure, WorkflowResult>> execute({
137
+ required String workflowId,
138
+ required WorkflowAction action,
139
+ required Map<String, dynamic> context,
140
+ }) async {
141
+ try {
142
+ // Load current workflow state
143
+ final workflowResult = await repository.getWorkflow(workflowId);
144
+ if (workflowResult.isLeft()) {
145
+ return workflowResult.fold((l) => Left(l), (r) => throw Exception());
146
+ }
147
+
148
+ final workflow = workflowResult.getOrElse(() => throw Exception());
149
+
150
+ // Validate action is allowed in current state
151
+ final validationResult = domainService.validateWorkflowTransition(
152
+ workflow: workflow,
153
+ action: action,
154
+ context: context,
155
+ );
156
+
157
+ if (!validationResult.isValid) {
158
+ return Left(ValidationFailure(validationResult.errors.join(', ')));
159
+ }
160
+
161
+ // Execute workflow transition
162
+ final updatedWorkflow = domainService.executeWorkflowTransition(
163
+ workflow: workflow,
164
+ action: action,
165
+ context: context,
166
+ );
167
+
168
+ // Persist updated workflow
169
+ final saveResult = await repository.saveWorkflow(updatedWorkflow);
170
+ if (saveResult.isLeft()) {
171
+ return saveResult.fold((l) => Left(l), (r) => throw Exception());
172
+ }
173
+
174
+ // Send notifications if required
175
+ if (updatedWorkflow.requiresNotification) {
176
+ await notificationService.sendWorkflowNotification(updatedWorkflow);
177
+ }
178
+
179
+ return Right(WorkflowResult(
180
+ workflow: updatedWorkflow,
181
+ previousState: workflow.state,
182
+ newState: updatedWorkflow.state,
183
+ executedAt: DateTime.now(),
184
+ ));
185
+
186
+ } catch (e) {
187
+ return Left(DomainFailure('Workflow execution failed: ${e.toString()}'));
188
+ }
189
+ }
190
+ }
191
+ ```
192
+
193
+ ### Batch Processing Use Case
194
+ ```dart
195
+ class BatchProcess{Feature}UseCase {
196
+ final {Feature}Repository repository;
197
+ final {Feature}DomainService domainService;
198
+
199
+ Future<Either<Failure, BatchResult>> execute({
200
+ required List<String> entityIds,
201
+ required BatchOperation operation,
202
+ required Map<String, dynamic> parameters,
203
+ }) async {
204
+ final results = <String, Either<Failure, {Entity}>>[];
205
+ final errors = <String>[];
206
+
207
+ // Process entities in batches to manage memory
208
+ const batchSize = 50;
209
+ for (int i = 0; i < entityIds.length; i += batchSize) {
210
+ final batch = entityIds.skip(i).take(batchSize).toList();
211
+
212
+ // Load batch entities
213
+ final entitiesResult = await repository.getEntitiesByIds(batch);
214
+ if (entitiesResult.isLeft()) {
215
+ errors.add('Failed to load batch ${i ~/ batchSize + 1}');
216
+ continue;
217
+ }
218
+
219
+ final entities = entitiesResult.getOrElse(() => []);
220
+
221
+ // Process each entity in the batch
222
+ for (final entity in entities) {
223
+ try {
224
+ final processedEntity = await _processEntity(entity, operation, parameters);
225
+ results[entity.id] = Right(processedEntity);
226
+ } catch (e) {
227
+ results[entity.id] = Left(DomainFailure(e.toString()));
228
+ errors.add('Failed to process entity ${entity.id}: $e');
229
+ }
230
+ }
231
+ }
232
+
233
+ return Right(BatchResult(
234
+ totalProcessed: results.length,
235
+ successCount: results.values.where((r) => r.isRight()).length,
236
+ errorCount: results.values.where((r) => r.isLeft()).length,
237
+ results: results,
238
+ errors: errors,
239
+ ));
240
+ }
241
+ }
242
+ ```
243
+
244
+ ## Domain Events System
245
+
246
+ ### Domain Event Definition
247
+ ```dart
248
+ abstract class DomainEvent extends Equatable {
249
+ final String eventId;
250
+ final DateTime occurredAt;
251
+ final String aggregateId;
252
+ final int aggregateVersion;
253
+
254
+ const DomainEvent({
255
+ required this.eventId,
256
+ required this.occurredAt,
257
+ required this.aggregateId,
258
+ required this.aggregateVersion,
259
+ });
260
+ }
261
+
262
+ class {Entity}CreatedEvent extends DomainEvent {
263
+ final {Entity} entity;
264
+
265
+ const {Entity}CreatedEvent({
266
+ required this.entity,
267
+ required String eventId,
268
+ required DateTime occurredAt,
269
+ required String aggregateId,
270
+ required int aggregateVersion,
271
+ }) : super(
272
+ eventId: eventId,
273
+ occurredAt: occurredAt,
274
+ aggregateId: aggregateId,
275
+ aggregateVersion: aggregateVersion,
276
+ );
277
+
278
+ @override
279
+ List<Object> get props => [entity, eventId, occurredAt, aggregateId, aggregateVersion];
280
+ }
281
+ ```
282
+
283
+ ### Aggregate Root with Events
284
+ ```dart
285
+ abstract class AggregateRoot<T> extends Equatable {
286
+ final String id;
287
+ final int version;
288
+ final List<DomainEvent> _domainEvents = [];
289
+
290
+ AggregateRoot({
291
+ required this.id,
292
+ required this.version,
293
+ });
294
+
295
+ List<DomainEvent> get domainEvents => List.unmodifiable(_domainEvents);
296
+
297
+ void addDomainEvent(DomainEvent event) {
298
+ _domainEvents.add(event);
299
+ }
300
+
301
+ void clearDomainEvents() {
302
+ _domainEvents.clear();
303
+ }
304
+
305
+ void markEventsAsCommitted() {
306
+ _domainEvents.clear();
307
+ }
308
+ }
309
+
310
+ class {Entity}Aggregate extends AggregateRoot<{Entity}> {
311
+ final {Entity} entity;
312
+
313
+ {Entity}Aggregate({
314
+ required this.entity,
315
+ required String id,
316
+ required int version,
317
+ }) : super(id: id, version: version);
318
+
319
+ {Entity}Aggregate update{Entity}({
320
+ required String name,
321
+ required {EntityStatus} status,
322
+ }) {
323
+ final updatedEntity = entity.copyWith(
324
+ name: name,
325
+ status: status,
326
+ updatedAt: DateTime.now(),
327
+ );
328
+
329
+ // Add domain event
330
+ addDomainEvent({Entity}UpdatedEvent(
331
+ entity: updatedEntity,
332
+ previousEntity: entity,
333
+ eventId: const Uuid().v4(),
334
+ occurredAt: DateTime.now(),
335
+ aggregateId: id,
336
+ aggregateVersion: version + 1,
337
+ ));
338
+
339
+ return {Entity}Aggregate(
340
+ entity: updatedEntity,
341
+ id: id,
342
+ version: version + 1,
343
+ );
344
+ }
345
+
346
+ @override
347
+ List<Object> get props => [entity, id, version];
348
+ }
349
+ ```
350
+
351
+ ## Advanced Repository Patterns
352
+
353
+ ### Specification Pattern for Complex Queries
354
+ ```dart
355
+ abstract class Specification<T> {
356
+ bool isSatisfiedBy(T candidate);
357
+ Specification<T> and(Specification<T> other);
358
+ Specification<T> or(Specification<T> other);
359
+ Specification<T> not();
360
+ }
361
+
362
+ class {Entity}ActiveSpecification extends Specification<{Entity}> {
363
+ @override
364
+ bool isSatisfiedBy({Entity} candidate) {
365
+ return candidate.status == {EntityStatus}.active;
366
+ }
367
+ }
368
+
369
+ class {Entity}CreatedAfterSpecification extends Specification<{Entity}> {
370
+ final DateTime date;
371
+
372
+ {Entity}CreatedAfterSpecification(this.date);
373
+
374
+ @override
375
+ bool isSatisfiedBy({Entity} candidate) {
376
+ return candidate.createdAt.isAfter(date);
377
+ }
378
+ }
379
+
380
+ // Extended repository interface
381
+ abstract class {Feature}Repository {
382
+ Future<Either<Failure, List<{Entity}>>> findBySpecification(
383
+ Specification<{Entity}> specification,
384
+ );
385
+
386
+ Future<Either<Failure, PaginatedResult<{Entity}>>> findPaginatedBySpecification(
387
+ Specification<{Entity}> specification,
388
+ PaginationParams pagination,
389
+ );
390
+ }
391
+ ```
392
+
393
+ ## Integration Requirements Checklist
394
+
395
+ ### Business Logic Complexity
396
+ - [ ] Complex validation rules implemented
397
+ - [ ] Cross-entity business rules defined
398
+ - [ ] State transition logic validated
399
+ - [ ] Business invariants enforced
400
+ - [ ] Domain services for complex operations
401
+
402
+ ### Use Case Extensions
403
+ - [ ] Workflow management use cases
404
+ - [ ] Batch processing capabilities
405
+ - [ ] Transaction management
406
+ - [ ] Event-driven architecture
407
+ - [ ] Saga pattern implementation (if needed)
408
+
409
+ ### Domain Events
410
+ - [ ] Domain event definitions
411
+ - [ ] Aggregate root with event support
412
+ - [ ] Event publishing mechanism
413
+ - [ ] Event handlers for side effects
414
+ - [ ] Event store integration (if needed)
415
+
416
+ ### Advanced Patterns
417
+ - [ ] Specification pattern for queries
418
+ - [ ] Domain service implementations
419
+ - [ ] Value object validations
420
+ - [ ] Repository extensions
421
+ - [ ] Unit of work pattern (if needed)
422
+
423
+ ### Performance Considerations
424
+ - [ ] Batch processing for large datasets
425
+ - [ ] Lazy loading strategies
426
+ - [ ] Caching mechanisms
427
+ - [ ] Query optimization patterns
428
+ - [ ] Memory management for large aggregates
429
+
430
+ ## Notes
431
+ Add any feature-specific domain requirements, complex business rules, or architectural patterns needed for your application.
@@ -0,0 +1,205 @@
1
+ # Additional UI Requirements - Role-Based Access Control
2
+
3
+ This template defines role-specific UI implementations and access controls for your feature.
4
+
5
+ ## Role Definitions
6
+
7
+ ### Admin Role
8
+ **Access Level**: Full system access
9
+ **UI Elements**:
10
+ - [ ] Admin dashboard with system statistics
11
+ - [ ] User management interface (create, edit, delete users)
12
+ - [ ] System configuration panels
13
+ - [ ] Advanced analytics and reporting
14
+ - [ ] Role assignment interface
15
+ - [ ] Security logs and audit trails
16
+
17
+ **Restricted Elements**:
18
+ - None - full access
19
+
20
+ ### Manager Role
21
+ **Access Level**: Department/team management access
22
+ **UI Elements**:
23
+ - [ ] Team dashboard with team metrics
24
+ - [ ] Team member management (view, edit team members)
25
+ - [ ] Department-specific reporting
26
+ - [ ] Approval workflows interface
27
+ - [ ] Team scheduling and task assignment
28
+
29
+ **Restricted Elements**:
30
+ - [ ] System-wide configuration (admin only)
31
+ - [ ] User role changes (admin only)
32
+ - [ ] Global system analytics (admin only)
33
+
34
+ ### User Role
35
+ **Access Level**: Standard user access
36
+ **UI Elements**:
37
+ - [ ] Personal dashboard with user-specific data
38
+ - [ ] Profile management (edit own profile)
39
+ - [ ] Task list and personal workflows
40
+ - [ ] Basic reporting (own data only)
41
+ - [ ] Standard feature access
42
+
43
+ **Restricted Elements**:
44
+ - [ ] User management (manager/admin only)
45
+ - [ ] System configuration (admin only)
46
+ - [ ] Other users' data (unless shared)
47
+ - [ ] Administrative functions
48
+
49
+ ### Guest Role
50
+ **Access Level**: Limited read-only access
51
+ **UI Elements**:
52
+ - [ ] Public information display
53
+ - [ ] Basic feature demonstration
54
+ - [ ] Registration/login prompts
55
+ - [ ] Public content consumption
56
+
57
+ **Restricted Elements**:
58
+ - [ ] Any personal data access
59
+ - [ ] Modification capabilities
60
+ - [ ] Internal system information
61
+
62
+ ## Screen-Specific Access Control
63
+
64
+ ### Login/Authentication Screens
65
+ ```dart
66
+ // Role-based redirect after authentication
67
+ switch (userRole) {
68
+ case UserRole.admin:
69
+ // Navigate to admin dashboard
70
+ break;
71
+ case UserRole.manager:
72
+ // Navigate to manager dashboard
73
+ break;
74
+ case UserRole.user:
75
+ // Navigate to user dashboard
76
+ break;
77
+ case UserRole.guest:
78
+ // Navigate to public content
79
+ break;
80
+ }
81
+ ```
82
+
83
+ ### Dashboard Implementation
84
+ - **Admin Dashboard**: System overview, all metrics, user management
85
+ - **Manager Dashboard**: Team metrics, team management, approvals
86
+ - **User Dashboard**: Personal metrics, tasks, profile
87
+ - **Guest Dashboard**: Public information, registration prompts
88
+
89
+ ### Navigation Structure
90
+ ```yaml
91
+ Admin Navigation:
92
+ - Dashboard
93
+ - User Management
94
+ - System Settings
95
+ - Reports & Analytics
96
+ - Security & Logs
97
+
98
+ Manager Navigation:
99
+ - Team Dashboard
100
+ - Team Management
101
+ - Team Reports
102
+ - Approvals
103
+ - Schedule
104
+
105
+ User Navigation:
106
+ - Personal Dashboard
107
+ - My Tasks
108
+ - My Profile
109
+ - My Reports
110
+
111
+ Guest Navigation:
112
+ - Public Content
113
+ - Features Overview
114
+ - Login/Register
115
+ ```
116
+
117
+ ## UI Component Visibility Rules
118
+
119
+ ### Conditional Widget Display
120
+ ```dart
121
+ // Example role-based widget visibility
122
+ if (userRole == UserRole.admin || userRole == UserRole.manager) {
123
+ // Show management tools
124
+ return ManagementToolsWidget();
125
+ }
126
+
127
+ if (userRole != UserRole.guest) {
128
+ // Show authenticated user content
129
+ return AuthenticatedContentWidget();
130
+ }
131
+
132
+ // Show guest content
133
+ return GuestContentWidget();
134
+ ```
135
+
136
+ ### Button/Action Availability
137
+ - **Create**: Admin, Manager (for their scope)
138
+ - **Edit**: Admin, Manager (for their scope), User (own data)
139
+ - **Delete**: Admin, Manager (for their scope), User (own data, limited)
140
+ - **View**: Based on data ownership and role permissions
141
+
142
+ ## Data Access Patterns
143
+
144
+ ### API Endpoint Access by Role
145
+ ```yaml
146
+ Admin Endpoints:
147
+ - /api/admin/* (all admin functions)
148
+ - /api/users/* (all user data)
149
+ - /api/system/* (system configuration)
150
+
151
+ Manager Endpoints:
152
+ - /api/teams/{teamId}/* (team data)
153
+ - /api/users/team/{teamId} (team members)
154
+ - /api/reports/team/{teamId} (team reports)
155
+
156
+ User Endpoints:
157
+ - /api/users/{userId} (own profile)
158
+ - /api/tasks/user/{userId} (own tasks)
159
+ - /api/reports/user/{userId} (own reports)
160
+
161
+ Guest Endpoints:
162
+ - /api/public/* (public information)
163
+ - /api/auth/* (authentication)
164
+ ```
165
+
166
+ ## Error Handling for Unauthorized Access
167
+
168
+ ### Permission Denied Screens
169
+ - **Graceful Degradation**: Show alternative content instead of errors
170
+ - **Clear Messaging**: Explain why access is restricted
171
+ - **Upgrade Prompts**: Suggest role upgrades where appropriate
172
+ - **Alternative Actions**: Provide allowed alternatives
173
+
174
+ ### Security Considerations
175
+ - [ ] Never expose role checking logic in client-side code
176
+ - [ ] Always validate permissions on backend
177
+ - [ ] Log unauthorized access attempts
178
+ - [ ] Implement session timeout for sensitive roles
179
+ - [ ] Use secure token-based authentication
180
+
181
+ ## Implementation Checklist
182
+
183
+ ### UI Implementation
184
+ - [ ] Role-based navigation menus implemented
185
+ - [ ] Conditional widget rendering based on roles
186
+ - [ ] Role-specific dashboard layouts created
187
+ - [ ] Permission-based button/action visibility
188
+ - [ ] Graceful handling of unauthorized access
189
+
190
+ ### Security Implementation
191
+ - [ ] Backend permission validation for all endpoints
192
+ - [ ] Secure role assignment and validation
193
+ - [ ] Session management with appropriate timeouts
194
+ - [ ] Audit logging for role-based actions
195
+ - [ ] Regular security review of role permissions
196
+
197
+ ### Testing
198
+ - [ ] Unit tests for role-based UI logic
199
+ - [ ] Integration tests for role-based workflows
200
+ - [ ] Security testing for unauthorized access attempts
201
+ - [ ] User experience testing for each role
202
+ - [ ] Performance testing with role-based data loading
203
+
204
+ ## Notes
205
+ Add any additional role-specific requirements, custom permissions, or special access patterns needed for your application.