@jarrodmedrano/claude-skills 1.0.3 → 1.0.4

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 (26) hide show
  1. package/.claude/skills/bevy/SKILL.md +406 -0
  2. package/.claude/skills/bevy/references/bevy_specific_tips.md +385 -0
  3. package/.claude/skills/bevy/references/common_pitfalls.md +217 -0
  4. package/.claude/skills/bevy/references/ecs_patterns.md +277 -0
  5. package/.claude/skills/bevy/references/project_structure.md +116 -0
  6. package/.claude/skills/bevy/references/ui_development.md +147 -0
  7. package/.claude/skills/domain-driven-design/SKILL.md +459 -0
  8. package/.claude/skills/domain-driven-design/references/ddd_foundations_and_patterns.md +664 -0
  9. package/.claude/skills/domain-driven-design/references/rich_hickey_principles.md +406 -0
  10. package/.claude/skills/domain-driven-design/references/visualization_examples.md +790 -0
  11. package/.claude/skills/domain-driven-design/references/wlaschin_patterns.md +639 -0
  12. package/.claude/skills/godot/SKILL.md +728 -0
  13. package/.claude/skills/godot/assets/templates/attribute_template.gd +109 -0
  14. package/.claude/skills/godot/assets/templates/component_template.gd +76 -0
  15. package/.claude/skills/godot/assets/templates/interaction_template.gd +108 -0
  16. package/.claude/skills/godot/assets/templates/item_resource.tres +11 -0
  17. package/.claude/skills/godot/assets/templates/spell_resource.tres +20 -0
  18. package/.claude/skills/godot/references/architecture-patterns.md +608 -0
  19. package/.claude/skills/godot/references/common-pitfalls.md +518 -0
  20. package/.claude/skills/godot/references/file-formats.md +491 -0
  21. package/.claude/skills/godot/references/godot4-physics-api.md +302 -0
  22. package/.claude/skills/godot/scripts/validate_tres.py +145 -0
  23. package/.claude/skills/godot/scripts/validate_tscn.py +170 -0
  24. package/.claude/skills/react-three-fiber/SKILL.md +2055 -0
  25. package/.claude/skills/react-three-fiber/scripts/build-scene.ts +171 -0
  26. package/package.json +1 -1
@@ -0,0 +1,664 @@
1
+ # DDD Foundations and Practical Patterns
2
+
3
+ This reference combines Eric Evans' foundational Domain-Driven Design concepts with practical patterns from Clojure and functional programming approaches, plus Martin Fowler's guidance on Ubiquitous Language.
4
+
5
+ ## Eric Evans' Core DDD Concepts
6
+
7
+ ### Ubiquitous Language (Fowler & Evans)
8
+
9
+ **Definition:** A language structured around the domain model and used by all team members to connect all activities of the team with the software.
10
+
11
+ **Key Principles:**
12
+ - Same language in conversations, documentation, diagrams, and code
13
+ - Developed through collaboration between developers and domain experts
14
+ - Evolves with the model; changes in language reflect changes in understanding
15
+ - No translation between business and technical discussions
16
+
17
+ **Building Ubiquitous Language:**
18
+ 1. Listen to how domain experts speak
19
+ 2. Extract nouns (concepts) and verbs (operations)
20
+ 3. Challenge vague terms - ask for precision
21
+ 4. Document terms in a glossary
22
+ 5. Use these exact terms in code - class names, function names, module names
23
+ 6. When language feels awkward in code, it reveals model problems
24
+
25
+ **Red Flags:**
26
+ - Developers use different terms than domain experts
27
+ - Translation happens between "business language" and "technical language"
28
+ - Terms have different meanings in different contexts (without bounded contexts)
29
+ - Code uses generic names like "Manager", "Handler", "Processor" instead of domain terms
30
+
31
+ **Example - Good Ubiquitous Language:**
32
+ ```clojure
33
+ ;; Domain expert: "We post a transfer to debit one account and credit another"
34
+ (defn post-transfer [transfer-number debit credit]
35
+ ...)
36
+
37
+ ;; NOT:
38
+ (defn create-transaction [id source dest] ; "transaction" is technical jargon
39
+ ...)
40
+ ```
41
+
42
+ ### Entities
43
+
44
+ **Definition:** An object defined primarily by its identity, rather than its attributes.
45
+
46
+ **Characteristics:**
47
+ - Has a unique identifier
48
+ - Identity persists through time
49
+ - Attributes may change while identity remains constant
50
+ - Two entities with same attributes but different IDs are distinct
51
+
52
+ **When to Use:**
53
+ - Domain experts refer to things by name/ID
54
+ - The thing continues to exist even as its properties change
55
+ - Need to track the thing over time or across system boundaries
56
+
57
+ **Clojure Pattern:**
58
+ ```clojure
59
+ ;; Entity: Account (identity = account number)
60
+ (s/def :account/number
61
+ (s/and string? #(re-matches #"[1-9]{12}" %)))
62
+
63
+ (s/def :account/account
64
+ (s/keys :req-un [:account/number ; The identity
65
+ :balance/balance])) ; Mutable state
66
+
67
+ (defn make-account [account-number balance]
68
+ (s/assert :account/account
69
+ {:number account-number
70
+ :balance balance}))
71
+
72
+ ;; Same account, even as balance changes:
73
+ (def account-1 (make-account "123456789012" (make-balance 1000 :usd)))
74
+ (def account-2 (make-account "123456789012" (make-balance 500 :usd)))
75
+ ;; account-1 and account-2 represent the same account entity
76
+ ```
77
+
78
+ **Immutable Entities:**
79
+ Not all entities are mutable. Some are created once and never change:
80
+
81
+ ```clojure
82
+ ;; Transfer is immutable but still an entity (has identity: transfer number)
83
+ (s/def :transfer/number
84
+ (s/and string? #(re-matches #"[A-Z]{3}[1-9]{8}" %)))
85
+
86
+ (s/def :transfer/transfer
87
+ (s/keys :req-un [:transfer/id
88
+ :transfer/number ; Identity
89
+ :debit/debit
90
+ :credit/credit
91
+ :transfer/creation-date]))
92
+ ```
93
+
94
+ ### Value Objects
95
+
96
+ **Definition:** An object defined entirely by its attributes, with no identity.
97
+
98
+ **Characteristics:**
99
+ - Defined by what it is, not who it is
100
+ - No unique identifier
101
+ - Two value objects with same attributes are equivalent
102
+ - Typically immutable
103
+ - Can be freely shared and replaced
104
+
105
+ **When to Use:**
106
+ - Domain experts describe things by their properties, not by name
107
+ - Identity doesn't matter - only the value matters
108
+ - Can be replaced with an equivalent value without concern
109
+
110
+ **Clojure Pattern:**
111
+ ```clojure
112
+ ;; Value Object: Amount
113
+ (s/def :amount/currency #{:usd :cad})
114
+ (s/def :amount/value (s/and number? pos?))
115
+
116
+ (s/def :amount/amount
117
+ (s/keys :req-un [:amount/currency
118
+ :amount/value]))
119
+
120
+ (defn make-amount [value currency]
121
+ (s/assert :amount/amount
122
+ {:currency currency
123
+ :value value}))
124
+
125
+ ;; Two amounts with same value are equivalent:
126
+ (= (make-amount 100 :usd) (make-amount 100 :usd)) ; => true
127
+ ;; No identity - only the value matters
128
+ ```
129
+
130
+ **Entity vs Value Object - Decision Guide:**
131
+
132
+ | Question | Entity | Value Object |
133
+ |----------|--------|--------------|
134
+ | Do domain experts refer to it by ID/name? | Yes | No |
135
+ | Does it change over time? | Often yes | No |
136
+ | Do two instances with same attributes mean the same thing? | No | Yes |
137
+ | Can you replace it with an equivalent copy? | No | Yes |
138
+
139
+ ### Aggregates and Aggregate Roots
140
+
141
+ **Definition:** A cluster of domain objects (entities and value objects) that can be treated as a single unit for data changes.
142
+
143
+ **Aggregate Root:** The single entity through which all operations on the aggregate must pass.
144
+
145
+ **Purpose:**
146
+ - Define transactional consistency boundaries
147
+ - Simplify the domain model by grouping related objects
148
+ - Enforce invariants that span multiple objects
149
+
150
+ **Rules:**
151
+ 1. **External references go only to the aggregate root**
152
+ - Other systems/aggregates can only hold references to the root
153
+ - Never hold a reference to an internal entity
154
+
155
+ 2. **Root enforces all invariants**
156
+ - Only the root can directly change internal entities
157
+ - Internal entities can exist, but external code can't access them directly
158
+
159
+ 3. **Delete removes everything**
160
+ - Deleting the root should delete all internal entities
161
+
162
+ 4. **Transactions don't span aggregates**
163
+ - One transaction = one aggregate
164
+ - Cross-aggregate consistency is eventual
165
+
166
+ **Example - Order Aggregate:**
167
+
168
+ Traditional OOP approach would have:
169
+ ```
170
+ Order (root)
171
+ ├─ OrderLine (internal entity)
172
+ ├─ ShippingAddress (value object)
173
+ └─ PaymentInfo (value object)
174
+ ```
175
+
176
+ **Clojure/Functional Approach:**
177
+ ```clojure
178
+ ;; In Clojure, aggregates are often just nested data with root-level invariants
179
+
180
+ (s/def ::order-line
181
+ (s/keys :req-un [::product-id ::quantity ::price]))
182
+
183
+ (s/def ::order
184
+ (s/and
185
+ (s/keys :req-un [::order-id ; Root identity
186
+ ::customer-id
187
+ ::order-lines ; Collection of internal entities
188
+ ::shipping-address
189
+ ::status])
190
+ ;; Aggregate-level invariant:
191
+ (fn [order]
192
+ (and (seq (:order-lines order)) ; Must have at least one line
193
+ (every-price-matches-product (:order-lines order))))))
194
+
195
+ ;; Operations go through the root:
196
+ (defn add-order-line [order line]
197
+ ;; Add line and validate aggregate invariants
198
+ (let [updated-order (update order :order-lines conj line)]
199
+ (s/assert ::order updated-order)))
200
+
201
+ ;; External references by ID only:
202
+ (s/def ::customer-reference
203
+ (s/keys :req-un [::customer-id])) ; Reference to Customer aggregate by ID
204
+
205
+ ;; NOT: embedding full customer aggregate
206
+ ```
207
+
208
+ **When NOT to Use Aggregates:**
209
+
210
+ From the Clojure example:
211
+ ```clojure
212
+ ;; Transfer involves two accounts, but they don't form an aggregate
213
+ ;; because accounts can be modified independently of transfers.
214
+ ;; Instead, transfer-money is a domain service.
215
+ ```
216
+
217
+ Not everything that's related should be an aggregate. Ask:
218
+ - Do these objects need to change together in a single transaction?
219
+ - Do invariants span these objects?
220
+ - Can one exist without the other?
221
+
222
+ **Size Guideline:**
223
+ - Keep aggregates small
224
+ - Prefer smaller aggregates with eventual consistency between them
225
+ - Large aggregates create contention and performance issues
226
+
227
+ ### Domain Services
228
+
229
+ **Definition:** Operations that don't naturally belong to any single entity or value object.
230
+
231
+ **When to Use:**
232
+ - Operation involves multiple aggregates or entities
233
+ - Operation doesn't conceptually belong to one object
234
+ - Named after an activity/operation, not a thing
235
+
236
+ **Clojure Pattern:**
237
+ ```clojure
238
+ ;; Domain Service: transfer-money
239
+ ;; Involves: two Account entities + one Transfer entity
240
+ ;; Doesn't belong exclusively to any one of them
241
+
242
+ (defn transfer-money
243
+ "Domain service for transferring money between accounts.
244
+ Pure function that returns domain events describing the changes."
245
+ [transfer-number from-account to-account amount]
246
+ (let [debit (dm/make-debit (:number from-account) amount)
247
+ credit (dm/make-credit (:number to-account) amount)
248
+ debited-account (dm/debit-account from-account debit)
249
+ credited-account (dm/credit-account to-account credit)
250
+ posted-transfer (dm/post-transfer transfer-number debit credit)]
251
+ ;; Return domain event describing all changes
252
+ {:debited-account debited-account
253
+ :credited-account credited-account
254
+ :posted-transfer posted-transfer}))
255
+ ```
256
+
257
+ **Domain Service vs Application Service:**
258
+
259
+ | Domain Service | Application Service |
260
+ |----------------|---------------------|
261
+ | Pure function | Coordinates effects |
262
+ | Domain logic | Orchestration |
263
+ | Returns events/new states | Persists changes |
264
+ | Part of domain model | Uses domain model |
265
+ | No dependencies on infrastructure | Uses repositories, external services |
266
+
267
+ ### Repositories
268
+
269
+ **Definition:** Provides the illusion of an in-memory collection of aggregates, abstracting persistence.
270
+
271
+ **Purpose:**
272
+ - Encapsulate data access logic
273
+ - Provide aggregate-oriented persistence
274
+ - Separate domain model from persistence concerns
275
+
276
+ **Key Characteristics:**
277
+ - Operate at aggregate boundaries (save/load whole aggregates)
278
+ - Provide lookup by ID
279
+ - Hide database/storage implementation
280
+ - Return domain entities, not data structures
281
+
282
+ **Clojure Pattern:**
283
+ ```clojure
284
+ ;; Repository provides aggregate-level persistence
285
+
286
+ (defn get-account
287
+ "Returns Account entity by account-number, nil if not found."
288
+ [account-number]
289
+ (when-let [account-row (fetch-from-db account-number)]
290
+ (account-row->domain-entity account-row)))
291
+
292
+ (defn save-account
293
+ "Persists Account aggregate."
294
+ [account]
295
+ (let [account-row (domain-entity->account-row account)]
296
+ (persist-to-db account-row)))
297
+
298
+ ;; Application Service orchestrates:
299
+ (defn transfer-money-use-case [transfer-number from-id to-id amount]
300
+ ;; Get aggregates
301
+ (let [from-account (repository/get-account from-id)
302
+ to-account (repository/get-account to-id)
303
+ ;; Execute domain logic (pure)
304
+ result (domain-service/transfer-money transfer-number
305
+ from-account
306
+ to-account
307
+ amount)]
308
+ ;; Persist changes
309
+ (repository/commit-transfer-event result)
310
+ result))
311
+ ```
312
+
313
+ **Repository vs DAO/Active Record:**
314
+ - Repository: Aggregate-oriented, domain-driven
315
+ - DAO: Table-oriented, database-driven
316
+
317
+ ### Bounded Contexts
318
+
319
+ **Definition:** An explicit boundary within which a domain model applies. The same term can mean different things in different contexts.
320
+
321
+ **Purpose:**
322
+ - Manage complexity by dividing the domain
323
+ - Allow different models in different parts of the system
324
+ - Prevent model corruption from mixing concepts
325
+
326
+ **Key Insights:**
327
+ - Ubiquitous language is only ubiquitous within a context
328
+ - Same word can have different meanings in different contexts
329
+ - Make boundaries explicit and protect them
330
+
331
+ **Example:**
332
+
333
+ ```
334
+ Bounded Context: Sales
335
+ - "Customer": Entity with sales history, credit limit
336
+ - "Product": Catalog item with pricing
337
+
338
+ Bounded Context: Shipping
339
+ - "Customer": Just name and shipping address
340
+ - "Product": Weight and dimensions for shipping
341
+
342
+ Bounded Context: Accounting
343
+ - "Customer": Billing entity with payment terms
344
+ - "Product": Revenue recognition rules
345
+ ```
346
+
347
+ **Context Mapping:**
348
+
349
+ Relationships between bounded contexts:
350
+
351
+ 1. **Shared Kernel**: Two contexts share a subset of the model
352
+ 2. **Customer/Supplier**: Downstream context depends on upstream
353
+ 3. **Conformist**: Downstream accepts upstream model as-is
354
+ 4. **Anti-Corruption Layer**: Translate between contexts to protect model
355
+ 5. **Separate Ways**: Contexts are completely independent
356
+
357
+ **Clojure Organization:**
358
+ ```clojure
359
+ ;; Each bounded context as a separate namespace or library
360
+
361
+ ;; sales/
362
+ ;; domain_model.clj
363
+ ;; domain_services.clj
364
+ ;; application_service.clj
365
+ ;; repository.clj
366
+
367
+ ;; shipping/
368
+ ;; domain_model.clj
369
+ ;; domain_services.clj
370
+ ;; application_service.clj
371
+ ;; repository.clj
372
+
373
+ ;; Integration via anti-corruption layer:
374
+ (defn sales-customer->shipping-customer [sales-customer]
375
+ {:name (:name sales-customer)
376
+ :shipping-address (:default-address sales-customer)})
377
+ ```
378
+
379
+ ### Domain Events
380
+
381
+ **Definition:** Something important that happened in the domain that domain experts care about.
382
+
383
+ **Characteristics:**
384
+ - Named in past tense (OrderPlaced, PaymentProcessed, AccountDebited)
385
+ - Immutable facts
386
+ - Contain data about what happened
387
+ - Can trigger reactions in same or different bounded contexts
388
+
389
+ **Uses:**
390
+ 1. **Within a bounded context**: Decouple domain logic
391
+ 2. **Between bounded contexts**: Integration and eventual consistency
392
+ 3. **Event Sourcing**: Store events as source of truth
393
+
394
+ **Clojure Pattern:**
395
+ ```clojure
396
+ ;; Domain events describe state changes
397
+
398
+ (s/def :debited-account/event #{:debited-account})
399
+
400
+ (s/def :account/debited-account
401
+ (s/keys :req-un [:debited-account/event
402
+ :account/number
403
+ :debited-account/amount-value]))
404
+
405
+ (defn debit-account [account debit]
406
+ ;; Returns domain event describing the change
407
+ (if (valid-debit? account debit)
408
+ {:event :debited-account
409
+ :number (:number account)
410
+ :amount-value (-> debit :amount :value)}
411
+ (throw (ex-info "Can't debit account" {...}))))
412
+
413
+ ;; Application service interprets events:
414
+ (defn handle-transfer [result]
415
+ (let [{:keys [debited-account credited-account posted-transfer]} result]
416
+ ;; Persist events
417
+ (persist-event debited-account)
418
+ (persist-event credited-account)
419
+ (persist-event posted-transfer)
420
+ ;; Trigger side effects
421
+ (send-notification (:number debited-account))
422
+ (update-balance-cache debited-account credited-account)))
423
+ ```
424
+
425
+ ## Functional DDD Architecture
426
+
427
+ ### Layered Architecture (Functional Style)
428
+
429
+ **Domain Layer (Functional Core):**
430
+ - Pure functions
431
+ - No IO, no side effects
432
+ - Specs for entities, value objects, aggregates
433
+ - Domain services as pure transformations
434
+ - Returns new values or domain events
435
+
436
+ **Application Layer (Imperative Shell):**
437
+ - Orchestrates use cases
438
+ - Fetches data via repositories
439
+ - Calls pure domain functions
440
+ - Interprets domain events
441
+ - Performs side effects
442
+
443
+ **Infrastructure Layer:**
444
+ - Repositories (persistence)
445
+ - External services (HTTP, message queues)
446
+ - Framework integrations
447
+
448
+ **Example Structure:**
449
+ ```clojure
450
+ ;; Domain Layer (pure)
451
+ (ns my-app.domain.model
452
+ (:require [clojure.spec.alpha :as s]))
453
+
454
+ (s/def ::entity ...)
455
+ (defn make-entity [...] ...)
456
+ (defn update-entity [entity change] ...)
457
+
458
+ ;; Application Layer (orchestration + effects)
459
+ (ns my-app.application
460
+ (:require [my-app.domain.model :as model]
461
+ [my-app.infrastructure.repository :as repo]))
462
+
463
+ (defn use-case [inputs]
464
+ (let [entity (repo/get-entity (:id inputs))
465
+ updated (model/update-entity entity inputs)]
466
+ (repo/save-entity updated)
467
+ updated))
468
+
469
+ ;; Infrastructure Layer (IO)
470
+ (ns my-app.infrastructure.repository)
471
+
472
+ (defn get-entity [id]
473
+ ;; Database access
474
+ ...)
475
+
476
+ (defn save-entity [entity]
477
+ ;; Database write
478
+ ...)
479
+ ```
480
+
481
+ ### Functional Core, Imperative Shell
482
+
483
+ **Principle:** Maximize pure functional code, minimize and isolate side effects.
484
+
485
+ **Pattern:**
486
+ 1. **Shell** reads inputs (IO)
487
+ 2. **Shell** calls pure **Core** with data
488
+ 3. **Core** returns results (pure computation)
489
+ 4. **Shell** performs effects based on results
490
+
491
+ ```clojure
492
+ ;; CORE: Pure domain logic
493
+ (defn calculate-order-total [order]
494
+ (reduce + (map :price (:items order))))
495
+
496
+ (defn apply-discount [total discount-rules customer]
497
+ ;; Pure calculation
498
+ ...)
499
+
500
+ ;; SHELL: Application service with effects
501
+ (defn process-order [order-request]
502
+ ;; Read (effect)
503
+ (let [customer (db/get-customer (:customer-id order-request))
504
+ products (db/get-products (:product-ids order-request))
505
+
506
+ ;; Pure domain logic
507
+ order (domain/create-order customer products order-request)
508
+ total (domain/calculate-order-total order)
509
+ final-total (domain/apply-discount total @discount-rules customer)
510
+
511
+ ;; Write (effects)
512
+ saved-order (db/save-order (assoc order :total final-total))]
513
+
514
+ ;; More effects
515
+ (email/send-confirmation customer saved-order)
516
+ (analytics/track-order saved-order)
517
+
518
+ saved-order))
519
+ ```
520
+
521
+ ## Practical Patterns from Clojure DDD
522
+
523
+ ### Using Specs for Invariants
524
+
525
+ ```clojure
526
+ ;; Spec defines valid states
527
+ (s/def ::transfer
528
+ (s/and
529
+ (s/keys :req-un [::id ::number ::debit ::credit ::creation-date])
530
+ ;; Invariants as predicates:
531
+ (fn [{:keys [debit credit]}]
532
+ (and
533
+ ;; Same amount debited and credited
534
+ (= (:amount debit) (:amount credit))
535
+ ;; Different accounts
536
+ (not= (:number debit) (:number credit))))))
537
+
538
+ ;; Constructor validates on creation
539
+ (defn make-transfer [transfer-number debit credit]
540
+ (s/assert ::transfer
541
+ {:id (random-uuid)
542
+ :number transfer-number
543
+ :debit debit
544
+ :credit credit
545
+ :creation-date (java.util.Date.)}))
546
+ ```
547
+
548
+ ### Event-Driven State Changes
549
+
550
+ Instead of mutating entities, return events describing changes:
551
+
552
+ ```clojure
553
+ ;; Instead of: (set! account.balance new-balance)
554
+
555
+ ;; Return event:
556
+ (defn debit-account [account debit]
557
+ (if (can-debit? account debit)
558
+ {:event :debited-account
559
+ :account-number (:number account)
560
+ :amount (:value debit)
561
+ :timestamp (now)}
562
+ (throw (ex-info "Cannot debit" {...}))))
563
+
564
+ ;; Repository interprets event:
565
+ (defn commit-debit-event [event]
566
+ (swap! state update-in
567
+ [:accounts (:account-number event)]
568
+ apply-debit
569
+ (:amount event)))
570
+ ```
571
+
572
+ ### Eventual Consistency Trade-offs
573
+
574
+ From the Clojure example:
575
+ ```clojure
576
+ ;; With eventual consistency:
577
+ ;; - Can process 2000 concurrent transfers
578
+ ;; - Never double-spend (total money is conserved)
579
+ ;; - BUT: Account can go temporarily negative
580
+
581
+ ;; Business decision: Is this acceptable?
582
+ ;; - Maybe: charge overdraft fee
583
+ ;; - Maybe: customer can cover temporarily
584
+ ;; - Trade-off: massive scalability for eventual consistency
585
+
586
+ ;; If not acceptable: Use strong consistency (transactions, locks)
587
+ ;; Trade-off: Lower scalability but immediate consistency
588
+ ```
589
+
590
+ ## Anti-Patterns to Avoid
591
+
592
+ ### Anemic Domain Model
593
+
594
+ **Problem:** Entities are just data holders; all logic in services.
595
+
596
+ ```clojure
597
+ ;; ANEMIC (Bad):
598
+ (s/def ::account (s/keys :req-un [::number ::balance]))
599
+
600
+ (defn debit-account [account amount]
601
+ (update account :balance - amount)) ; No validation!
602
+
603
+ ;; Service does all validation:
604
+ (defn debit-with-validation [account amount]
605
+ (if (>= (:balance account) amount)
606
+ (debit-account account amount)
607
+ (throw ...)))
608
+ ```
609
+
610
+ **Better:** Put invariants in domain model
611
+ ```clojure
612
+ ;; Domain model validates:
613
+ (s/def ::account
614
+ (s/and
615
+ (s/keys :req-un [::number ::balance])
616
+ #(>= (:balance %) 0))) ; Invariant: balance never negative
617
+
618
+ (defn debit-account [account amount]
619
+ (let [new-account (update account :balance - amount)]
620
+ (s/assert ::account new-account))) ; Validates invariant
621
+ ```
622
+
623
+ ### God Aggregates
624
+
625
+ **Problem:** Massive aggregates that do everything.
626
+
627
+ **Better:** Keep aggregates small and focused.
628
+
629
+ ### Missing Bounded Contexts
630
+
631
+ **Problem:** One model trying to serve all use cases.
632
+
633
+ **Better:** Separate models for separate contexts.
634
+
635
+ ## Checklist for DDD Implementation
636
+
637
+ - [ ] **Ubiquitous Language**: Same terms in code and conversations
638
+ - [ ] **Entities have clear identity**: Can track over time
639
+ - [ ] **Value objects are immutable**: Defined by attributes
640
+ - [ ] **Aggregates enforce invariants**: Consistency boundaries are clear
641
+ - [ ] **Operations through aggregate roots**: No direct access to internals
642
+ - [ ] **Domain services for multi-aggregate operations**: Pure functions
643
+ - [ ] **Repositories at aggregate level**: Load/save whole aggregates
644
+ - [ ] **Application services orchestrate**: Thin layer calling domain
645
+ - [ ] **Bounded contexts are explicit**: Clear boundaries and integration
646
+ - [ ] **Domain events capture important happenings**: Past tense, immutable
647
+
648
+ ## Key Takeaways
649
+
650
+ 1. **Start with language**: Listen to domain experts, extract ubiquitous language
651
+ 2. **Entities vs Value Objects**: Identity vs attributes
652
+ 3. **Aggregates are consistency boundaries**: Keep them small
653
+ 4. **Domain logic is pure**: No side effects in domain model/services
654
+ 5. **Repository abstracts persistence**: Aggregate-oriented
655
+ 6. **Bounded contexts divide complexity**: Different models for different contexts
656
+ 7. **Domain events decouple**: Integration and eventual consistency
657
+ 8. **Functional core, imperative shell**: Maximize purity, isolate effects
658
+
659
+ Always ask:
660
+ - What does the domain expert call this?
661
+ - Does this have identity or just value?
662
+ - What are the invariants?
663
+ - What's the consistency boundary?
664
+ - Which context are we in?