Skip to content

Commit 2b14aa9

Browse files
committed
feat: add real-world analogies to various domain concepts for enhanced understanding
1 parent 2ff6a46 commit 2b14aa9

7 files changed

Lines changed: 76 additions & 0 deletions

File tree

docs/guide/core/aggregates.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -252,6 +252,10 @@ Let's break down the key elements:
252252

253253
## Determining Aggregate Boundaries
254254

255+
::: tip Real-world Analogy
256+
Drawing aggregate boundaries is like deciding what goes in each drawer of your desk. Items that you always use together (pen and notepad) go in the same drawer, while items used separately (printer paper and USB drives) go in different drawers. The goal is optimal organization based on use patterns.
257+
:::
258+
255259
One of the most challenging aspects of using aggregates is deciding what should be included within a single aggregate boundary. This decision impacts both consistency and performance.
256260

257261
### Guidelines for Good Aggregate Design
@@ -363,6 +367,10 @@ console.log(draftOrder.status); // Still 'DRAFT'
363367

364368
## Invariants: Protecting Business Rules
365369

370+
::: tip Real-world Analogy
371+
Think of invariants like the safety features in a car. No matter what the driver does, certain rules must be followed—the car won't shift into reverse while moving forward, won't start without a key, and the airbags must be operational. These are non-negotiable safety invariants built into the system. Similarly, business invariants protect your domain from entering invalid states.
372+
:::
373+
366374
Invariants are business rules that must always be satisfied within an aggregate. They're checked whenever an aggregate is created or updated:
367375

368376
```javascript
@@ -469,6 +477,10 @@ eventBus.on('OrderPlaced', async (event) => {
469477

470478
## Inter-Aggregate References
471479

480+
::: tip Real-world Analogy
481+
Think of how government agencies reference people. Instead of physically bringing a person to their office (direct reference), they use a Social Security Number or ID (reference by identity). This allows independence between systems—the DMV doesn't need to involve the person when the tax office wants to reference them. Similarly, aggregates reference each other by ID rather than directly including the objects.
482+
:::
483+
472484
A critical rule of aggregates is that they should reference other aggregates by identity, not by direct object reference. This maintains proper boundaries and prevents tangled object graphs:
473485

474486
```javascript

docs/guide/core/domain-events.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,10 @@ Domain events offer several powerful benefits for your architecture:
3737

3838
### 1. Decoupling Components
3939

40+
::: tip Real-world Analogy
41+
Think of how a fire alarm works. The alarm doesn't know who will respond—it could be residents evacuating, the fire department coming, or security guards checking the situation. The alarm just signals that fire was detected, and the appropriate parties react based on their responsibilities. This is exactly how domain events create loose coupling in your system.
42+
:::
43+
4044
Events create loose coupling between parts of your system. The component that emits an event doesn't need to know who's listening or what they'll do with the information.
4145

4246
```
@@ -499,6 +503,10 @@ OrderPlacedIntegration.create({
499503

500504
## Event Sourcing
501505

506+
::: tip Real-world Analogy
507+
Think of a basketball game's scoreboard versus the play-by-play record. The scoreboard shows the current state (final score 88-82), but the play-by-play log shows every basket, foul, and timeout that led to that score. If there's ever a dispute, you can replay the log to verify the score. Event sourcing works the same way—storing every significant action rather than just the final result.
508+
:::
509+
502510
Event sourcing is a powerful pattern where events become the primary source of truth, with the current state derived from the event history.
503511

504512
<!-- DIAGRAM: Flow showing: Events → Event Store → Replay → Current State, with branches for snapshots and projections -->

docs/guide/core/domain-services.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,10 @@ Domain services offer several benefits:
3131

3232
## When to Use Domain Services
3333

34+
::: tip Real-world Analogy
35+
Consider a real estate transaction. Neither the buyer nor the seller handles all the complex legal and financial tasks—instead, they work with a neutral third party (escrow service) that coordinates the process. Similarly, domain services handle operations that don't naturally belong to a single entity, providing a neutral space for coordinating complex business processes.
36+
:::
37+
3438
You should use a domain service when:
3539

3640
- An operation involves multiple aggregates
@@ -84,6 +88,10 @@ A domain service:
8488

8589
## Creating Domain Services with DomainDrivenJS
8690

91+
::: tip Real-world Analogy
92+
Think of domain services like professional specialists (accountants, lawyers, etc.) who perform specific services but don't own any business assets themselves. You bring them information, they apply their expertise and return results, but they maintain no permanent state of their own. Similarly, domain services apply expertise to domain objects without maintaining their own persistent state.
93+
:::
94+
8795
DomainDrivenJS provides a simple way to create domain services:
8896

8997
```javascript
@@ -387,6 +395,10 @@ const InventoryAllocationService = domainService({
387395

388396
## Domain Services and Dependencies
389397

398+
::: tip Real-world Analogy
399+
Think of domain services with dependencies like specialized hospital departments. A surgical team needs anesthesiology, nursing, and sterilization services to function effectively. These dependencies are explicit, required, and their absence would prevent the surgery from proceeding safely. Similarly, domain services declare what other services they require to perform their operations properly.
400+
:::
401+
390402
Domain services may depend on other domain services:
391403

392404
```javascript

docs/guide/core/entities.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,10 @@ Entities are common in everyday life:
3232

3333
### Identity Matters
3434

35+
::: tip Real-world Analogy
36+
Consider twins who look identical but are different people. Even with the same appearance, same birthday, and same parents, they are separate individuals with distinct identities. Similarly, entities in code might have identical attributes but remain distinct because of their unique identifiers.
37+
:::
38+
3539
The key characteristic of entities is that identity matters more than attributes:
3640

3741
```javascript
@@ -225,6 +229,10 @@ This immutability helps prevent bugs from unexpected state changes and makes you
225229

226230
## Entity Lifecycle
227231

232+
::: tip Real-world Analogy
233+
Think of a caterpillar's lifecycle: egg → caterpillar → chrysalis → butterfly. At each stage, it's the same organism with the same identity, but with very different attributes and capabilities. Similarly, entities move through defined lifecycle states while maintaining their core identity.
234+
:::
235+
228236
Entities typically have a lifecycle with different states:
229237

230238
![State Diagram](/images/entity_lifecycle.png)
@@ -328,6 +336,10 @@ History tracking can be useful for audit trails, debugging, and understanding th
328336

329337
## Value Objects Within Entities
330338

339+
::: tip Real-world Analogy
340+
Think of how a car (entity) contains components like an engine, wheels, and a navigation system. Each component has its own specifications but doesn't have an independent identity outside the car. Similarly, entities can contain value objects that encapsulate complex attributes but don't have independent identities.
341+
:::
342+
331343
Entities often contain value objects for complex attributes. DomainDrivenJS makes this integration seamless:
332344

333345
```javascript

docs/guide/core/repositories.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,10 @@ Let's break down the components:
101101

102102
## Repository Adapters
103103

104+
::: tip Real-world Analogy
105+
Repository adapters are like power adapters for international travel. Whether you're in Europe, Asia, or America with different wall outlets, the adapter ensures your device gets the power it needs. Similarly, repository adapters ensure your domain objects work with different storage systems (MongoDB, SQL, memory) without changing your core code.
106+
:::
107+
104108
A key strength of the repository pattern is its abstraction of storage details through adapters. DomainDrivenJS provides adapters for different storage systems:
105109

106110
```javascript
@@ -162,6 +166,10 @@ await productRepo.delete('123e4567-e89b-12d3-a456-426614174000');
162166

163167
## Standard Repository Methods
164168

169+
::: tip Real-world Analogy
170+
Repository methods are like the standard services offered by a storage facility. You can store items (save), retrieve them (find), check if you have something in storage (exists), replace items (update), or remove them altogether (delete)—all without needing to know how the facility is organized internally.
171+
:::
172+
165173
All DomainDrivenJS repositories come with these standard methods:
166174

167175
| Method | Description |
@@ -361,6 +369,10 @@ describe('ProductService', () => {
361369

362370
## Best Practices
363371

372+
::: tip Real-world Analogy
373+
Good repository design is like a well-organized kitchen. Ingredients (data) are stored logically (separate repositories for different types), the chef (domain service) requests ingredients as needed, and kitchen staff (repositories) know exactly where to find and how to prepare each ingredient. No one needs to know that milk is on the third shelf of the walk-in cooler—they just ask for milk.
374+
:::
375+
364376
1. **Repository per aggregate**: Create one repository for each aggregate root, not for every entity
365377
2. **Keep repositories focused**: Each repository should handle one type of entity
366378
3. **Abstract storage details**: Don't expose storage-specific code through repositories

docs/guide/core/specifications.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,10 @@ Specifications offer several benefits:
3131

3232
## How Specifications Work
3333

34+
::: tip Real-world Analogy
35+
Think of a specification like a recipe. When shopping (querying), you use the recipe to determine what ingredients to buy. When cooking (validation), you use the same recipe to verify you have the right ingredients before starting. Similarly, specifications serve dual purposes—helping you find objects that match criteria and verifying that specific objects meet those same criteria.
36+
:::
37+
3438
Specifications have two main responsibilities:
3539

3640
1. **Validation**: Check if a domain object satisfies a business rule (`isSatisfiedBy` method)
@@ -100,6 +104,10 @@ Let's break down the components:
100104

101105
## Composing Specifications
102106

107+
::: tip Real-world Analogy
108+
Think of specifications like filters for a camera. You might have one filter for reducing glare, another for enhancing colors, and a third for softening focus. By combining these filters, you create complex effects from simple components. Similarly, you can combine simple specifications like "premium customer" and "recent purchaser" to create complex business rules while keeping each component focused and reusable.
109+
:::
110+
103111
The true power of specifications emerges when you compose them to create more complex specifications:
104112

105113
```javascript
@@ -126,6 +134,10 @@ The composed specifications behave just like atomic specifications, with both `i
126134

127135
## Using Specifications with Repositories
128136

137+
::: tip Real-world Analogy
138+
Using specifications with repositories is like using a search engine with advanced filters. Instead of scrolling through thousands of results, you apply filters for price range, brand, rating, etc. The search engine (repository) knows how to translate your selection (specification) into an efficient query, saving you from examining each item individually.
139+
:::
140+
129141
Specifications shine when used with repositories for querying data:
130142

131143
```javascript

docs/guide/core/value-objects.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,10 @@ Think of a value object as answering the question "what" rather than "which one.
2424

2525
### Value Objects vs. Primitives
2626

27+
::: tip Real-world Analogy
28+
Using primitives instead of value objects is like writing out street directions as "turn left, go straight, turn right" instead of saying "go to Central Park." The value object gives meaning and context to what would otherwise be just raw data.
29+
:::
30+
2731
Many developers default to using primitive types (strings, numbers, booleans) to represent domain concepts. This leads to what's called "primitive obsession" - a code smell where primitives are used for domain concepts that deserve their own type.
2832

2933
```javascript
@@ -368,6 +372,10 @@ try {
368372

369373
## Composing Value Objects
370374

375+
::: tip Real-world Analogy
376+
Think of LEGO blocks. Each block is useful on its own, but by combining smaller blocks, you can build more complex and meaningful structures. Similarly, you can compose simple value objects into more complex ones that better represent your domain concepts.
377+
:::
378+
371379
Complex domain concepts can be composed of other value objects:
372380

373381
```javascript

0 commit comments

Comments
 (0)