@el-j/magic-helix-plugins 4.0.0-beta.2 → 4.0.0-beta.3

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 (120) hide show
  1. package/dist/architecture/codeowners.md +123 -0
  2. package/dist/architecture/monorepo.md +146 -0
  3. package/dist/architecture/nx.md +122 -0
  4. package/dist/architecture/turborepo.md +114 -0
  5. package/dist/ci/github-actions.md +268 -0
  6. package/dist/ci/gitlab-ci.md +330 -0
  7. package/dist/containers/docker-multistage.md +120 -0
  8. package/dist/containers/kubernetes-deploy.md +210 -0
  9. package/dist/cpp/index.cjs +79 -0
  10. package/dist/cpp/index.mjs +209 -0
  11. package/dist/csharp/index.cjs +2 -2
  12. package/dist/csharp/{index.js → index.mjs} +17 -11
  13. package/dist/csharp/templates/framework-aspnetcore.md +205 -0
  14. package/dist/csharp/templates/framework-blazor.md +271 -0
  15. package/dist/csharp/templates/lang-csharp.md +162 -0
  16. package/dist/devops/docker-compose.md +111 -0
  17. package/dist/devops/docker-dockerfile.md +94 -0
  18. package/dist/devops/github-actions.md +160 -0
  19. package/dist/devops/gitlab-ci.md +210 -0
  20. package/dist/generic/lang-typescript.md +57 -0
  21. package/dist/generic/state-redux.md +21 -0
  22. package/dist/generic/state-rxjs.md +6 -0
  23. package/dist/generic/style-mui.md +23 -0
  24. package/dist/generic/style-tailwind.md +76 -0
  25. package/dist/generic/test-cypress.md +21 -0
  26. package/dist/generic/test-jest.md +20 -0
  27. package/dist/generic/test-playwright.md +21 -0
  28. package/dist/generic/test-vitest.md +131 -0
  29. package/dist/go/index.cjs +3 -3
  30. package/dist/go/{index.js → index.mjs} +18 -15
  31. package/dist/go/templates/lang-go.md +571 -0
  32. package/dist/index.cjs +1 -1
  33. package/dist/index.mjs +24 -0
  34. package/dist/java/index.cjs +2 -2
  35. package/dist/java/{index.js → index.mjs} +25 -19
  36. package/dist/java/templates/build-gradle.md +102 -0
  37. package/dist/java/templates/build-maven.md +86 -0
  38. package/dist/java/templates/framework-spring-boot.md +179 -0
  39. package/dist/java/templates/lang-java.md +78 -0
  40. package/dist/java/templates/lang-kotlin.md +88 -0
  41. package/dist/meta/magic-helix-meta.md +213 -0
  42. package/dist/meta/meta-debug.md +459 -0
  43. package/dist/meta/meta-implement.md +450 -0
  44. package/dist/meta/meta-roadmap.md +265 -0
  45. package/dist/nodejs/templates/angular-core.md +19 -0
  46. package/dist/nodejs/templates/lang-typescript.md +57 -0
  47. package/dist/nodejs/templates/nestjs-core.md +7 -0
  48. package/dist/nodejs/templates/react-core.md +677 -0
  49. package/dist/nodejs/templates/react-zustand.md +7 -0
  50. package/dist/nodejs/templates/state-redux.md +21 -0
  51. package/dist/nodejs/templates/state-rxjs.md +6 -0
  52. package/dist/nodejs/templates/style-primevue.md +6 -0
  53. package/dist/nodejs/templates/style-quasar.md +22 -0
  54. package/dist/nodejs/templates/style-tailwind.md +76 -0
  55. package/dist/nodejs/templates/test-cypress.md +21 -0
  56. package/dist/nodejs/templates/test-jest.md +20 -0
  57. package/dist/nodejs/templates/test-playwright.md +21 -0
  58. package/dist/nodejs/templates/test-vitest.md +131 -0
  59. package/dist/nodejs/templates/vue-core.md +108 -0
  60. package/dist/nodejs/templates/vue-pinia.md +5 -0
  61. package/dist/patterns/architecture/clean-architecture.md +469 -0
  62. package/dist/patterns/architecture/dependency-injection.md +517 -0
  63. package/dist/patterns/architecture/domain-driven-design.md +621 -0
  64. package/dist/patterns/architecture/layered-architecture.md +382 -0
  65. package/dist/patterns/architecture/repository-pattern.md +408 -0
  66. package/dist/patterns/domain-expertise/nextjs-rules.md +115 -0
  67. package/dist/patterns/domain-expertise/react-patterns.md +181 -0
  68. package/dist/patterns/domain-expertise/server-components.md +212 -0
  69. package/dist/patterns/domain-expertise/shadcn-ui.md +52 -0
  70. package/dist/patterns/domain-expertise/tailwind-patterns.md +52 -0
  71. package/dist/patterns/environment/container-awareness.md +17 -0
  72. package/dist/patterns/environment/ide-features.md +17 -0
  73. package/dist/patterns/environment/os-commands.md +17 -0
  74. package/dist/patterns/organization/heading-hierarchy.md +103 -0
  75. package/dist/patterns/organization/sequential-workflows.md +102 -0
  76. package/dist/patterns/organization/xml-rule-groups.md +64 -0
  77. package/dist/patterns/reasoning/agent-loop.md +151 -0
  78. package/dist/patterns/reasoning/confirmation-gates.md +141 -0
  79. package/dist/patterns/reasoning/dependency-analysis.md +132 -0
  80. package/dist/patterns/reasoning/one-tool-per-iteration.md +152 -0
  81. package/dist/patterns/reasoning/preview-before-action.md +194 -0
  82. package/dist/patterns/reasoning/reflection-checkpoints.md +166 -0
  83. package/dist/patterns/reasoning/result-verification.md +157 -0
  84. package/dist/patterns/reasoning/subtask-breakdown.md +131 -0
  85. package/dist/patterns/reasoning/thinking-tags.md +100 -0
  86. package/dist/patterns/role-definition/capability-declarations.md +72 -0
  87. package/dist/patterns/role-definition/expert-identity.md +45 -0
  88. package/dist/patterns/role-definition/scope-boundaries.md +61 -0
  89. package/dist/patterns/safety/code-safety-rules.md +17 -0
  90. package/dist/patterns/safety/credential-handling.md +17 -0
  91. package/dist/patterns/safety/destructive-warnings.md +17 -0
  92. package/dist/patterns/safety/refusal-messages.md +17 -0
  93. package/dist/patterns/tone/adaptive-tone.md +17 -0
  94. package/dist/patterns/tone/concise-communication.md +17 -0
  95. package/dist/patterns/tone/forbidden-phrases.md +17 -0
  96. package/dist/patterns/tool-guidelines/function-schemas.md +143 -0
  97. package/dist/patterns/tool-guidelines/parameter-examples.md +137 -0
  98. package/dist/patterns/tool-guidelines/usage-policies.md +105 -0
  99. package/dist/php/index.cjs +2 -2
  100. package/dist/php/{index.js → index.mjs} +12 -6
  101. package/dist/php/templates/framework-laravel.md +112 -0
  102. package/dist/php/templates/lang-php.md +94 -0
  103. package/dist/python/index.cjs +4 -4
  104. package/dist/python/{index.js → index.mjs} +10 -7
  105. package/dist/python/templates/lang-python.md +508 -0
  106. package/dist/ruby/index.cjs +2 -2
  107. package/dist/ruby/{index.js → index.mjs} +16 -10
  108. package/dist/ruby/templates/framework-rails.md +309 -0
  109. package/dist/ruby/templates/framework-sinatra.md +227 -0
  110. package/dist/ruby/templates/lang-ruby.md +216 -0
  111. package/dist/rust/index.cjs +3 -3
  112. package/dist/rust/{index.js → index.mjs} +24 -18
  113. package/dist/rust/templates/lang-rust.md +89 -0
  114. package/dist/swift/index.cjs +32 -0
  115. package/dist/swift/index.mjs +112 -0
  116. package/dist/swift/templates/framework-vapor.md +352 -0
  117. package/dist/swift/templates/lang-swift.md +291 -0
  118. package/package.json +31 -21
  119. package/dist/index.js +0 -20
  120. /package/dist/nodejs/{index.js → index.mjs} +0 -0
@@ -0,0 +1,621 @@
1
+ # Domain-Driven Design (DDD) Patterns
2
+
3
+ ## Purpose
4
+ Model complex business domains through strategic and tactical patterns. Align software structure with business concepts. Manage complexity through bounded contexts and rich domain models.
5
+
6
+ ## Core Concepts
7
+
8
+ ### Ubiquitous Language
9
+
10
+ **The same terms used by domain experts must be used in code.**
11
+
12
+ ```typescript
13
+ // ✅ Good: Uses business domain language
14
+ class Order {
15
+ confirmPayment(payment: Payment): void {
16
+ if (this.status !== OrderStatus.AwaitingPayment) {
17
+ throw new InvalidOrderStateError('Order is not awaiting payment');
18
+ }
19
+ this.status = OrderStatus.Confirmed;
20
+ this.events.push(new OrderConfirmedEvent(this.id));
21
+ }
22
+ }
23
+
24
+ // ❌ Bad: Technical jargon, not business language
25
+ class Order {
26
+ updateStatus(newStatus: number): void {
27
+ this.statusCode = newStatus;
28
+ this.lastModified = new Date();
29
+ }
30
+ }
31
+ ```
32
+
33
+ **Learn the language from domain experts, then use it everywhere:** code, tests, docs, discussions.
34
+
35
+ ## Building Blocks (Tactical Patterns)
36
+
37
+ ### 1. Entities
38
+
39
+ Objects with identity that persist over time. Two entities are equal if they have the same ID, even if their attributes differ.
40
+
41
+ ```typescript
42
+ // ✅ Entity: Has identity (id)
43
+ export class User {
44
+ constructor(
45
+ private readonly id: UserId, // Identity (value object)
46
+ private email: Email, // Value object
47
+ private name: string,
48
+ private createdAt: Date
49
+ ) {}
50
+
51
+ getId(): UserId {
52
+ return this.id;
53
+ }
54
+
55
+ changeEmail(newEmail: Email): void {
56
+ if (!newEmail.isValid()) {
57
+ throw new InvalidEmailError();
58
+ }
59
+ this.email = newEmail;
60
+ }
61
+
62
+ // Equality based on identity
63
+ equals(other: User): boolean {
64
+ return this.id.equals(other.id);
65
+ }
66
+ }
67
+
68
+ // Usage
69
+ const user1 = new User(UserId.create(), Email.create('john@example.com'), 'John', new Date());
70
+ const user2 = new User(UserId.create(), Email.create('jane@example.com'), 'Jane', new Date());
71
+
72
+ user1.equals(user2); // false (different IDs)
73
+ ```
74
+
75
+ **Rules:**
76
+ - Has a unique identifier (ID)
77
+ - Equality based on ID, not attributes
78
+ - Contains business logic methods
79
+ - Can change over time while keeping identity
80
+
81
+ ### 2. Value Objects
82
+
83
+ Immutable objects defined by their attributes. Two value objects are equal if all their attributes are equal.
84
+
85
+ ```typescript
86
+ // ✅ Value Object: Immutable, equality by attributes
87
+ export class Money {
88
+ constructor(
89
+ private readonly amount: number,
90
+ private readonly currency: string
91
+ ) {
92
+ if (amount < 0) {
93
+ throw new NegativeAmountError();
94
+ }
95
+ Object.freeze(this); // Enforce immutability
96
+ }
97
+
98
+ add(other: Money): Money {
99
+ if (this.currency !== other.currency) {
100
+ throw new CurrencyMismatchError();
101
+ }
102
+ return new Money(this.amount + other.amount, this.currency);
103
+ }
104
+
105
+ multiply(factor: number): Money {
106
+ return new Money(this.amount * factor, this.currency);
107
+ }
108
+
109
+ equals(other: Money): boolean {
110
+ return this.amount === other.amount && this.currency === other.currency;
111
+ }
112
+
113
+ toString(): string {
114
+ return `${this.amount} ${this.currency}`;
115
+ }
116
+ }
117
+
118
+ // Usage
119
+ const price = new Money(100, 'USD');
120
+ const tax = price.multiply(0.2);
121
+ const total = price.add(tax); // New object, original unchanged
122
+ ```
123
+
124
+ **Examples:** Email, Address, Money, DateRange, PhoneNumber
125
+
126
+ **Rules:**
127
+ - **ALWAYS** immutable (no setters)
128
+ - Equality based on all attributes
129
+ - No identity (no ID)
130
+ - Encapsulate validation
131
+ - Return new instances for changes
132
+
133
+ ### 3. Aggregates
134
+
135
+ Cluster of entities and value objects with a single root entity. The root controls access and enforces invariants.
136
+
137
+ ```typescript
138
+ // ✅ Aggregate Root: Order
139
+ export class Order {
140
+ private readonly id: OrderId;
141
+ private customerId: CustomerId;
142
+ private items: OrderItem[] = []; // Entities within aggregate
143
+ private status: OrderStatus;
144
+ private total: Money;
145
+
146
+ constructor(customerId: CustomerId) {
147
+ this.id = OrderId.create();
148
+ this.customerId = customerId;
149
+ this.status = OrderStatus.Draft;
150
+ this.total = Money.zero('USD');
151
+ }
152
+
153
+ // ✅ All modifications go through aggregate root
154
+ addItem(product: Product, quantity: number): void {
155
+ if (this.status !== OrderStatus.Draft) {
156
+ throw new OrderAlreadyConfirmedError();
157
+ }
158
+
159
+ const item = new OrderItem(product, quantity);
160
+ this.items.push(item);
161
+ this.recalculateTotal(); // Maintain invariant
162
+ }
163
+
164
+ removeItem(productId: ProductId): void {
165
+ if (this.status !== OrderStatus.Draft) {
166
+ throw new OrderAlreadyConfirmedError();
167
+ }
168
+
169
+ this.items = this.items.filter(item => !item.productId.equals(productId));
170
+ this.recalculateTotal();
171
+ }
172
+
173
+ confirm(): void {
174
+ if (this.items.length === 0) {
175
+ throw new EmptyOrderError();
176
+ }
177
+ this.status = OrderStatus.Confirmed;
178
+ }
179
+
180
+ // ✅ Enforce invariant: total = sum of items
181
+ private recalculateTotal(): void {
182
+ this.total = this.items.reduce(
183
+ (sum, item) => sum.add(item.getTotal()),
184
+ Money.zero('USD')
185
+ );
186
+ }
187
+
188
+ getTotal(): Money {
189
+ return this.total;
190
+ }
191
+ }
192
+
193
+ // ❌ Never modify entities within aggregate from outside
194
+ class OrderItem {
195
+ constructor(
196
+ private product: Product,
197
+ private quantity: number
198
+ ) {}
199
+
200
+ getTotal(): Money {
201
+ return this.product.price.multiply(this.quantity);
202
+ }
203
+ }
204
+
205
+ // ❌ Bad: Bypassing aggregate root
206
+ order.items[0].quantity = 10; // Violates encapsulation!
207
+
208
+ // ✅ Good: Use aggregate root
209
+ order.addItem(product, 10);
210
+ ```
211
+
212
+ **Aggregate Boundaries:**
213
+ - Small aggregates (1-3 entities) perform better
214
+ - One aggregate per transaction
215
+ - Reference other aggregates by ID, not direct reference
216
+
217
+ ```typescript
218
+ // ✅ Good: Reference by ID
219
+ class Order {
220
+ private customerId: CustomerId; // ID reference
221
+ }
222
+
223
+ // ❌ Bad: Direct reference across aggregates
224
+ class Order {
225
+ private customer: Customer; // Couples two aggregates
226
+ }
227
+ ```
228
+
229
+ ### 4. Domain Events
230
+
231
+ Represent something significant that happened in the domain.
232
+
233
+ ```typescript
234
+ // ✅ Domain Event: Immutable record of what happened
235
+ export class OrderConfirmedEvent {
236
+ constructor(
237
+ public readonly orderId: OrderId,
238
+ public readonly customerId: CustomerId,
239
+ public readonly total: Money,
240
+ public readonly occurredAt: Date = new Date()
241
+ ) {
242
+ Object.freeze(this);
243
+ }
244
+ }
245
+
246
+ // ✅ Aggregate raises events
247
+ export class Order {
248
+ private events: DomainEvent[] = [];
249
+
250
+ confirm(): void {
251
+ if (this.items.length === 0) {
252
+ throw new EmptyOrderError();
253
+ }
254
+ this.status = OrderStatus.Confirmed;
255
+
256
+ // Raise event
257
+ this.events.push(
258
+ new OrderConfirmedEvent(this.id, this.customerId, this.total)
259
+ );
260
+ }
261
+
262
+ getEvents(): DomainEvent[] {
263
+ return [...this.events];
264
+ }
265
+
266
+ clearEvents(): void {
267
+ this.events = [];
268
+ }
269
+ }
270
+
271
+ // ✅ Event handler in separate bounded context
272
+ class InventoryEventHandler {
273
+ async onOrderConfirmed(event: OrderConfirmedEvent): Promise<void> {
274
+ // Reserve inventory when order confirmed
275
+ await this.inventoryService.reserveItems(event.orderId);
276
+ }
277
+ }
278
+ ```
279
+
280
+ **Use events for:**
281
+ - Cross-aggregate consistency (eventual consistency)
282
+ - Decoupling bounded contexts
283
+ - Audit trail
284
+ - Triggering side effects
285
+
286
+ ### 5. Domain Services
287
+
288
+ Business logic that doesn't belong to a single entity or value object.
289
+
290
+ ```typescript
291
+ // ✅ Domain Service: Stateless, operates on domain objects
292
+ export class OrderPricingService {
293
+ calculateDiscount(order: Order, customer: Customer): Money {
294
+ let discount = Money.zero('USD');
295
+
296
+ // VIP customers get 10% off
297
+ if (customer.isVip()) {
298
+ discount = order.getTotal().multiply(0.1);
299
+ }
300
+
301
+ // Bulk orders (>10 items) get additional 5%
302
+ if (order.getItemCount() > 10) {
303
+ const bulkDiscount = order.getTotal().multiply(0.05);
304
+ discount = discount.add(bulkDiscount);
305
+ }
306
+
307
+ return discount;
308
+ }
309
+ }
310
+
311
+ // ❌ Bad: Logic scattered across entities
312
+ class Order {
313
+ calculateVipDiscount(): Money { /* ... */ }
314
+ calculateBulkDiscount(): Money { /* ... */ }
315
+ }
316
+
317
+ class Customer {
318
+ calculateOrderDiscount(order: Order): Money { /* ... */ }
319
+ }
320
+ ```
321
+
322
+ **When to use domain services:**
323
+ - Logic involves multiple aggregates
324
+ - Logic doesn't naturally fit in an entity
325
+ - Stateless operations
326
+
327
+ ### 6. Repositories
328
+
329
+ Provide collection-like interface for aggregates. Only for aggregate roots.
330
+
331
+ ```typescript
332
+ // ✅ Repository: Collection interface for aggregates
333
+ export interface IOrderRepository {
334
+ save(order: Order): Promise<void>;
335
+ findById(id: OrderId): Promise<Order | null>;
336
+ findByCustomer(customerId: CustomerId): Promise<Order[]>;
337
+ delete(id: OrderId): Promise<void>;
338
+ }
339
+
340
+ // ✅ Implementation in infrastructure layer
341
+ export class PostgresOrderRepository implements IOrderRepository {
342
+ constructor(private db: Database) {}
343
+
344
+ async save(order: Order): Promise<void> {
345
+ await this.db.transaction(async (tx) => {
346
+ // Save aggregate root
347
+ await tx.query(
348
+ 'INSERT INTO orders (id, customer_id, status, total) VALUES ($1, $2, $3, $4)',
349
+ [order.id, order.customerId, order.status, order.total]
350
+ );
351
+
352
+ // Save child entities (items)
353
+ for (const item of order.items) {
354
+ await tx.query(
355
+ 'INSERT INTO order_items (order_id, product_id, quantity) VALUES ($1, $2, $3)',
356
+ [order.id, item.productId, item.quantity]
357
+ );
358
+ }
359
+
360
+ // Publish domain events
361
+ for (const event of order.getEvents()) {
362
+ await this.eventPublisher.publish(event);
363
+ }
364
+ order.clearEvents();
365
+ });
366
+ }
367
+
368
+ async findById(id: OrderId): Promise<Order | null> {
369
+ const row = await this.db.query('SELECT * FROM orders WHERE id = $1', [id.value]);
370
+ if (!row) return null;
371
+
372
+ const items = await this.db.query('SELECT * FROM order_items WHERE order_id = $1', [id.value]);
373
+
374
+ return this.mapToAggregate(row, items);
375
+ }
376
+ }
377
+ ```
378
+
379
+ **Rules:**
380
+ - One repository per aggregate root
381
+ - No repositories for entities inside aggregates
382
+ - Interface in domain layer, implementation in infrastructure
383
+
384
+ ## Strategic Patterns
385
+
386
+ ### Bounded Contexts
387
+
388
+ Explicit boundaries where a domain model applies. Same term can have different meanings in different contexts.
389
+
390
+ ```
391
+ ┌─────────────────────────────────────────────────────────────┐
392
+ │ E-commerce System │
393
+ ├─────────────────┬─────────────────┬────────────────────────┤
394
+ │ Sales Context │ Inventory Ctx │ Shipping Context │
395
+ ├─────────────────┼─────────────────┼────────────────────────┤
396
+ │ Product: │ Product: │ Package: │
397
+ │ - price │ - quantity │ - weight │
398
+ │ - description │ - location │ - dimensions │
399
+ │ - reviews │ - reorderLevel │ - tracking number │
400
+ │ │ │ │
401
+ │ Order: │ │ Shipment: │
402
+ │ - customer │ │ - destination │
403
+ │ - total │ │ - carrier │
404
+ │ - items │ │ - status │
405
+ └─────────────────┴─────────────────┴────────────────────────┘
406
+ ```
407
+
408
+ **Example: Product in different contexts**
409
+
410
+ ```typescript
411
+ // Sales Context - Product focuses on sellability
412
+ namespace SalesContext {
413
+ export class Product {
414
+ constructor(
415
+ public id: ProductId,
416
+ public name: string,
417
+ public price: Money,
418
+ public description: string
419
+ ) {}
420
+ }
421
+ }
422
+
423
+ // Inventory Context - Product focuses on stock management
424
+ namespace InventoryContext {
425
+ export class Product {
426
+ constructor(
427
+ public id: ProductId,
428
+ public sku: string,
429
+ public quantityOnHand: number,
430
+ public reorderLevel: number,
431
+ public warehouseLocation: string
432
+ ) {}
433
+ }
434
+ }
435
+ ```
436
+
437
+ ### Context Mapping
438
+
439
+ Define relationships between bounded contexts.
440
+
441
+ ```typescript
442
+ // ✅ Anti-Corruption Layer (ACL): Translate between contexts
443
+ export class SalesInventoryAdapter {
444
+ constructor(private inventoryService: InventoryService) {}
445
+
446
+ async checkAvailability(salesProduct: SalesContext.Product): Promise<boolean> {
447
+ // Translate Sales Product → Inventory Product
448
+ const inventoryProduct = await this.inventoryService.findBySku(
449
+ salesProduct.id.toSku()
450
+ );
451
+
452
+ return inventoryProduct.quantityOnHand > 0;
453
+ }
454
+ }
455
+ ```
456
+
457
+ ## Layered Architecture with DDD
458
+
459
+ ```
460
+ ┌──────────────────────────────────────────────┐
461
+ │ Presentation Layer (Controllers, UI) │
462
+ │ - Handles HTTP requests │
463
+ │ - Maps DTOs ↔ Domain models │
464
+ └──────────────────────────────────────────────┘
465
+
466
+ ┌──────────────────────────────────────────────┐
467
+ │ Application Layer (Use Cases, Services) │
468
+ │ - Orchestrates domain objects │
469
+ │ - Transaction boundaries │
470
+ │ - Triggers domain events │
471
+ └──────────────────────────────────────────────┘
472
+
473
+ ┌──────────────────────────────────────────────┐
474
+ │ Domain Layer (Entities, VOs, Aggregates) │
475
+ │ - Business logic │
476
+ │ - Domain rules and invariants │
477
+ │ - Rich domain model │
478
+ └──────────────────────────────────────────────┘
479
+
480
+ ┌──────────────────────────────────────────────┐
481
+ │ Infrastructure Layer (DB, APIs, Messaging) │
482
+ │ - Repository implementations │
483
+ │ - External service clients │
484
+ │ - Database access │
485
+ └──────────────────────────────────────────────┘
486
+ ```
487
+
488
+ **Example: Complete DDD Flow**
489
+
490
+ ```typescript
491
+ // 1. Presentation Layer: Controller
492
+ export class OrderController {
493
+ constructor(private createOrderUseCase: CreateOrderUseCase) {}
494
+
495
+ async createOrder(req: Request, res: Response) {
496
+ const dto: CreateOrderDTO = req.body;
497
+
498
+ const result = await this.createOrderUseCase.execute({
499
+ customerId: dto.customerId,
500
+ items: dto.items,
501
+ });
502
+
503
+ if (result.isFailure) {
504
+ return res.status(400).json({ error: result.error });
505
+ }
506
+
507
+ return res.status(201).json({ orderId: result.value.id });
508
+ }
509
+ }
510
+
511
+ // 2. Application Layer: Use Case
512
+ export class CreateOrderUseCase {
513
+ constructor(
514
+ private orderRepo: IOrderRepository,
515
+ private productRepo: IProductRepository,
516
+ private pricingService: OrderPricingService
517
+ ) {}
518
+
519
+ async execute(request: CreateOrderRequest): Promise<Result<Order>> {
520
+ // Load customer aggregate
521
+ const customer = await this.customerRepo.findById(request.customerId);
522
+ if (!customer) {
523
+ return Result.fail('Customer not found');
524
+ }
525
+
526
+ // Create order aggregate
527
+ const order = new Order(request.customerId);
528
+
529
+ // Add items
530
+ for (const item of request.items) {
531
+ const product = await this.productRepo.findById(item.productId);
532
+ if (!product) {
533
+ return Result.fail(`Product ${item.productId} not found`);
534
+ }
535
+ order.addItem(product, item.quantity);
536
+ }
537
+
538
+ // Apply business logic via domain service
539
+ const discount = this.pricingService.calculateDiscount(order, customer);
540
+ order.applyDiscount(discount);
541
+
542
+ // Confirm order
543
+ order.confirm();
544
+
545
+ // Persist aggregate
546
+ await this.orderRepo.save(order);
547
+
548
+ return Result.ok(order);
549
+ }
550
+ }
551
+
552
+ // 3. Domain Layer: Aggregate
553
+ export class Order {
554
+ // See aggregate example above
555
+ }
556
+
557
+ // 4. Infrastructure Layer: Repository
558
+ export class PostgresOrderRepository implements IOrderRepository {
559
+ // See repository example above
560
+ }
561
+ ```
562
+
563
+ ## File Organization
564
+
565
+ ```
566
+ src/
567
+ ├── presentation/ # Presentation layer
568
+ │ └── controllers/
569
+ │ └── OrderController.ts
570
+
571
+ ├── application/ # Application layer
572
+ │ └── use-cases/
573
+ │ └── CreateOrderUseCase.ts
574
+
575
+ ├── domain/ # Domain layer
576
+ │ ├── order/ # Bounded context
577
+ │ │ ├── entities/
578
+ │ │ │ ├── Order.ts
579
+ │ │ │ └── OrderItem.ts
580
+ │ │ ├── value-objects/
581
+ │ │ │ ├── OrderId.ts
582
+ │ │ │ └── OrderStatus.ts
583
+ │ │ ├── repositories/
584
+ │ │ │ └── IOrderRepository.ts
585
+ │ │ ├── services/
586
+ │ │ │ └── OrderPricingService.ts
587
+ │ │ └── events/
588
+ │ │ └── OrderConfirmedEvent.ts
589
+ │ │
590
+ │ ├── customer/ # Another bounded context
591
+ │ │ ├── entities/
592
+ │ │ │ └── Customer.ts
593
+ │ │ └── value-objects/
594
+ │ │ └── CustomerId.ts
595
+ │ │
596
+ │ └── shared/ # Shared kernel
597
+ │ └── value-objects/
598
+ │ └── Money.ts
599
+
600
+ └── infrastructure/ # Infrastructure layer
601
+ ├── repositories/
602
+ │ └── PostgresOrderRepository.ts
603
+ └── messaging/
604
+ └── RabbitMQEventPublisher.ts
605
+ ```
606
+
607
+ ## Rules Summary
608
+
609
+ - **ALWAYS** use ubiquitous language from domain experts
610
+ - **ALWAYS** model aggregates with clear boundaries
611
+ - **ALWAYS** make value objects immutable
612
+ - **ALWAYS** enforce invariants in aggregate roots
613
+ - **ALWAYS** access child entities through aggregate root
614
+ - **ALWAYS** reference other aggregates by ID, not direct reference
615
+ - **NEVER** allow direct modification of entities inside aggregates
616
+ - **NEVER** create repositories for non-root entities
617
+ - **NEVER** let infrastructure concerns leak into domain layer
618
+ - **PREFER** small aggregates (1-3 entities)
619
+ - **PREFER** eventual consistency between aggregates
620
+ - **CONSIDER** domain events for cross-aggregate operations
621
+ - **CONSIDER** anti-corruption layers between bounded contexts