Files
next.orly.dev/.claude/skills/domain-driven-design/references/strategic-patterns.md
mleku c9a03db395
Some checks failed
Go / build-and-release (push) Has been cancelled
Fix Blossom CORS headers and add root-level upload routes (v0.36.12)
- Add proper CORS headers for Blossom endpoints including X-SHA-256,
  X-Content-Length, X-Content-Type headers required by blossom-client-sdk
- Add root-level Blossom routes (/upload, /media, /mirror, /report, /list/)
  for clients like Jumble that expect Blossom at root
- Export BaseURLKey from pkg/blossom for use by app handlers
- Make blossomRootHandler return URLs with /blossom prefix so blob
  downloads work via the registered /blossom/ route
- Remove Access-Control-Allow-Credentials header (not needed for * origin)
- Add Access-Control-Expose-Headers for X-Reason and other response headers

Files modified:
- app/blossom.go: Add blossomRootHandler, use exported BaseURLKey
- app/server.go: Add CORS handling for blossom paths, register root routes
- pkg/blossom/server.go: Fix CORS headers, export BaseURLKey
- pkg/blossom/utils.go: Minor formatting
- pkg/version/version: Bump to v0.36.12

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-24 11:32:52 +01:00

359 lines
13 KiB
Markdown

# Strategic DDD Patterns
Strategic DDD patterns address the large-scale structure of a system: how to divide it into bounded contexts, how those contexts relate, and how to prioritize investment across subdomains.
## Bounded Context
### Definition
A Bounded Context is an explicit boundary within which a domain model exists. Inside the boundary, all terms have specific, unambiguous meanings. The same term may mean different things in different bounded contexts.
### Why It Matters
- **Linguistic clarity** - "Customer" in Sales means something different than "Customer" in Shipping
- **Model isolation** - Changes to one model don't cascade across the system
- **Team autonomy** - Teams can work independently within their context
- **Focused complexity** - Each context solves one set of problems well
### Identification Heuristics
1. **Language divergence** - When stakeholders use the same word differently, there's a context boundary
2. **Department boundaries** - Organizational structure often mirrors domain structure
3. **Process boundaries** - End-to-end business processes often define context edges
4. **Data ownership** - Who is the authoritative source for this data?
5. **Change frequency** - Parts that change together should stay together
### Example: E-Commerce Platform
| Context | "Order" means... | "Product" means... |
|---------|------------------|-------------------|
| **Catalog** | N/A | Displayable item with description, images, categories |
| **Inventory** | N/A | Stock keeping unit with quantity and location |
| **Sales** | Shopping cart ready for checkout | Line item with price |
| **Fulfillment** | Shipment to be picked and packed | Physical item to ship |
| **Billing** | Invoice to collect payment | Taxable good |
### Implementation Patterns
#### Separate Deployables
Each bounded context as its own service/application.
```
catalog-service/
├── src/domain/Product.ts
└── src/infrastructure/CatalogRepository.ts
sales-service/
├── src/domain/Product.ts # Different model!
└── src/domain/Order.ts
```
#### Module Boundaries
Bounded contexts as modules within a monolith.
```
src/
├── catalog/
│ └── domain/Product.ts
├── sales/
│ └── domain/Product.ts # Different model!
└── shared/
└── kernel/Money.ts # Shared kernel
```
## Context Map
### Definition
A Context Map is a visual and documented representation of how bounded contexts relate to each other. It makes integration patterns explicit.
### Integration Patterns
#### Partnership
Two contexts develop together with mutual dependencies. Changes are coordinated.
```
┌─────────────┐ Partnership ┌─────────────┐
│ Catalog │◄──────────────────►│ Inventory │
└─────────────┘ └─────────────┘
```
**Use when**: Two teams must succeed or fail together.
#### Shared Kernel
A small, shared model that multiple contexts depend on. Changes require agreement from all consumers.
```
┌─────────────┐ ┌─────────────┐
│ Sales │ │ Billing │
└──────┬──────┘ └──────┬──────┘
│ │
└─────────► Money ◄──────────────┘
(shared kernel)
```
**Use when**: Core concepts genuinely need the same model.
**Danger**: Creates coupling. Keep shared kernels minimal.
#### Customer-Supplier
Upstream context (supplier) provides data/services; downstream context (customer) consumes. Supplier considers customer needs.
```
┌─────────────┐ ┌─────────────┐
│ Catalog │───── supplies ────►│ Sales │
│ (upstream) │ │ (downstream)│
└─────────────┘ └─────────────┘
```
**Use when**: One context clearly serves another, and the supplier is responsive.
#### Conformist
Downstream adopts upstream's model without negotiation. Upstream doesn't accommodate downstream needs.
```
┌─────────────┐ ┌─────────────┐
│ External │───── dictates ────►│ Our App │
│ API │ │ (conformist)│
└─────────────┘ └─────────────┘
```
**Use when**: Upstream won't change (third-party API), and their model is acceptable.
#### Anti-Corruption Layer (ACL)
Translation layer that protects a context from external models. Transforms data at the boundary.
```
┌─────────────┐ ┌───────┐ ┌─────────────┐
│ Legacy │───────►│ ACL │───────►│ New System │
│ System │ └───────┘ └─────────────┘
```
**Use when**: Upstream model would pollute downstream; translation is worth the cost.
```typescript
// Anti-Corruption Layer example
class LegacyOrderAdapter {
constructor(private legacyApi: LegacyOrderApi) {}
translateOrder(legacyOrder: LegacyOrder): Order {
return new Order({
id: OrderId.from(legacyOrder.order_num),
customer: this.translateCustomer(legacyOrder.cust_data),
items: legacyOrder.line_items.map(this.translateLineItem),
// Transform legacy status codes to domain concepts
status: this.mapStatus(legacyOrder.stat_cd),
});
}
private mapStatus(legacyCode: string): OrderStatus {
const mapping: Record<string, OrderStatus> = {
'OP': OrderStatus.Open,
'SH': OrderStatus.Shipped,
'CL': OrderStatus.Closed,
};
return mapping[legacyCode] ?? OrderStatus.Unknown;
}
}
```
#### Open Host Service
A context provides a well-defined protocol/API for others to consume.
```
┌─────────────┐
┌──────────►│ Reports │
│ └─────────────┘
┌───────┴───────┐ ┌─────────────┐
│ Catalog API │──►│ Search │
│ (open host) │ └─────────────┘
└───────┬───────┘ ┌─────────────┐
└──────────►│ Partner │
└─────────────┘
```
**Use when**: Multiple downstream contexts need access; worth investing in a stable API.
#### Published Language
A shared language format (schema) for communication between contexts. Often combined with Open Host Service.
Examples: JSON schemas, Protocol Buffers, GraphQL schemas, industry standards (HL7 for healthcare).
#### Separate Ways
Contexts have no integration. Each solves its needs independently.
**Use when**: Integration cost exceeds benefit; duplication is acceptable.
### Context Map Notation
```
┌───────────────────────────────────────────────────────────────┐
│ CONTEXT MAP │
├───────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────┐ Partnership ┌─────────┐ │
│ │ Sales │◄────────────────────────────►│Inventory│ │
│ │ (U,D) │ │ (U,D) │ │
│ └────┬────┘ └────┬────┘ │
│ │ │ │
│ │ Customer/Supplier │ │
│ ▼ │ │
│ ┌─────────┐ │ │
│ │ Billing │◄──────────────────────────────────┘ │
│ │ (D) │ Conformist │
│ └─────────┘ │
│ │
│ Legend: U = Upstream, D = Downstream │
└───────────────────────────────────────────────────────────────┘
```
## Subdomain Classification
### Core Domain
The essential differentiator. This is where competitive advantage lives.
**Characteristics**:
- Unique to this business
- Complex, requires deep expertise
- Frequently changing as business evolves
- Worth significant investment
**Strategy**: Build in-house with best talent. Invest heavily in modeling.
### Supporting Subdomain
Necessary for the business but not a differentiator.
**Characteristics**:
- Important but not unique
- Moderate complexity
- Changes less frequently
- Custom implementation needed
**Strategy**: Build with adequate (not exceptional) investment. May outsource.
### Generic Subdomain
Solved problems with off-the-shelf solutions.
**Characteristics**:
- Common across industries
- Well-understood solutions exist
- Rarely changes
- Not a differentiator
**Strategy**: Buy or use open-source. Don't reinvent.
### Example: E-Commerce Platform
| Subdomain | Type | Strategy |
|-----------|------|----------|
| Product Recommendation Engine | Core | In-house, top talent |
| Inventory Management | Supporting | Build, adequate investment |
| Payment Processing | Generic | Third-party (Stripe, etc.) |
| User Authentication | Generic | Third-party or standard library |
| Shipping Logistics | Supporting | Build or integrate vendor |
| Customer Analytics | Core | In-house, strategic investment |
## Ubiquitous Language
### Definition
A common language shared by developers and domain experts. It appears in conversations, documentation, and code.
### Building Ubiquitous Language
1. **Listen to experts** - Use their terminology, not technical jargon
2. **Challenge vague terms** - "Process the order" → What exactly happens?
3. **Document glossary** - Maintain a living dictionary
4. **Enforce in code** - Class and method names use the language
5. **Refine continuously** - Language evolves with understanding
### Language in Code
```typescript
// Bad: Technical terms
class OrderProcessor {
handleOrderCreation(data: OrderData): void {
this.validateData(data);
this.persistToDatabase(data);
this.sendNotification(data);
}
}
// Good: Ubiquitous language
class OrderTaker {
placeOrder(cart: ShoppingCart): PlacedOrder {
const order = cart.checkout();
order.confirmWith(this.paymentGateway);
this.orderRepository.save(order);
this.domainEvents.publish(new OrderPlaced(order));
return order;
}
}
```
### Glossary Example
| Term | Definition | Context |
|------|------------|---------|
| **Order** | A confirmed purchase with payment collected | Sales |
| **Shipment** | Physical package(s) sent to fulfill an order | Fulfillment |
| **SKU** | Stock Keeping Unit; unique identifier for inventory | Inventory |
| **Cart** | Uncommitted collection of items a customer intends to buy | Sales |
| **Listing** | Product displayed for purchase in the catalog | Catalog |
### Anti-Pattern: Technical Language Leakage
```typescript
// Bad: Database terminology leaks into domain
order.setForeignKeyCustomerId(customerId);
order.persist();
// Bad: HTTP concerns leak into domain
order.deserializeFromJson(request.body);
order.setHttpStatus(200);
// Good: Domain language only
order.placeFor(customer);
orderRepository.save(order);
```
## Strategic Design Decisions
### When to Split a Bounded Context
Split when:
- Different parts need to evolve at different speeds
- Different teams need ownership
- Model complexity is becoming unmanageable
- Language conflicts are emerging within the context
Don't split when:
- Transaction boundaries would become awkward
- Integration cost outweighs isolation benefit
- Single team can handle the complexity
### When to Merge Bounded Contexts
Merge when:
- Integration overhead is excessive
- Same team owns both
- Models are converging naturally
- Separate contexts create artificial complexity
### Dealing with Legacy Systems
1. **Bubble context** - New bounded context with ACL to legacy
2. **Strangler fig** - Gradually replace legacy feature by feature
3. **Conformist** - Accept legacy model if acceptable
4. **Separate ways** - Rebuild independently, migrate data later