ドメインの合成
より大きなモデルを構築します
「ドメインの合成」はCoddyKit上の無料Scala for Backend Engineering & Functional Programmingレッスンです。 これはレッスン4/4です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはScala for Backend Engineering & Functional Programming学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 Scala for Backend Engineering & Functional Programmingコースには全4レッスンが含まれています。
より大きなモデルを構築する
実際のドメインは、より小さな部品から組み立てられます。ドメインの合成とは、適切にモデル化された ADT、newtype、検証済みの値を組み合わせ、より大きく、それでも正しさを保つ構造を作ることです。
- 小さく焦点を絞った型を、より大きな型へ合成できます。
- 各部品は、それぞれの不変条件を維持します。
case class Money(cents: Long)
case class Product(name: String, price: Money)
object Main:
def main(args: Array[String]): Unit =
println(Product("Book", Money(1999)))プロダクトのネスト
case class をネストしてプロダクト型を合成します。各レベルで構造を追加しながら、読みやすさも保てます。
case class Address(city: String, zip: String)
case class Customer(name: String, address: Address)
object Main:
def main(args: Array[String]): Unit =
val c = Customer("Ada", Address("London", "E1"))
println(c.address.city)コレクションとの合成
List、Map、Setを使って、ドメイン内の一対多の関係をモデル化します。
case class Item(name: String, qty: Int)
case class Order(id: Int, items: List[Item])
object Main:
def main(args: Array[String]): Unit =
val order = Order(1, List(Item("pen", 2), Item("ink", 1)))
println(order.items.map(_.qty).sum)サム型の合成
より大きな選択肢は、より小さな選択肢を組み合わせて作れます。enum のバリアント自体に別の ADT を含めることで、判断を幾層にも重ねられます。
enum Payment:
case Card(last4: String)
case Cash
enum Checkout:
case Completed(payment: Payment)
case Abandoned
object Main:
def main(args: Array[String]): Unit =
val c: Checkout = Checkout.Completed(Payment.Card("1234"))
println(c)ドメインを合成する関数
ドメイン操作は、入力型から出力型への純粋関数です。それらを合成することでワークフローを構築できます。
case class Cart(total: Long)
case class Receipt(total: Long, tax: Long)
object Main:
def checkout(c: Cart): Receipt =
Receipt(c.total, (c.total * 0.2).toLong)
def main(args: Array[String]): Unit =
println(checkout(Cart(1000)))検証の合成
各フィールドをスマートコンストラクターで構築する場合は、for 内包表記で合成します。すべての部分が検証に成功した場合にのみ、オブジェクト全体が作成されます。
object V:
def name(s: String): Option[String] = if s.nonEmpty then Some(s) else None
def age(n: Int): Option[Int] = if n >= 0 then Some(n) else None
case class Person(name: String, age: Int)
object Main:
def make(n: String, a: Int): Option[Person] =
for nm <- V.name(n); ag <- V.age(a) yield Person(nm, ag)
def main(args: Array[String]): Unit =
println(Main.make("Bob", 30))
println(Main.make("", 30))複合データの畳み込み
fold、または map と sum を組み合わせて、合成された構造を集約します。データの形が計算方法を導きます。
case class Line(price: Long, qty: Int)
case class Invoice(lines: List[Line])
object Main:
def total(inv: Invoice): Long =
inv.lines.foldLeft(0L)((acc, l) => acc + l.price * l.qty)
def main(args: Array[String]): Unit =
val inv = Invoice(List(Line(100, 2), Line(50, 4)))
println(total(inv))不変モデルの更新
copyを使って変更を合成します。copyは、指定したフィールドを置き換えた新しい値を返します。元の値は変更されません。
case class Account(id: Int, balance: Long)
object Main:
def deposit(a: Account, amt: Long): Account =
a.copy(balance = a.balance + amt)
def main(args: Array[String]): Unit =
val a = Account(1, 100)
println(deposit(a, 50))ステートマシンのモデル化
サム型と遷移関数を合成して、合法な遷移だけが存在するステートマシンをモデル化します。
enum Light:
case Red, Green, Yellow
object Main:
def next(l: Light): Light = l match
case Light.Red => Light.Green
case Light.Green => Light.Yellow
case Light.Yellow => Light.Red
def main(args: Array[String]): Unit =
println(next(Light.Red))
println(next(Light.Green))ドメインと操作のレイヤー化
データ型は純粋に保ち、振る舞いは関数またはメソッドに配置します。合成されたモデルと一連の変換を組み合わせると、小規模なドメインライブラリになります。
case class Temp(celsius: Double):
def toF: Double = celsius * 9 / 5 + 32
object Main:
def main(args: Array[String]): Unit =
val t = Temp(25.0)
println(t.toF)全体を組み合わせる
完全なモデルでは、newtype、プロダクト、サム、コレクション、検証済みの構築を合成します。その結果、誤って使いにくく、理解しやすいモデルになります。
- まず小さな型を構築します。
- それらを集約へ合成します。
- 操作を純粋関数として表現します。
case class Sku(value: String)
case class Line(sku: Sku, qty: Int)
case class Basket(lines: List[Line]):
def count: Int = lines.map(_.qty).sum
object Main:
def main(args: Array[String]): Unit =
val b = Basket(List(Line(Sku("A"), 2), Line(Sku("B"), 3)))
println(b.count)理解度チェック
ドメインの合成についての理解度を確認しましょう。
まとめ
ドメインを合成する方法を学びました。
- プロダクトとサムをネストして、豊かな構造をモデル化します。
- コレクションを一対多の関係に使用します。
for内包表記で検証を合成します。copyで不変のまま更新します。- データを純粋に保ち、操作を関数として表現します。
case class User(id: Int, name: String)
case class Post(author: User, text: String)
object Main:
def main(args: Array[String]): Unit =
val p = Post(User(1, "Ada"), "hello")
println(p.copy(text = "hi"))よくある質問
「ドメインの合成」レッスンは無料ですか?
はい。「ドメインの合成」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、Scala for Backend Engineering & Functional Programmingコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 Scala for Backend Engineering & Functional Programmingコースには全4レッスンが含まれています。
「ドメインの合成」で何を学びますか?
より大きなモデルを構築します ブラウザで直接実行するハンズオンコードでScala for Backend Engineering & Functional Programmingを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。
Scala for Backend Engineering & Functional Programmingを始めるのに経験は必要ですか?
事前経験は必要ありません。CoddyKitのScala for Backend Engineering & Functional Programmingは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン4/4です。
「ドメインの合成」レッスンにはどのくらい時間がかかりますか?
ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。
このScala for Backend Engineering & Functional Programmingレッスンでコードを書いて実行できますか?
はい。すべてのScala for Backend Engineering & Functional Programmingレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。