Skip to content
Domain Modeling with Types
step 1/5

Reading — step 1 of 5

Learn

~2 min readAsync and Domain Modeling

F#'s discriminated unions + records + active patterns enable type-driven design: encode domain rules in the type system so impossible states are unrepresentable.

Bad: stringly-typed fields:

type User = {
    Email: string       // could be empty? unvalidated?
    Status: string      // could be anything
    Age: int             // could be negative
}

Good: types encode constraints:

type Email = private Email of string
module Email =
    let create (s: string) =
        if s.Contains "@" then Some (Email s) else None
    let value (Email s) = s

type UserStatus =
    | Active
    | Suspended of reason: string
    | Closed of closedAt: System.DateTime

type Age = private Age of int
module Age =
    let create n = if n >= 0 && n < 200 then Some (Age n) else None
    let value (Age n) = n

type User = {
    Email: Email          // can't be invalid — type guarantees it
    Status: UserStatus    // exactly one of three states
    Age: Age              // can't be negative
}

The private constructor + factory functions ("smart constructors") prevent invalid creation. The DU forces handling all cases. The age type encodes the domain rule.

The pattern:

  1. Wrap primitives in single-case DUs to give them a name and constraints
  2. Make invalid states unrepresentable through DUs (Active | Suspended | Closed, not IsActive: bool + IsSuspended: bool)
  3. Use Result<Success, Error> for fallible operations

Workflow style:

type OrderError =
    | InvalidEmail of string
    | OutOfStock of string
    | PaymentFailed of string

type OrderResult = Result<OrderConfirmation, OrderError>

let placeOrder (request: OrderRequest) : OrderResult =
    request
    |> validateEmail
    |> Result.bind validateInventory
    |> Result.bind processPayment
    |> Result.map confirmOrder

Each step takes Result, returns Result. Errors short-circuit cleanly. The type signatures document the workflow.

This is the heart of "Designing with types" / DDD-with-FP — popularized by Scott Wlaschin's Domain Modeling Made Functional.

Discussion

Ask a question, share an insight, or help someone who’s stuck.

Sign in to post a comment or reply.

Loading…