@dv.nghiem/flowdeck 0.2.4 → 0.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.
Files changed (74) hide show
  1. package/README.md +24 -41
  2. package/dist/hooks/memory-hook.d.ts +21 -0
  3. package/dist/hooks/memory-hook.d.ts.map +1 -0
  4. package/dist/hooks/orchestrator-guard-hook.d.ts.map +1 -1
  5. package/dist/hooks/todo-hook.d.ts +1 -7
  6. package/dist/hooks/todo-hook.d.ts.map +1 -1
  7. package/dist/index.d.ts.map +1 -1
  8. package/dist/index.js +709 -420
  9. package/dist/services/memory-store.d.ts +40 -0
  10. package/dist/services/memory-store.d.ts.map +1 -0
  11. package/dist/tools/memory-search.d.ts +3 -0
  12. package/dist/tools/memory-search.d.ts.map +1 -0
  13. package/docs/commands/fd-doctor.md +21 -0
  14. package/docs/commands/fd-quick.md +33 -0
  15. package/docs/commands/fd-reflect.md +23 -0
  16. package/docs/commands/fd-status.md +31 -0
  17. package/docs/commands/fd-translate-intent.md +17 -0
  18. package/docs/commands.md +209 -271
  19. package/docs/configuration.md +1 -2
  20. package/docs/index.md +22 -28
  21. package/docs/memory.md +69 -0
  22. package/docs/quick-start.md +1 -1
  23. package/package.json +1 -1
  24. package/src/commands/fd-deploy-check.md +131 -11
  25. package/src/commands/fd-new-project.md +14 -1
  26. package/src/commands/fd-quick.md +60 -0
  27. package/src/commands/fd-reflect.md +41 -2
  28. package/src/commands/fd-status.md +84 -0
  29. package/src/rules/README.md +8 -7
  30. package/src/skills/agent-harness-construction/SKILL.md +227 -0
  31. package/src/skills/api-design/SKILL.md +5 -0
  32. package/src/skills/backend-patterns/SKILL.md +105 -0
  33. package/src/skills/clean-architecture/SKILL.md +85 -0
  34. package/src/skills/cqrs/SKILL.md +230 -0
  35. package/src/skills/ddd-architecture/SKILL.md +104 -0
  36. package/src/skills/django-patterns/SKILL.md +304 -0
  37. package/src/skills/django-tdd/SKILL.md +297 -0
  38. package/src/skills/event-driven-architecture/SKILL.md +152 -0
  39. package/src/skills/frontend-pattern/SKILL.md +159 -0
  40. package/src/skills/hexagonal-architecture/SKILL.md +80 -0
  41. package/src/skills/layered-architecture/SKILL.md +64 -0
  42. package/src/skills/postgres-patterns/SKILL.md +74 -0
  43. package/src/skills/python-patterns/SKILL.md +5 -0
  44. package/src/skills/saga-architecture/SKILL.md +113 -0
  45. package/dist/tools/run-parallel.d.ts +0 -4
  46. package/dist/tools/run-parallel.d.ts.map +0 -1
  47. package/docs/command-migration.md +0 -175
  48. package/docs/commands/fd-analyze-change.md +0 -107
  49. package/docs/commands/fd-dashboard.md +0 -11
  50. package/docs/commands/fd-evaluate-risk.md +0 -134
  51. package/docs/commands/fd-guarded-edit.md +0 -105
  52. package/docs/commands/fd-progress.md +0 -11
  53. package/docs/commands/fd-review-code.md +0 -29
  54. package/docs/commands/fd-roadmap.md +0 -10
  55. package/docs/commands/fd-settings.md +0 -10
  56. package/docs/parallel-execution.md +0 -255
  57. package/src/commands/fd-analyze-change.md +0 -57
  58. package/src/commands/fd-approve.md +0 -64
  59. package/src/commands/fd-blast-radius.md +0 -49
  60. package/src/commands/fd-dashboard.md +0 -57
  61. package/src/commands/fd-evaluate-risk.md +0 -62
  62. package/src/commands/fd-guarded-edit.md +0 -69
  63. package/src/commands/fd-impact-radar.md +0 -51
  64. package/src/commands/fd-learn.md +0 -36
  65. package/src/commands/fd-progress.md +0 -50
  66. package/src/commands/fd-regression-predict.md +0 -57
  67. package/src/commands/fd-review-code.md +0 -96
  68. package/src/commands/fd-review-route.md +0 -54
  69. package/src/commands/fd-roadmap.md +0 -46
  70. package/src/commands/fd-settings.md +0 -57
  71. package/src/commands/fd-test-gap.md +0 -54
  72. package/src/commands/fd-volatility-map.md +0 -64
  73. package/src/commands/fd-workspace-status.md +0 -34
  74. package/src/skills/parallel-execute/SKILL.md +0 -92
@@ -0,0 +1,227 @@
1
+ ---
2
+ name: agent-harness-construction
3
+ description: Build autonomous agent pipelines — construct agent loops, wire multi-agent orchestration, implement self-healing retry logic, and measure agent effectiveness
4
+ origin: FlowDeck
5
+ ---
6
+
7
+ # Agent Harness Construction Skill
8
+
9
+ Constructs autonomous agent pipelines that can plan, execute, self-correct, and measure their own effectiveness.
10
+
11
+ ## When to Activate
12
+
13
+ Activate when:
14
+ - Building multi-agent orchestration systems
15
+ - Implementing autonomous loops (self-correcting agents)
16
+ - Designing agent retry and self-healing policies
17
+ - Wiring agent-to-agent communication
18
+ - Measuring and optimizing agent effectiveness
19
+
20
+ ## Agent Loop Architecture
21
+
22
+ ### Core Loop Pattern
23
+
24
+ ```
25
+ ┌─────────────────────────────────────────────┐
26
+ │ 1. OBSERVE → Gather context state │
27
+ │ 2. THINK → Plan next action │
28
+ │ 3. ACT → Execute tool call │
29
+ │ 4. EVALUATE → Check result quality │
30
+ │ 5. ADAPT → Retry or proceed │
31
+ └─────────────────────────────────────────────┘
32
+ ```
33
+
34
+ ```typescript
35
+ interface AgentLoop {
36
+ observe: () => Promise<Context>;
37
+ think: (ctx: Context) => Promise<Plan>;
38
+ act: (plan: Plan) => Promise<Result>;
39
+ evaluate: (result: Result) => Evaluation;
40
+ adapt: (evaluation: Evaluation) => 'continue' | 'retry' | 'complete';
41
+ }
42
+ ```
43
+
44
+ ### Self-Healing Retry Logic
45
+
46
+ ```typescript
47
+ async function withRetry<T>(
48
+ fn: () => Promise<T>,
49
+ options: {
50
+ maxAttempts?: number;
51
+ backoff?: 'linear' | 'exponential';
52
+ onRetry?: (attempt: number, error: Error) => void;
53
+ } = {}
54
+ ): Promise<T> {
55
+ const { maxAttempts = 3, backoff = 'exponential', onRetry } = options;
56
+
57
+ for (let attempt = 1; attempt <= maxAttempts; attempt++) {
58
+ try {
59
+ return await fn();
60
+ } catch (error) {
61
+ if (attempt === maxAttempts) throw error;
62
+ const delay = backoff === 'exponential'
63
+ ? Math.pow(2, attempt - 1) * 1000
64
+ : attempt * 1000;
65
+ onRetry?.(attempt, error as Error);
66
+ await sleep(delay);
67
+ }
68
+ }
69
+ throw new Error('unreachable');
70
+ }
71
+ ```
72
+
73
+ ## Multi-Agent Orchestration
74
+
75
+ ### Supervisor Pattern
76
+
77
+ ```typescript
78
+ interface AgentMessage {
79
+ from: string;
80
+ to: string;
81
+ type: 'request' | 'response' | 'delegate' | 'result';
82
+ payload: unknown;
83
+ traceId: string;
84
+ }
85
+
86
+ class SupervisorAgent {
87
+ private agents: Map<string, Agent>;
88
+ private messageQueue: AgentMessage[] = [];
89
+
90
+ async delegate(task: Task, targetAgent: string): Promise<Result> {
91
+ const message: AgentMessage = {
92
+ from: this.id,
93
+ to: targetAgent,
94
+ type: 'delegate',
95
+ payload: task,
96
+ traceId: generateTraceId(),
97
+ };
98
+ return this.sendAndWait(message);
99
+ }
100
+
101
+ async parallelDelegate(tasks: Task[], agents: string[]): Promise<Result[]> {
102
+ return Promise.all(
103
+ tasks.map((task, i) => this.delegate(task, agents[i % agents.length]))
104
+ );
105
+ }
106
+ }
107
+ ```
108
+
109
+ ### Council Pattern
110
+
111
+ Multiple agents deliberate and vote on a decision:
112
+
113
+ ```typescript
114
+ interface CouncilMember {
115
+ id: string;
116
+ specialty: 'security' | 'performance' | 'correctness';
117
+ vote: (proposal: Proposal) => Promise<Vote>;
118
+ }
119
+
120
+ interface CouncilDecision {
121
+ votes: Vote[];
122
+ decision: 'approve' | 'reject' | 'revise';
123
+ consensus: number; // 0-1
124
+ }
125
+
126
+ async function councilDeliberate(
127
+ proposal: Proposal,
128
+ members: CouncilMember[]
129
+ ): Promise<CouncilDecision> {
130
+ const votes = await Promise.all(members.map(m => m.vote(proposal)));
131
+ const approvals = votes.filter(v => v.approve).length;
132
+ const consensus = approvals / votes.length;
133
+
134
+ return {
135
+ votes,
136
+ decision: consensus >= 0.7 ? 'approve' : consensus >= 0.4 ? 'revise' : 'reject',
137
+ consensus,
138
+ };
139
+ }
140
+ ```
141
+
142
+ ## Agent Effectiveness Measurement
143
+
144
+ ### Trace-Based Metrics
145
+
146
+ ```typescript
147
+ interface AgentTrace {
148
+ traceId: string;
149
+ agentId: string;
150
+ spans: {
151
+ name: string;
152
+ startTime: number;
153
+ endTime: number;
154
+ success: boolean;
155
+ tokensUsed?: number;
156
+ error?: string;
157
+ }[];
158
+ outcome: 'success' | 'failure' | 'timeout';
159
+ }
160
+
161
+ // Track effectiveness
162
+ function measureAgentEffectiveness(traces: AgentTrace[]): AgentMetrics {
163
+ return {
164
+ successRate: traces.filter(t => t.outcome === 'success').length / traces.length,
165
+ avgDuration: traces.reduce((sum, t) => {
166
+ const duration = t.spans[t.spans.length - 1].endTime - t.spans[0].startTime;
167
+ return sum + duration;
168
+ }, 0) / traces.length,
169
+ avgTokensPerTask: traces.reduce((sum, t) =>
170
+ sum + (t.spans.reduce((s, span) => s + (span.tokensUsed ?? 0), 0) / t.spans.length), 0
171
+ ) / traces.length,
172
+ retryRate: traces.filter(t => t.spans.some(s => s.name === 'retry')).length / traces.length,
173
+ };
174
+ }
175
+ ```
176
+
177
+ ## Error Classification
178
+
179
+ ```typescript
180
+ type ErrorCategory =
181
+ | 'transient' // Network blip, timeout — retry eligible
182
+ | 'recoverable' // Missing context, bad input — can fix with adaptation
183
+ | 'fatal'; // Auth failure, permission — cannot proceed
184
+
185
+ function classifyError(error: Error): ErrorCategory {
186
+ if (error.message.includes('timeout') || error.message.includes('ECONNREFUSED')) {
187
+ return 'transient';
188
+ }
189
+ if (error.message.includes('invalid input') || error.message.includes('missing context')) {
190
+ return 'recoverable';
191
+ }
192
+ return 'fatal';
193
+ }
194
+ ```
195
+
196
+ ## Self-Healing Policies
197
+
198
+ ```typescript
199
+ interface HealingPolicy {
200
+ trigger: (error: Error) => boolean;
201
+ action: (context: AgentContext) => Promise<Action>;
202
+ }
203
+
204
+ const healingPolicies: HealingPolicy[] = [
205
+ {
206
+ trigger: (e) => e.message.includes('rate limit'),
207
+ action: async (ctx) => {
208
+ ctx.throttleDelay = Math.min(ctx.throttleDelay * 2, 60000);
209
+ return { type: 'backoff', delay: ctx.throttleDelay };
210
+ },
211
+ },
212
+ {
213
+ trigger: (e) => e.message.includes('context too long'),
214
+ action: async (ctx) => {
215
+ ctx.summarizeOlderHistory();
216
+ return { type: 'compact' };
217
+ },
218
+ },
219
+ ];
220
+ ```
221
+
222
+ ## Related Skills
223
+
224
+ - [self-healing-policies](self-healing-policies) — Define recovery policies
225
+ - [agent-introspection-debugging](agent-introspection-debugging) — Debug agent issues
226
+ - [eval-harness](eval-harness) — Evaluate agent performance
227
+ - [continuous-agent-loop](continuous-agent-loop) — Maintain persistent agent sessions
@@ -141,3 +141,8 @@ X-RateLimit-Reset: 1609459200
141
141
  ```
142
142
 
143
143
  Return 429 when limit exceeded with `Retry-After` header.
144
+
145
+ ## Related Skills
146
+ - backend-patterns
147
+ - postgres-patterns
148
+ - api-connector-builder
@@ -0,0 +1,105 @@
1
+ # backend-patterns
2
+
3
+ ## When to Activate
4
+ When implementing backend services, APIs, or server-side logic. Use when designing service layers, data access patterns, or middleware.
5
+
6
+ ## Steps
7
+ 1. **Identify the service layer** - Determine if you need a service layer to orchestrate business logic
8
+ 2. **Apply Repository Pattern** - Encapsulate data access behind repository interfaces for testability
9
+ 3. **Use Dependency Injection** - Pass dependencies explicitly rather than creating them inside classes
10
+ 4. **Implement error handling** - Add comprehensive error handling with appropriate HTTP status codes
11
+ 5. **Add middleware/logging** - Log requests, responses, and errors consistently
12
+
13
+ ## Examples
14
+
15
+ ```typescript
16
+ // Service Layer with Repository Pattern
17
+ interface UserRepository {
18
+ findById(id: string): Promise<User | null>;
19
+ findAll(filter?: UserFilter): Promise<User[]>;
20
+ create(attributes: CreateUserDTO): Promise<User>;
21
+ update(id: string, attributes: UpdateUserDTO): Promise<User>;
22
+ delete(id: string): Promise<void>;
23
+ }
24
+
25
+ class UserService {
26
+ constructor(private readonly userRepository: UserRepository) {}
27
+
28
+ async getUser(id: string): Promise<User> {
29
+ const user = await this.userRepository.findById(id);
30
+ if (!user) {
31
+ throw new NotFoundError(`User with id ${id} not found`);
32
+ }
33
+ return user;
34
+ }
35
+
36
+ async createUser(dto: CreateUserDTO): Promise<User> {
37
+ const existing = await this.userRepository.findByEmail(dto.email);
38
+ if (existing) {
39
+ throw new ConflictError('User with this email already exists');
40
+ }
41
+ return this.userRepository.create(dto);
42
+ }
43
+ }
44
+
45
+ // Dependency Injection Container
46
+ const container = new Container();
47
+ container.register('userRepository', () => new PostgresUserRepository());
48
+ container.register('userService', () => new UserService(container.resolve('userRepository')));
49
+ ```
50
+
51
+ ```typescript
52
+ // Error Handling with Custom Exceptions
53
+ class AppError extends Error {
54
+ constructor(
55
+ message: string,
56
+ public readonly code: string,
57
+ public readonly statusCode: number = 500
58
+ ) {
59
+ super(message);
60
+ this.name = 'AppError';
61
+ }
62
+ }
63
+
64
+ class NotFoundError extends AppError {
65
+ constructor(message: string) {
66
+ super(message, 'NOT_FOUND', 404);
67
+ }
68
+ }
69
+
70
+ class ValidationError extends AppError {
71
+ constructor(
72
+ message: string,
73
+ public readonly details?: Record<string, string[]>
74
+ ) {
75
+ super(message, 'VALIDATION_ERROR', 422);
76
+ }
77
+ }
78
+
79
+ // Global Error Handler Middleware
80
+ function errorHandler(err: Error, req: Request, res: Response, next: NextFunction) {
81
+ if (err instanceof AppError) {
82
+ return res.status(err.statusCode).json({
83
+ error: {
84
+ code: err.code,
85
+ message: err.message,
86
+ details: err instanceof ValidationError ? err.details : undefined,
87
+ },
88
+ });
89
+ }
90
+ console.error('Unhandled error:', err);
91
+ return res.status(500).json({
92
+ error: {
93
+ code: 'INTERNAL_ERROR',
94
+ message: 'An unexpected error occurred',
95
+ },
96
+ });
97
+ }
98
+ ```
99
+
100
+ ## Related Skills
101
+ - api-design
102
+ - postgres-patterns
103
+ - python-patterns
104
+ - layered-architecture
105
+ - ddd-architecture
@@ -0,0 +1,85 @@
1
+ # clean-architecture
2
+
3
+ ## When to Activate
4
+ When designing or implementing a new feature or service that needs clear separation of concerns, testability, and independence from frameworks, databases, or UI libraries.
5
+
6
+ ## Steps
7
+ 1. **Identify the core business logic** - Determine the essential domain rules that would exist even if the application had no UI, database, or external services.
8
+ 2. **Define the boundary** - Draw a clear boundary between the inner circles (entities, use cases) and outer circles (interfaces, infrastructure).
9
+ 3. **Place dependencies pointing inward** - Dependencies should always point toward the center. The inner circle knows nothing about the outer circle.
10
+ 4. **Define ports (interfaces)** - Create interfaces in the domain layer that define how the outside world can interact with it.
11
+ 5. **Implement adapters** - Create concrete implementations (adapters) for databases, web frameworks, external APIs, etc. in the outer layers.
12
+ 6. **Wire everything via dependency injection** - Use a composition root or DI container to assemble the application.
13
+
14
+ ## Examples
15
+ ```typescript
16
+ // Domain Layer - Enterprise Business Rules (innermost circle)
17
+ class Order {
18
+ constructor(
19
+ private readonly id: string,
20
+ private readonly items: OrderItem[],
21
+ private readonly status: OrderStatus
22
+ ) {}
23
+
24
+ get total(): number {
25
+ return this.items.reduce((sum, item) => sum + item.price * item.quantity, 0)
26
+ }
27
+
28
+ canBeFulfilled(): boolean {
29
+ return this.status === 'pending' && this.items.length > 0
30
+ }
31
+ }
32
+
33
+ // Application Layer - Application Business Rules
34
+ interface OrderRepository {
35
+ findById(id: string): Promise<Order | null>
36
+ save(order: Order): Promise<void>
37
+ }
38
+
39
+ interface NotificationService {
40
+ sendOrderConfirmation(order: Order): Promise<void>
41
+ }
42
+
43
+ class PlaceOrderUseCase {
44
+ constructor(
45
+ private readonly orderRepo: OrderRepository,
46
+ private readonly notifier: NotificationService
47
+ ) {}
48
+
49
+ async execute(orderData: OrderData): Promise<Order> {
50
+ const order = new Order(orderData.id, orderData.items, 'pending')
51
+
52
+ if (!order.canBeFulfilled()) {
53
+ throw new InvalidOrderError('Order cannot be fulfilled')
54
+ }
55
+
56
+ await this.orderRepo.save(order)
57
+ await this.notifier.sendOrderConfirmation(order)
58
+
59
+ return order
60
+ }
61
+ }
62
+
63
+ // Infrastructure Layer - Interface Adapters (outermost circle)
64
+ class PostgresOrderRepository implements OrderRepository {
65
+ async findById(id: string): Promise<Order | null> {
66
+ // Database implementation
67
+ }
68
+
69
+ async save(order: Order): Promise<void> {
70
+ // Database implementation
71
+ }
72
+ }
73
+
74
+ class EmailNotificationService implements NotificationService {
75
+ async sendOrderConfirmation(order: Order): Promise<void> {
76
+ // Email implementation
77
+ }
78
+ }
79
+ ```
80
+
81
+ ## Related Skills
82
+ - layered-architecture
83
+ - hexagonal-architecture
84
+ - ddd-architecture
85
+ - backend-patterns
@@ -0,0 +1,230 @@
1
+ # CQRS (Command Query Responsibility Segregation)
2
+
3
+ ## When to Activate
4
+
5
+ Activate when:
6
+ - Designing read-heavy or write-heavy systems separately
7
+ - Implementing complex domain models with divergent read/write logic
8
+ - Building systems that need different data representations for reading vs. writing
9
+ - Scaling read and write workloads independently
10
+ - Implementing event sourcing alongside specialized read models
11
+
12
+ ## Steps
13
+
14
+ ### 1. Separate Command and Query Models
15
+
16
+ | Aspect | Command | Query |
17
+ |--------|---------|-------|
18
+ | Purpose | Modify state | Read state |
19
+ | Returns | Void / ACK | Data |
20
+ | Side Effects | Yes | No |
21
+ | Complexity | Business logic | Data shaping |
22
+
23
+ Commands and queries should use **different models** with different schemas optimized for their specific use case.
24
+
25
+ ### 2. Design Command Side
26
+
27
+ - Commands are **intent-based** (present tense: `PlaceOrder`, `UpdatePrice`)
28
+ - Validate business rules **before** executing
29
+ - Return success/failure, not data
30
+ - Keep command handlers small and focused
31
+
32
+ ```typescript
33
+ interface Command {
34
+ id: string; // Correlation ID
35
+ type: string; // Command type
36
+ payload: unknown; // Command data
37
+ metadata: {
38
+ userId: string;
39
+ timestamp: string;
40
+ correlationId: string;
41
+ };
42
+ }
43
+
44
+ interface CommandHandler<T extends Command> {
45
+ execute(command: T): Promise<CommandResult>;
46
+ }
47
+ ```
48
+
49
+ ### 3. Design Query Side
50
+
51
+ - Queries are **data-focused** (past/read tense: `GetUserOrders`, `FindActiveProducts`)
52
+ - Queries should be **side-effect free**
53
+ - Return **read-optimized** data structures (possibly denormalized)
54
+ - Support pagination, filtering, sorting
55
+
56
+ ```typescript
57
+ interface Query {
58
+ id: string;
59
+ type: string;
60
+ parameters: Record<string, unknown>;
61
+ pagination?: { page: number; limit: number };
62
+ sorting?: { field: string; direction: 'asc' | 'desc' }[];
63
+ }
64
+
65
+ interface QueryHandler<T extends Query> {
66
+ execute(query: T): Promise<QueryResult>;
67
+ }
68
+ ```
69
+
70
+ ### 4. Implement Synchronization
71
+
72
+ When commands and queries share data:
73
+
74
+ 1. **Synchronous** (same DB): Update the read model transactionally
75
+ 2. **Asynchronous** (event-driven): Project events to read models
76
+ 3. **Dual writes**: Update both models, handle eventual consistency
77
+
78
+ ```typescript
79
+ // Synchronous synchronization
80
+ async function placeOrder(command: PlaceOrderCommand): Promise<void> {
81
+ const order = Order.create(command.payload);
82
+
83
+ await this.transactionManager.execute(async (tx) => {
84
+ // Write to command model
85
+ await this.orderRepo.save(order, tx);
86
+
87
+ // Synchronize to read model
88
+ const readModel = {
89
+ orderId: order.id,
90
+ customerId: order.customerId,
91
+ status: order.status,
92
+ total: order.total,
93
+ placedAt: order.placedAt
94
+ };
95
+ await this.orderReadRepo.save(readModel, tx);
96
+ });
97
+ }
98
+ ```
99
+
100
+ ### 5. Handle Eventual Consistency
101
+
102
+ If read and write models are updated asynchronously:
103
+
104
+ - Document **expected consistency lag**
105
+ - Design UIs to handle stale data gracefully
106
+ - Implement **cache invalidation** strategies
107
+ - Use **version numbers** or timestamps for cache validation
108
+
109
+ ## Examples
110
+
111
+ ### Command Implementation
112
+
113
+ ```typescript
114
+ // commands/place-order.command.ts
115
+ interface PlaceOrderCommand {
116
+ orderId?: string; // Optional, generated if not provided
117
+ customerId: string;
118
+ items: OrderItem[];
119
+ paymentMethod: 'CARD' | 'PAYPAL';
120
+ }
121
+
122
+ class PlaceOrderCommandHandler implements CommandHandler<PlaceOrderCommand> {
123
+ async execute(command: PlaceOrderCommand): Promise<CommandResult> {
124
+ // 1. Validate command
125
+ const validation = this.validate(command);
126
+ if (!validation.success) {
127
+ return CommandResult.failure(validation.errors);
128
+ }
129
+
130
+ // 2. Check business invariants
131
+ const customer = await this.customerRepo.findById(command.customerId);
132
+ if (!customer.isActive) {
133
+ return CommandResult.failure('Customer account is not active');
134
+ }
135
+
136
+ // 3. Create aggregate
137
+ const order = Order.create({
138
+ id: command.orderId,
139
+ customerId: command.customerId,
140
+ items: command.items
141
+ });
142
+
143
+ // 4. Persist
144
+ await this.orderRepo.save(order);
145
+
146
+ // 5. Emit event for async processing
147
+ await this.eventBus.publish(OrderPlacedEvent.fromOrder(order));
148
+
149
+ return CommandResult.success({ orderId: order.id });
150
+ }
151
+ }
152
+ ```
153
+
154
+ ### Query Implementation
155
+
156
+ ```typescript
157
+ // queries/get-order-details.query.ts
158
+ interface GetOrderDetailsQuery {
159
+ orderId: string;
160
+ includeItems?: boolean;
161
+ }
162
+
163
+ interface OrderDetailsReadModel {
164
+ orderId: string;
165
+ customerId: string;
166
+ customerName: string;
167
+ status: string;
168
+ total: number;
169
+ placedAt: string;
170
+ items?: OrderItemReadModel[];
171
+ }
172
+
173
+ class GetOrderDetailsQueryHandler implements QueryHandler<GetOrderDetailsQuery, OrderDetailsReadModel> {
174
+ async execute(query: GetOrderDetailsQuery): Promise<OrderDetailsReadModel> {
175
+ const order = await this.readModelRepo.findOrderWithDetails(query.orderId);
176
+
177
+ if (!order) {
178
+ throw new QueryNotFoundError('Order not found');
179
+ }
180
+
181
+ const result: OrderDetailsReadModel = {
182
+ orderId: order.orderId,
183
+ customerId: order.customerId,
184
+ customerName: order.customerName,
185
+ status: order.status,
186
+ total: order.total,
187
+ placedAt: order.placedAt
188
+ };
189
+
190
+ if (query.includeItems) {
191
+ result.items = await this.readModelRepo.findOrderItems(query.orderId);
192
+ }
193
+
194
+ return result;
195
+ }
196
+ }
197
+ ```
198
+
199
+ ### Mediator Pattern for CQRS
200
+
201
+ ```typescript
202
+ class CqrsMediator {
203
+ private commandHandlers: Map<string, CommandHandler<any>>;
204
+ private queryHandlers: Map<string, QueryHandler<any>>;
205
+
206
+ async send<T>(message: Command | Query): Promise<CommandResult | QueryResult> {
207
+ const handler = message instanceof Command
208
+ ? this.commandHandlers.get(message.type)
209
+ : this.queryHandlers.get(message.type);
210
+
211
+ if (!handler) {
212
+ throw new HandlerNotFoundError(message.type);
213
+ }
214
+
215
+ return handler.execute(message);
216
+ }
217
+ }
218
+
219
+ // Usage
220
+ const result = await mediator.send(new PlaceOrderCommand({ ... }));
221
+ const orderDetails = await mediator.send(new GetOrderDetailsQuery({ orderId: '123' }));
222
+ ```
223
+
224
+ ## Related Skills
225
+
226
+ - api-design
227
+ - event-driven-architecture
228
+ - backend-patterns
229
+ - event-sourcing
230
+ - hexagonal-architecture