ドメインモデリング — 集約、不変条件、サービス
ブランド付きIDを持つCartなどの集約をモデル化し、ファクトリとサービスで不変条件を保証します
「ドメインモデリング — 集約、不変条件、サービス」はCoddyKit上の無料TypeScript Academyレッスンです。 これはレッスン3/3です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはTypeScript Academy学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 TypeScript Academyコースには全3レッスンが含まれています。
概要
目標: ブランド付き IDを使って Cart 集約をモデル化し、qty > 0 などのルールをファクトリとサービスで確実に適用します。
- ブランド付き ID でエンティティの境界を区別する
- ファクトリから Result の union を返す
- サービス関数で無効な状態遷移を防ぐ
エンティティとブランド
ブランド付き ID により、CartId と ProductId はどちらも string でありながら互換性がなくなります。これにより取り違えを防げます。
type Brand<Tag extends string, T> = T & { readonly __brand: Tag }
type CartId = Brand<"CartId", string>
type ProductId = Brand<"ProductId", string>
type Result<T> = { ok: true; value: T } | { ok: false; error: string }
const cartId = (s: string): CartId => s as CartId
const productId = (s: string): ProductId => s as ProductId集約と不変条件
ファクトリでルールを検証します。qty は > 0 でなければなりません。有効な Line オブジェクトだけを作成できます。
type Money = number & { readonly __brand: "Money" }
const money = (n: number): Money => n as Money
interface Line { productId: ProductId; qty: number; price: Money }
interface Cart { id: CartId; lines: Line[]; total: Money }
function makeLine(productId: ProductId, qty: number, price: Money): Result<Line> {
if (!Number.isInteger(qty) || qty <= 0) return { ok: false, error: "qty must be > 0" }
return { ok: true, value: { productId, qty, price } }
}
function makeCart(id: CartId): Cart { return { id, lines: [], total: money(0) } }サービス: アイテムを追加
サービス関数は安全な状態遷移を実行し、合計を再計算します。合計が無効な場合、状態を壊す代わりにエラーを返します。
function addItem(cart: Cart, line: Line): Result<Cart> {
const lines = [...cart.lines, line]
const totalNum = lines.reduce((s, x) => s + (x.qty * (x.price as number)), 0)
if (!Number.isFinite(totalNum) || totalNum < 0) return { ok: false, error: "invalid total" }
return { ok: true, value: { ...cart, lines, total: (totalNum as unknown) as Money } }
}
// Usage
const c0 = makeCart(cartId("c1"))
const l = makeLine(productId("p1"), 2, money(10))
const c1 = l.ok ? addItem(c0, l.value) : { ok: false, error: "bad line" }境界でのマッピング
API などの境界では DTO に変換し、再び読み込む際に検証します。ファクトリによってデータの正しさを保証します。
type CartDTO = { id: string; lines: { productId: string; qty: number; price: number }[]; total: number }
function serialize(c: Cart): CartDTO {
return {
id: c.id as unknown as string,
lines: c.lines.map(x => ({ productId: x.productId as unknown as string, qty: x.qty, price: x.price as unknown as number })),
total: c.total as unknown as number
}
}
function parse(dto: CartDTO): Result<Cart> {
const id = cartId(dto.id)
const lines: Line[] = []
for (const raw of dto.lines) {
const l = makeLine(productId(raw.productId), raw.qty, money(raw.price))
if (!l.ok) return { ok: false, error: l.error }
lines.push(l.value)
}
const totalNum = lines.reduce((s, x) => s + (x.qty * (x.price as number)), 0)
return { ok: true, value: { id, lines, total: money(totalNum) } }
}ヒント
ベストプラクティス:
- 検証をファクトリに集約します。
- サービス関数は純粋かつ決定的に保ちます。
- ブランドの変換はシステム境界でのみ行います。
不変条件の確認
簡単な確認: qty > 0 のような不変条件は、どのように適用すべきですか。
振り返り
振り返り: ブランド付き ID をファクトリやサービスと組み合わせることで、不変条件を適用できます。すべてのエントリーポイントで入力を検証するため、無効な状態は作成できません。
よくある質問
「ドメインモデリング — 集約、不変条件、サービス」レッスンは無料ですか?
はい。「ドメインモデリング — 集約、不変条件、サービス」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、TypeScript Academyコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 TypeScript Academyコースには全3レッスンが含まれています。
「ドメインモデリング — 集約、不変条件、サービス」で何を学びますか?
ブランド付きIDを持つCartなどの集約をモデル化し、ファクトリとサービスで不変条件を保証します ブラウザで直接実行するハンズオンコードでTypeScript Academyを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。
TypeScript Academyを始めるのに経験は必要ですか?
事前経験は必要ありません。CoddyKitのTypeScript Academyは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン3/3です。
「ドメインモデリング — 集約、不変条件、サービス」レッスンにはどのくらい時間がかかりますか?
ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。
このTypeScript Academyレッスンでコードを書いて実行できますか?
はい。すべてのTypeScript Academyレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。
このコースのすべてのレッスン
- 単位の取り違えを防ぐブランド型・opaque型
- タグ付きIDとドメインモデリングのパターン
- ドメインモデリング — 集約、不変条件、サービス