nw-fp-domain-modeling
nw-fp-domain-modeling is a Claude Code skill for building type-safe domain models using functional programming patterns. It teaches algebraic data types (AND for records, OR for choice types), domain-specific wrappers around primitives, smart constructors with validation, and structural type design that makes invalid states impossible to construct. Use this skill when designing domain models where compile-time type safety prevents runtime errors and invalid business states.
git clone --depth 1 https://github.com/nWave-ai/nWave /tmp/nw-fp-domain-modeling && cp -r /tmp/nw-fp-domain-modeling/nWave/skills/nw-fp-domain-modeling ~/.claude/skills/nw-fp-domain-modelingSKILL.md
# FP Domain Modeling
Domain modeling with types. Make illegal states unrepresentable, workflows as pipelines, error handling at the type level.
Cross-references: [fp-principles](../nw-fp-principles/SKILL.md) | [fp-hexagonal-architecture](../nw-fp-hexagonal-architecture/SKILL.md) | [fp-algebra-driven-design](../nw-fp-algebra-driven-design/SKILL.md)
---
## 1. The Two Building Blocks
[STARTER]
All domain types compose from two operations:
- **AND (Record Types)**: Value has ALL of these fields. Order requires CustomerInfo AND ShippingAddress AND OrderLines.
- **OR (Choice Types)**: Value is ONE OF these alternatives. ProductCode is either WidgetCode OR GizmoCode.
Combined recursively, these express virtually any domain structure.
---
## 2. Domain Wrappers for Primitives
[STARTER]
Never use primitives directly in the domain model. Each domain concept gets its own wrapper type.
**What**: Wrap primitives so the compiler distinguishes CustomerId from OrderId.
**When**: Every primitive with domain meaning.
**Why**: Prevents accidental mixing (compiler rejects comparing CustomerId with OrderId). Each wrapper carries its own validation rules. The type name IS the documentation.
---
## 3. Validated Construction (Smart Constructors)
[STARTER]
Raw constructor is private. A `create` function validates input and returns a Result type, making validation failure explicit.
**Pattern**: UnitQuantity must be between 1 and 1000. Its `create` function rejects values outside that range. A companion `value` function provides read access to the inner primitive.
**When**: Every domain wrapper with validation rules.
**Why**: Once constructed, a value is guaranteed valid. No defensive checks deeper in the code.
---
## 4. Making Illegal States Unrepresentable
[STARTER]
The central design guideline. Instead of flags and runtime checks, model the domain so invalid states cannot be constructed.
### Replace Flags with Distinct Types
Instead of `{ EmailAddress; IsVerified: bool }`, create separate `VerifiedEmailAddress` and `UnverifiedEmailAddress` types. Functions requiring verification take `VerifiedEmailAddress`, making misuse a compile error.
### Replace Optional Fields with Choice Types
Instead of `{ Email: option; Address: option }` (where both could be None), create: `EmailOnly | AddressOnly | EmailAndAddress`. The "at least one required" rule becomes structurally enforced.
### NonEmptyList for "At Least One" Rules
Define a type guaranteeing at least one element. Order with `OrderLines: NonEmptyList<OrderLine>` cannot have zero lines.
---
## 5. Workflows as Functions
[STARTER]
Every business workflow is a single function: Command in, Events out.
```
PlaceOrderWorkflow : PlaceOrderCommand -> AsyncResult<PlaceOrderEvent list>
```
### Pipeline Composition
Workflows decompose into steps, each transforming one document type into the next:
```
UnvalidatedOrder -> ValidatedOrder -> PricedOrder -> Events
```
Each step is stateless, pure, has single input/output type, and is independently testable. The workflow assembles by piping steps together.
**Why**: Pipeline makes the business process visible. Each step name is a domain concept.
---
## 6. Document Lifecycle as State Types
[INTERMEDIATE]
Rather than one Order type with flags, create separate types for each lifecycle stage:
- `UnvalidatedOrder` (raw input, all fields strings)
- `ValidatedOrder` (all fields checked)
- `PricedOrder` (prices calculated)
A top-level Order choice type unifies all states. New states (e.g., `Refunded`) added without breaking existing code.
**When**: Domain entities with distinct lifecycle stages where different data is available at each stage.
---
## 7. State Machines with Types
[INTERMEDIATE]
When an entity has distinct states with different data and different allowed operations, model each state as a separate type.
```
ShoppingCart = EmptyCart | ActiveCart of ActiveCartData | PaidCart of PaidCartData
```
Transition functions take the choice type, pattern-match on current state, return new state.
**Benefits**: All states explicit | each state has own data | invalid transitions prevented by types | pattern matching warnings reveal unhandled edge cases.
---
## 8. Error-Track Pipelines (Railway Pattern)
[INTERMEDIATE]
Each function returns a Result type. Pipeline short-circuits on first failure.
```
rawInput
|> validateOrder -- Result<ValidOrder, Error>
|> bind calculateTotal -- Result<PricedOrder, Error>
|> bind checkInventory -- Result<ConfirmedOrder, Error>
|> bind chargePayment -- Result<PaidOrder, Error>
|> map generateReceipt -- Result<Receipt, Error>
```
**Key combinators**:
- **map**: Transform success value (one-track into two-track)
- **bind**: Chain a function that itself returns Result
- **mapError**: Transform error value
- **tee**: Perform side effect without changing value (logging)
### Error Classification
| Category | Examples | Strategy |
|---|---|---|
| Domain Errors | Validation failure, out of stock | Model as types, return via Result |
| Panics | Out of memory, null reference | Throw exceptions, catch at top level |
| Infrastructure Errors | Network timeout, auth failure | Case-by-case |
### Unifying Error Types
Each step may have its own error type. Define a common error choice type and use `mapError` to lift step errors before composing.
### Collecting All Errors (Applicative Validation)
[ADVANCED]
Standard bind short-circuits on first error. For validation where you want ALL errors, use Applicative style -- runs all validations and accumulates errors into a list. See [fp-principles](../nw-fp-principles/SKILL.md) section 5.
**When**: Form validation | batch input checking | any place user needs all errors at once.
---
## 9. Modeling Dependencies
[INTERMEDIATE]
Each workflow step declares exactly the functions it needs as parameters:
```
CheckProductCodeExists : ProductCode -> bool
GetProductPrice : ProductReview dimensions for validating agent quality - template compliance, safety, testing, and priority validation
Review dimensions for validating agent quality - template compliance, safety, testing, and priority validation
Review dimensions for acceptance test quality - happy path bias, GWT compliance, business language purity, coverage completeness, walking skeleton user-centricity, priority validation, observable behavior assertions, traceability coverage, and walking skeleton boundary proof
Detailed 5-phase workflow for creating agents - from requirements analysis through validation and iterative refinement
5-layer testing approach for agent validation including adversarial testing, security validation, and prompt injection resistance
Architectural style selection decision matrices, trade-off analysis, structural enforcement rules, and combination patterns. Load when choosing or evaluating architecture styles.
Comprehensive architecture patterns, methodologies, quality frameworks, and evaluation methods for solution architects. Load when designing system architecture or selecting patterns.
Canonical AT completeness gate — research-anchored 7-category taxonomy (C1-C7) + 15-item mechanical checklist. Paradigm-neutral. Drives acceptance-designer reviewer verdict deterministically.