Atlas / Skills / nwave-ai / Nw Fp Domain Modeling

Nw Fp Domain ModelingSAFE

skills/nwave-ai/nw-fp-domain-modeling

AI agents that guide you from idea to working code, with you in control at every step.

Verdict
SAFE
Grade
B
Trust score
89 /100
Version
—
Hosts
—
License
MIT
Stars
617
01

Overview

AI agents that guide you from idea to working code, with you in control at every step.

Read from source at commit 1a864ec81cdeOBSERVED · 2026-10-14
Advertisement
02

What 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 
03

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.

LayerWhat it checksResult
L0Provenance & inventoryPASS
L1Static analysis of the codeNA
L2Instruction surface (what it tells the agent)PASS
L3Class-specific surfacePASS
L4Behavioural (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.

Audited 2026-10-14 · audit v0.4.1 · source sha 1a864ec81cdefull audit observations/trust-audit/skill/nwave-ai__nw-fp-domain-modeling.json · Report an issue / request a re-scan
04

Audit history

Every audit this skill has had.

DateSourceVerdictGradeScoreChange
2026-10-141a864ec81cdeSAFEB89first audit
05

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.

Advertisement