| title |
Your First Domain Model |
| id |
domain-modeling-hello-world |
| skillLevel |
beginner |
| applicationPatternId |
domain-modeling |
| summary |
Create a simple domain model using TypeScript interfaces and Effect to represent your business entities. |
| tags |
domain-modeling |
getting-started |
types |
interfaces |
|
| rule |
| description |
Start domain modeling by defining clear interfaces for your business entities. |
|
| author |
PaulJPhilp |
| related |
domain-modeling-with-brand |
domain-modeling-tagged-errors |
|
| lessonOrder |
3 |
Start by defining TypeScript interfaces that represent your business entities. Use descriptive names that match your domain language.
Good domain modeling:
- Clarifies intent - Types document what data means
- Prevents errors - Compiler catches wrong data usage
- Enables tooling - IDE autocompletion and refactoring
- Communicates - Code becomes documentation
import { Effect } from "effect"
// ============================================
// 1. Define domain entities as interfaces
// ============================================
interface User {
readonly id: string
readonly email: string
readonly name: string
readonly createdAt: Date
}
interface Product {
readonly sku: string
readonly name: string
readonly price: number
readonly inStock: boolean
}
interface Order {
readonly id: string
readonly userId: string
readonly items: ReadonlyArray<OrderItem>
readonly total: number
readonly status: OrderStatus
}
interface OrderItem {
readonly productSku: string
readonly quantity: number
readonly unitPrice: number
}
type OrderStatus = "pending" | "confirmed" | "shipped" | "delivered"
// ============================================
// 2. Create domain functions
// ============================================
const createUser = (email: string, name: string): User => ({
id: crypto.randomUUID(),
email,
name,
createdAt: new Date(),
})
const calculateOrderTotal = (items: ReadonlyArray<OrderItem>): number =>
items.reduce((sum, item) => sum + item.quantity * item.unitPrice, 0)
// ============================================
// 3. Use in Effect programs
// ============================================
const program = Effect.gen(function* () {
const user = createUser("alice@example.com", "Alice")
yield* Effect.log(`Created user: ${user.name}`)
const items: OrderItem[] = [
{ productSku: "WIDGET-001", quantity: 2, unitPrice: 29.99 },
{ productSku: "GADGET-002", quantity: 1, unitPrice: 49.99 },
]
const order: Order = {
id: crypto.randomUUID(),
userId: user.id,
items,
total: calculateOrderTotal(items),
status: "pending",
}
yield* Effect.log(`Order total: $${order.total.toFixed(2)}`)
return order
})
Effect.runPromise(program)
| Concept |
Explanation |
| Interfaces |
Define the shape of your domain entities |
| readonly |
Prevent accidental mutation |
| Union types |
Model finite states (OrderStatus) |
| Pure functions |
Create and transform entities |
- Use
readonly - Immutable by default
- Descriptive names - Match your business language
- Small interfaces - Single responsibility
- Union types - For finite states