0Pricing
TypeScript Academy · レッスン

ドメインモデリング — 集約、不変条件、サービス

ブランド付き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フィードバックを取得できます。ローカル設定は不要です。

このコースのすべてのレッスン

  1. 単位の取り違えを防ぐブランド型・opaque型
  2. タグ付きIDとドメインモデリングのパターン
  3. ドメインモデリング — 集約、不変条件、サービス
← TypeScript Academyに戻る