Nw Fp Domain ModelingSAFE
AI agents that guide you from idea to working code, with you in control at every step.
Overview
AI agents that guide you from idea to working code, with you in control at every step.
1a864ec81cdeOBSERVED · 2026-10-14What it tells the agent
The instruction file, verbatim from the audited commit — this is the text the model reads, and the surface the audit's instruction layer examines. Quoted here so you can judge it without cloning anything.
---
name: nw-fp-domain-modeling
agent: nw-functional-software-crafter
description: Domain modeling with algebraic data types, smart constructors, and type-level error handling
user-invocable: false
disable-model-invocation: true
---
# 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 Trust audit
SAFEgrade B · trust 89/100 Nothing in the source contradicts what it says it does. Grade A is reserved for packages that have also passed the behavioural sandbox.
| Layer | What it checks | Result |
|---|---|---|
| L0 | Provenance & inventory | PASS |
| L1 | Static analysis of the code | NA |
| L2 | Instruction surface (what it tells the agent) | PASS |
| L3 | Class-specific surface | PASS |
| L4 | Behavioural (sandbox) | SKIPPED |
What the source does
- Filesystem
- none-observed
- Network
- none-observed
- Shell
- none-observed
- Dependencies
- pinned
- Secrets in source
- none-found
Findings (0)
No findings outside the package's declared scope.
Gates applied: no_behavioural_pass.
1a864ec81cdefull audit observations/trust-audit/skill/nwave-ai__nw-fp-domain-modeling.json · Report an issue / request a re-scanAudit history
Every audit this skill has had.
| Date | Source | Verdict | Grade | Score | Change |
|---|---|---|---|---|---|
| 2026-10-14 | 1a864ec81cde | SAFE | B | 89 | first audit |
Questions
What does the Nw Fp Domain Modeling skill do?
AI agents that guide you from idea to working code, with you in control at every step.
Is Nw Fp Domain Modeling safe to install?
The audit found nothing in the source that contradicts what it says it does, and graded it B (89/100). Grade A is held back for packages that have also passed a sandboxed behavioural run, which is why a clean skill reads B.
What can Nw Fp Domain Modeling access on my machine?
The audit observed no filesystem, network or shell use at all in its source.
How current is this page?
The grade is for one exact copy of the source (1a864ec81cde), read on 2026-10-14. The repository is watched, and a new audit runs when it changes — this is the first audit.