『関数型ドメインモデリング』勉強会 第2章 ドメインの理解

  • 2024.08.29
141
『関数型ドメインモデリング』勉強会 第2章 ドメインの理解

前章では以下のような内容について学んだ。

  • モデルを共有することの重要性
  • データ構造ではなく、ビジネスイベントやワークフローに焦点を当てる
  • モデルは「解決空間」で扱われ、境界づけられたコンテキストとして構成する
  • ビジネスドメインの共有メンタルモデルを定義する言語としてのユビキタス言語

本章では、ドメインエキスパートへのインタビューを通じて、ワークフローについて理解することを目指す。そしてそのワークフローを構造化された文章として表現する。

2.1 ドメインエキスパートへのインタビュー

受注部門の担当者(ドメインエキスパート)に対して、受注プロセスについてインタビューを行う。

担当者からは紙の注文書(p.26)を見せられるが、これを見て典型的なEコマースだと即断してはいけない。

担当者によれば「顧客は、自分が何を注文したいかをすでに正確に知っている」「我々は、顧客が製品コードと数量を入力できるシンプルな入力フォームが欲しいだけ」なのである。

先入観を持たず、顧客がシステムをどのように使用するかなど、何かについて結論を急がないようにする。

2.1.1 非機能要件の理解

はじめに、ワークフローの文脈や規模について話し合う。

  • 注文のトラフィックは一年を通じてかなり安定している
    • データ量の急激な変化を想定した設計をする必要がないことがわかる
  • 顧客はモノを買うことの専門家であり、営業日の終わりまでに顧客が自分の注文について確認できること
    • スピードよりも一貫性のほうが重要

ここでも先入観にとらわれずにドメインエキスパートの話を聞くことの重要性が示されている。

2.1.2 残りのワークフローについての理解

  • 見積依頼と注文は同じ依頼書を使うが、ワークフローが異なる
  • 製品コードで指定された製品が存在するか否かを「製品カタログ」を使って調べている
    • 製品カタログは、 もう1つの境界づけられたコンテキスト

2.1.3 インプットとアウトプットを考える

  • ワークフローのインプットは注文書
  • ワークフローのアウトプットは、ワークフローが生成するイベント
    • 他の境界づけられたコンテキストにおけるアクションに対するトリガーとなるもの

2.2 データベース駆動設計をしたいという衝動との戦い

データベースの経験が豊富な人であれば、すぐにOrder、 OrderLine、Customerなどの テーブルを思い浮かべ、それらの間の関連を図(p.29)で記述するかもしれないが、これ大きな間違い。

ドメイン駆動設計では、ドメインから設計を導き出す。データベースのスキーマや特定のストレージの実装は考慮しない。

そもそもユーザーは、データの永続化が必要であることは理解しても、それがどのように永続化されるかについては気にしない。DDD の用語では、 これを永続性非依存と呼ぶ(らしい)。

2.3 クラス駆動設計をしたいという衝動との戦い

クラス駆動の設計は、 データベース駆動の設計と 同じくらい危険である。

クラス図(p.30)で図示された設計では注文と見積を分離しており、現実の世界には存在しない人工的な基底クラス、 OrderBase を導入している。これはドメインエキスパートには理解できないシロモノだろう。

自分の技術的な指向をドメインに押しつけないようにすること。

2.4 ドメインの文書化

それでは、ドメインエキスパートとのインタビューで得られた要件をどうやって表現するか。

ここではUML などの視覚的な図は採用せず、構造化された文章によって表現している。テーブルやクラスを記述するわけではなく、ワークフローやデータ構造を文書化する。

一部を本書から引用すると、以下のようなものである(p.31~32)。

bounded context: Order-Taking

Workflow: "Place order"
  triggered by:
    "Order form received" event (when Quote is not checked)
  primary input:
    An order form
  other input:
    Product catalog
  output events:
    "Order Placed" event
bounded context: Order-Taking

data Order =
  CustomerInfo
  AND ShippingAddress
  AND BillingAddressAND list of OrderLines
  AND AmountToBill

2.5 受注のワークフローを深堀りする

  • 何が重要な優先事項か迷ったら、お金を稼ぐことを優先する
  • コンテキスト外の、サードパーティ製サービスとコミュニケーションを取る必要がある
  • 多くのビジネスプロセスにおいて、紙(注文書とか見積書とか)の山は非常に重要な要素
    • 技術的には「紙の山」はキューにうまく対応するが、この時点では技術的な詳細には触れないようにする
  • 製品カタログによって製品コードのチェックを行うが、これは別のコンテキストであり、境界づけられたコンテキストにおける自律性に関連する
  • 製品ごとに数量は整数だったり小数点を含んでいたりするが、ドメインエキスパートは、「浮動小数」 のようなプログラミング用語を使わない。「注文数量」といった言葉をユビキタス言語の用語にする必要がある
  • 担当者は、何かするたびに注文書に何らかのマークをつける。これをどのようにモデルに反映させるか

2.6 複雑さをドメインモデルで表現する

2.6.1 制約条件の表現

商品の数量(個数や重量)の制約を構造化文章で表現する。

data UnitQuantity = integer between 1 and 1000
data KilogramQuantity = decimal between 0.05 and 100.00

2.6.2 注文のライフサイクルを表現する

注文にはフェーズの遷移がある。未検証の郵便物⇒検証⇒価格付け。紙の注文書では、担当者は各フェーズでマークをつけて区別しているが、これをドメインモデルにも取り 込む。そのもっとも簡単な方法は、各フェーズに新しい名前を作ること。

こうして出来た最終的なモデルは、当初のものよりもずっと複雑になったが、それは

  • ビジネスの仕組みを素直に反映させているだけ
  • もしモデルが複雑でないならば、要件を適切に把握できていない可能性がある
  • このような区別をコードでも保持できれば、コードはドメインを正確に反映できることになる

2.6.3 ワークフローのステップを具体化する

このワークフローは、検証、価格設定など、より 小さなステップに分けられる 。それぞれのステップに、同じ入出力のアプローチを適用する。

まずワークフロー全体をいくつかのステップに分割して疑似コードで表現してみる。以下、本書から引用(p.39)。

workflow "Place Order" =
  input: OrderForm
  output:
    OrderPlaced event (put on a pile to send to other teams)
    OR InvalidOrder (put on appropriate pile)

  // ステップ 1
  do ValidateOrder
  If order is invalid then:
    add InvalidOrder to pile
    stop
  // ステップ 2
  do PriceOrder
  // ステップ 3
  do SendAcknowledgmentToCustomer
  // ステップ 4
  return OrderPlaced event (if no errors)

ここから「注文書の検証」ValidateOrder、「価格の計算」PriceOrder、「確認書を作成して送信」SendAcknowledgmentToCustomer の各ステップを詳細化していき、最後に次のコンテキストへの入力となるイベントを返す。

この要件を記述した文書はコードのようにも見えるが、ドメインエキスパートにも十分理解可能な範囲にとどまっている。