<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>Rindrics Mumbles</title>
    <link>https://rindrics.com/</link>
    <description>Rindrics Mumbles</description>    
    <generator>Hugo -- gohugo.io</generator>
    <language>en</language>
    <lastBuildDate>Mon, 28 Sep 2026 00:00:00 +0900</lastBuildDate>
    <atom:link href="https://rindrics.com/index.xml" rel="self" type="application/rss+xml" />
    
    <item>
      <title>第 1 章: ドメイン駆動設計入門</title>
      <link>https://rindrics.com/books/domain-model-made-functional/introducing-ddd/</link>
      <pubDate>Wed, 13 Nov 2024 00:00:00 +0900</pubDate>
      
      <guid>https://rindrics.com/books/domain-model-made-functional/introducing-ddd/</guid>
      <description>&lt;p&gt;&amp;ldquo;1. Introducing Domain-Driven Design&amp;rdquo; from &amp;ldquo;&lt;a href=&#34;https://rindrics.com/books/domain-model-made-functional/&#34;&gt;Domain Modeling Made Functional: Tackle Software Complexity with Domain-Driven Design and F#&lt;/a&gt;&amp;rdquo; &amp;#x1f4da;&lt;/p&gt;
&lt;p&gt;ソフトウェア開発者の仕事とは:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;not only コードを書くこと&lt;/li&gt;
&lt;li&gt;but also &lt;strong&gt;ソフトウェアを通して問題を解決すること&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;要件が不明瞭だったり、設計が悪ければコーディングをどれだけ頑張ろうとよい価値は得られない&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;つまり garbage in, garbage out
&lt;ul&gt;
&lt;li&gt;&amp;#x1f4dd; この表現はソフトウェア自体への入力と出力に対して使われることが多いが、ここでは input が「要件」、output を「得られる価値」というかなり高レイヤな意味で使われている&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;ldquo;garbage in&amp;rdquo; を減らすのに必要なもの:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;明解なコミュニケーション&lt;/li&gt;
&lt;li&gt;共有ドメイン知識に基づく設計アプローチ&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;ドメイン駆動設計（DDD）をによってこれらを実現できる。&lt;/p&gt;
&lt;h1 id=&#34;共有モデルの重要性&#34;&gt;共有モデルの重要性&lt;/h1&gt;
&lt;p&gt;まず開発者が問題を正しく理解することが重要&lt;/p&gt;
&lt;p&gt;とりうるアプローチを 3 つ紹介:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;仕様書・要件定義書を整備する &amp;#x1f4a3;&lt;/li&gt;
&lt;li&gt;ドメインエキスパートを開発プロセスに入れる &amp;#x26a0;&amp;#xfe0f;&lt;/li&gt;
&lt;li&gt;ドメイン駆動設計 &amp;#x2705;&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&#34;1-仕様書要件定義書を整備する-bomb&#34;&gt;1. 仕様書・要件定義書を整備する &amp;#x1f4a3;&lt;/h2&gt;
&lt;p&gt;これはだめなやり方。&lt;/p&gt;
&lt;p&gt;理由:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;ドメインエキスパートと開発チームの距離が遠くなってしまう &amp;#x274c;&lt;/li&gt;
&lt;li&gt;&amp;ldquo;伝言ゲーム&amp;rdquo; のようなことが実際に起こり、これはプロジェクトにおいては致命的 &amp;#x274c;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;2-ドメインエキスパートを開発プロセスに組み込む-warning&#34;&gt;2. ドメインエキスパートを開発プロセスに組み込む &amp;#x26a0;&amp;#xfe0f;&lt;/h2&gt;
&lt;p&gt;いわゆるアジャイルなやり方。一見良さそうに思えるがベストではない。&lt;/p&gt;
&lt;p&gt;誤りをすぐに修正できるようにするために、ドメインエキスパートを開発プロセスのフィードバックループに組み入れる。&lt;/p&gt;
&lt;p&gt;デメリット:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;開発者の働きがドメインエキスパートのメンタルモデルの単なる翻訳者でしかなくなってしまう &amp;#x274c;&lt;/li&gt;
&lt;li&gt;必ず抜け落ちてしまう情報がある &amp;#x274c;&lt;/li&gt;
&lt;li&gt;将来、ドメインエキスパートからの入力なしにコードをメンテする開発者は容易に方針を誤ってしまう &amp;#x274c;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;3-ドメイン駆動設計-white_check_mark&#34;&gt;3. ドメイン駆動設計 &amp;#x2705;&lt;/h2&gt;
&lt;p&gt;プロジェクトに関わる人々（下記）が&lt;strong&gt;共通のメンタルモデル&lt;/strong&gt;を共有した状態で開発を進める:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;ドメインエキスパート&lt;/li&gt;
&lt;li&gt;開発者&lt;/li&gt;
&lt;li&gt;ステークホルダー&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;ソフトウェアのコードはメンタルモデルを直接反映したものになるので、DDD では &amp;ldquo;翻訳&amp;rdquo; という作業は存在し得ない。&lt;/p&gt;
&lt;p&gt;メリット:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;市場投入スピード &amp;#x2705;&lt;/li&gt;
&lt;li&gt;得られるビジネス価値の大きさ &amp;#x2705;&lt;/li&gt;
&lt;li&gt;無駄の排除 &amp;#x2705;&lt;/li&gt;
&lt;li&gt;スムーズなメンテナンス &amp;#x2705;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;（コラムでは、開発者自身がドメインエキスパートになる重要性が示されている）&lt;/p&gt;
&lt;p&gt;共有メンタルモデルを作るためのガイドライン:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;データ構造ではなく、ビジネスイベントとワークフローに着目せよ&lt;/li&gt;
&lt;li&gt;ドメインを小さいサブドメインに分割せよ&lt;/li&gt;
&lt;li&gt;それぞれのサブドメインについてモデルを作れ&lt;/li&gt;
&lt;li&gt;会話やコードで使われる共通言語を作れ&lt;/li&gt;
&lt;/ul&gt;
&lt;h1 id=&#34;ビジネスイベントからドメインを理解する&#34;&gt;ビジネスイベントからドメインを理解する&lt;/h1&gt;
&lt;p&gt;共有メンタルモデルの構築はどこから始めたらいい？ → イベントから始めよ &amp;#x2705;データ構造から始めるな &amp;#x274c;&lt;/p&gt;
&lt;p&gt;理由:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;ビジネスがデータを&lt;strong&gt;ただ持っているだけ&lt;/strong&gt;で終わることは有り得ないため&lt;/li&gt;
&lt;li&gt;ビジネスは何らかの方法でデータを&lt;strong&gt;変換&lt;/strong&gt;するため&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;ビジネスプロセスとは、データやドキュメントの変換処理の集まり → これらの変換がどう行われ、どう関連しているかを理解することが重要&lt;/p&gt;
&lt;p&gt;使われずにただ置かれているだけのデータは何の価値も生まない。
これらのデータから価値を引き出すためのプロセスはどうやって始まるか？ → &lt;strong&gt;ドメインイベント&lt;/strong&gt;によって始まる&lt;/p&gt;
&lt;p&gt;ドメインイベントの例:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;外部トリガー&lt;/li&gt;
&lt;li&gt;スケジュールに基づいたトリガー&lt;/li&gt;
&lt;li&gt;観測に基づくトリガー&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;ドメインイベントは事実である変化しないため必ず過去形で表現される。&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;（中略）&lt;/p&gt;
&lt;p&gt;下記の 2 節で、イベントストーミングの説明と実例が書いてある:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Using Event Storming to Discover the Domain&lt;/li&gt;
&lt;li&gt;Discovering the Domain: An Order-Taking System&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;イベントストーミング自体を理解するには&lt;a href=&#34;https://leanpub.com/introducing_eventstorming&#34;&gt;提唱者の解説&lt;/a&gt;を読むほうがいい。&lt;/p&gt;
&lt;p&gt;イベントストーミングで重要な点:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;「&amp;ldquo;我々&amp;rdquo; vs &amp;ldquo;彼ら&amp;rdquo; 思考」から脱却できること&lt;/li&gt;
&lt;li&gt;意見が合わなかったら、それは問題ではなくチャンス&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&#34;イベントを境界にまで拡張する&#34;&gt;イベントを境界にまで拡張する&lt;/h2&gt;
&lt;p&gt;要件の取りこぼしを防ぐために下記に注意を払う:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;最初のイベントと思っていたイベントをトリガーする他のイベントがないか&lt;/li&gt;
&lt;li&gt;最後のイベントと思っていたイベントにトリガーされる後続のイベントはないか&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;全てを一気に変える必要はない。最も価値を生む一部分から取りかかれ。&lt;/p&gt;
&lt;h2 id=&#34;コマンドを記述する&#34;&gt;コマンドを記述する&lt;/h2&gt;
&lt;p&gt;イベントストーミングによってイベントが見えるようになった。
それらのイベントはどのように発生するのか？ → そのイベントが起こることを誰かが要求したから&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;全てのイベントがコマンドに紐付いている必要はない。スケジュールによるトリガーや監視に基づくトリガーなどがあるため。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;DDD ではこれらの要求を「コマンド」という。
コマンドは常に命令形で表現される。
紛らわしいが、&lt;a href=&#34;https://ja.wikipedia.org/wiki/Command_%E3%83%91%E3%82%BF%E3%83%BC%E3%83%B3&#34;&gt;OO のコマンドパターン&lt;/a&gt;とは違う。&lt;/p&gt;
&lt;p&gt;コマンドが成功するとワークフローがトリガーされ、ドメインイベントが作られる。&lt;/p&gt;
&lt;p&gt;例:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;「X を起こせ」というコマンドによってワークフローが X を起こし、「X が起こった」というドメインイベントが発行される&lt;/li&gt;
&lt;li&gt;「注文書を会社 A に送れ」というコマンドによってワークフローが注文書を送ると、「注文書が送信された」というドメインインベントが発行される&lt;/li&gt;
&lt;li&gt;コマンド「注文を確定せよ」 → ドメインイベント「注文が確定された」&lt;/li&gt;
&lt;li&gt;コマンド「配送を顧客 B に送れ」 → 「配送が送られた」&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;#x1f4dd; 「配送が送られた」は日本語としておかしいが、DDD ではモデルを記述する用語（ユビキタス言語）が重要なのでこのままにしておく。原著が英語なので限界がある。自然な日本語にしようとして「配送」の代わりに「商品」とするのは絶対に NG。いずれにせよ、ドメインエキスパートが使う言葉に合わせることが重要。&lt;/p&gt;
&lt;p&gt;これから、コマンド、ワークフロー、イベントの組み合わせでビジネスプロセスを記述していく。
これはまさに関数プログラミングの動きと同じ。&lt;/p&gt;
&lt;h1 id=&#34;ドメインをサブドメインに分ける&#34;&gt;ドメインをサブドメインに分ける&lt;/h1&gt;
&lt;p&gt;イベントストーミングによってイベントとコマンドを洗い出したことで、多数のビジネスプロセスの中身を理解できた状態になった。
しかし全体像がまだ整理されていない。
コードを書き始める&lt;strong&gt;前に&lt;/strong&gt;整理する必要がある。&lt;/p&gt;
&lt;p&gt;本文中では、受注プロセスを例として分割の様子が示されている。&lt;/p&gt;
&lt;p&gt;受注プロセスとは:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;受注&lt;/li&gt;
&lt;li&gt;配送&lt;/li&gt;
&lt;li&gt;請求&lt;/li&gt;
&lt;li&gt;その他&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;部署が既に分かれていれば、サブドメイン分割の強力な手がかりとなる。&lt;/p&gt;
&lt;p&gt;ドメインとは？ → DDD において、一貫した知識領域のこと。
&amp;hellip;わかりにくい。
言い換えると、ドメインエキスパートが専門性を持っている領域。
この理解はかなり実用的。なぜなら辞書で「請求」を調べて頭を悩ませるのではなく、シンプルに「請求部門のドメインエキスパートがやっていること」と表現できるようになるため。&lt;/p&gt;
&lt;p&gt;ドメインは重複しうるため、分割する際には注意が必要。
はっきり分けたくなるが、現実世界はそれほど単純ではない。&lt;/p&gt;
&lt;p&gt;ドメインは少し重複しているので、例えば受注部門の担当者は請求部門や配送部門の業務を少しだけ知っている必要がある。&lt;/p&gt;
&lt;h1 id=&#34;境界づけられたコンテキストを使って解決策を作る&#34;&gt;境界づけられたコンテキストを使って解決策を作る&lt;/h1&gt;
&lt;p&gt;ドメインの情報全てを反映する必要はない。
得られた情報の中から、解こうとしている問題に関するもののみを抽出する。ほとんどは無関係なはず&lt;/p&gt;
&lt;p&gt;なので「問題領域」と「解決領域」を分け、両者を別物として扱う必要がある:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;問題領域は現実世界&lt;/li&gt;
&lt;li&gt;解決領域はドメインモデル&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;問題領域は設計プロセスを経て解決領域になる&lt;/p&gt;
&lt;p&gt;なぜ「サブシステム」でなく「境界づけられたコンテキスト」という表現なのか？&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;理由: 解決策を考える際に、本当に重要なものに集中できるようになるため&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;コンテキストと境界を常に意識せよ。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;コンテキストが重要な理由: それぞれのコンテキストは解決策に独自の知識を反映するから
コンテキスト内では言語は共通し、デザインも一貫しているが、これらはコンテキストの外では使えなくなる&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;境界が重要な理由: 現実世界では境界はぼやけているが、ソフトウェアの場合にはそれぞれを独立して開発できるようにするために分割したい。ソフトウェアでは API を使うなどして達成できるが、裏を返せばそのように作ったドメインモデルは現実世界に比べて情報量が減るということ。しかしこれは複雑さを減らし、メンテナンス性を手に入れるためのトレードオフ&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;問題領域のドメインは必ずしも解決領域のコンテキストと一対一対応しない:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;あるドメインが複数の境界づけられたコンテキストに分割されることもある&lt;/li&gt;
&lt;li&gt;複数のドメインが一つの境界づけられたコンテキストになるケースもある（レガシーソフトウェア）
&lt;ul&gt;
&lt;li&gt;例: ある企業が「受注」と「請求」をひとまとめに扱うシステムを既にインストールしていた場合、このレガシーシステムと連携するにはそれを一つの境界づけられたコンテキストとして扱う必要がある&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;ドメインを分けるなら、それぞれの境界づけられたコンテキストが明確な責務を持つ必要がある&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;理由: モデルを実装するとき、境界づけられたコンテキストはそのままソフトウェアコンポーネント（DLL、サービス、名前空間など）になるため&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;コンテキストを正しくする&#34;&gt;コンテキストを正しくする&lt;/h2&gt;
&lt;p&gt;聞くは易し、実行するは難し。
というか DDD で1番難しいのがこの境界を正しく定めることで、これは科学ではなく一種の芸術といえる。&lt;/p&gt;
&lt;p&gt;ガイドライン:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;【ドメインエキスパートの声を聞け】彼らが共通言語を話し、同じ問題に注目しているならばおそらく同じサブドメインにいる。それが境界づけられたコンテキストになる&lt;/li&gt;
&lt;li&gt;【既存チームや部署の境界に着目せよ】ビジネスが何をドメイン・サブドメインと認識しているかの強力な手がかり。必ずしも一致はしない（部署内で違うことしてたり、他部署とコラボしたり）&lt;/li&gt;
&lt;li&gt;【境界づけられたコンテキストの「境界」に着目せよ】スコープクリープに気をつけろ。要求が変化する複雑なプロジェクトでは、無慈悲なまでに境界を守る必要がある。大きすぎる境界は何もないのと同じ。よいフェンスはよい隣人を作る&lt;/li&gt;
&lt;li&gt;【自律可能性が生まれるように設計せよ】足を結ばれた 2 人のランナーは独立した 2 人のランナーよりも速くなることはない。それぞれの境界づけられたコンテキストを独立して開発できるようにしろ&lt;/li&gt;
&lt;li&gt;【ビジネスフローの摩擦の摩擦を最小化するように設計せよ】複数の境界づけられたコンテキストと関わるビジネスワークフローがそれらによって遅くなっているなら、デザインを損ねてでもワークフローをスムーズにしろ。純粋なデザインよりもビジネスと顧客が重要&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;変わらない設計などない。いかなるモデルも、ビジネス要件が変わるたびに進化する必要がある。これは受注システムに要件を追加する例として 13 章で話す。&lt;/p&gt;
&lt;h2 id=&#34;コンテキストマップをつくる&#34;&gt;コンテキストマップをつくる&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;コンテキストマップ&lt;/strong&gt;とは:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;境界づけられたコンテキスト間の関係を示した全体像。これによって、コンテキスト同士の関係性について技術的詳細に囚われずにコミュニケーションできるようになる&lt;/li&gt;
&lt;li&gt;コンテキストマップは高次に保つ。複雑なシステムにおいては、それぞれのコンテキストの詳細にフォーカスした個別のマップが必要になる&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;#x1f4dd; 境界づけられたコンテキスト同士が連携するならば、当然それらのコンテキストは、交換可能な共有メッセージのフォーマットについて合意している必要がある。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;フォーマットの決定については主に上流側のコンテキストが主導権を持つ
&lt;ul&gt;
&lt;li&gt;例外: 【レガシーシステムが下流側にある場合など】上流側のコンテキストが、下流側のコンテキストが要求するフォーマットに合わせないといけない。その場合には翻訳コンポーネントが必要&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;最も重要な境界づけられたコンテキストに着目せよ&#34;&gt;最も重要な境界づけられたコンテキストに着目せよ&lt;/h2&gt;
&lt;p&gt;ここまでで、いくつか境界づけられたコンテキストを見つけた。
ドメインでの作業をすすめる過程でも、追加で見つかるだろう。
それらは等しく重要だろうか？&lt;/p&gt;
&lt;p&gt;どこから着手すべきだろう？ → &lt;strong&gt;需要なところから&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;全ての境界づけられたコンテキストを同時に実装したりするのは NG &amp;#x274c;&lt;/li&gt;
&lt;li&gt;優先順位を付ける必要がある。何が重要かについて、コンセンサスがないこともあるが（各部署は自分こそが最重要だと思っている）&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;重要とは: &lt;strong&gt;ビジネス優位性&lt;/strong&gt;を生むドメインや&lt;strong&gt;売上げ&lt;/strong&gt;を生むドメイン&lt;/p&gt;
&lt;p&gt;ドメインの大分類:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;コアドメイン: ビジネスの根幹となるもの&lt;/li&gt;
&lt;li&gt;補助ドメイン: 業務に必要であるもののビジネスの核ではないもの&lt;/li&gt;
&lt;li&gt;汎用ドメイン: 他のビジネスでも見られるもの（認証や配送など）。これは安全にアウトソースできる&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;#x1f4dd; 補助ドメインに見えるものがコアドメインになりうる例:
物販ビジネスにおいて、どの商品も常に出荷可能な状態が保たれていることが顧客満足に決定的に重要な場合、在庫管理ドメインはコアドメインになりうる。&lt;/p&gt;
&lt;h1 id=&#34;ユビキタス言語をつくる&#34;&gt;ユビキタス言語をつくる&lt;/h1&gt;
&lt;p&gt;コードとドメインエキスパートは同じモデルを共有しなければならない&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;ドメインエキスパートが「注文」と言ったならば、コードでもそれに対応し、同じように振る舞う「注文」というものを定義する &amp;#x2705;&lt;/li&gt;
&lt;li&gt;逆に、ドメインエキスパートのメンタルモデルにないもの（注文ファクトリ、注文マネージャ、注文ヘルパーなど）は設計に含めてはならない &amp;#x274c;
&lt;ul&gt;
&lt;li&gt;ドメインエキスパートはそれらの意味を理解できないので害にしかならない。もちろんそれらの技術的概念はコードベースの方には出てくるだろうが、それらを&lt;strong&gt;設計&lt;/strong&gt;に入れてはいけない&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;ユビキタス言語&lt;/strong&gt;で設計せよ&lt;/p&gt;
&lt;p&gt;ユビキタス言語とは: プロジェクトの関係者全員に共有される概念や用語のまとまり。これがビジネスドメインの共通メンタルモデルを定義する。プロジェクトのあらゆる場所で一貫して使われるべき。あらゆる場所とは、コードも含む。&lt;/p&gt;
&lt;p&gt;（確か、ドメインモデルを正確に反映するためにコード中のユビキタス言語も日本語で書いている日本のチームの事例があった。言語は Ruby だったと思う）&lt;/p&gt;
&lt;p&gt;ユビキタス言語の構築プロセス: 常に WIP。設計とともに進化する&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;not ドメインエキスパート起点の一方通行 &amp;#x274c;&lt;/li&gt;
&lt;li&gt;but プロジェクトみんなによるコラボレーション &amp;#x2705;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;#x26a0;&amp;#xfe0f; ドメインの「方言」に注意&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;ある用語が意味する概念が、ドメインによって微妙に異なる場合がある。イベントストーミング時には特にこれに気をつける必要がある
&lt;ul&gt;
&lt;li&gt;例: 配送部門が言う「注文」は請求部門が言うそれとは微妙に違う。配送部門の関心は在庫つまり数にあるが、請求部門の関心は価格と支払いにある。もし「注文」をコンテキストの明示なしにさまざまな場所で使ってしまうと誤解が生じてしまう&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;→ 全てのドメインとコンテキストをカバーするユビキタス言語を作るのは無理&lt;/p&gt;
&lt;h1 id=&#34;ドメイン駆動設計の概念をまとめる&#34;&gt;ドメイン駆動設計の概念をまとめる&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;【ドメイン】 解こうとする問題に関係する知識領域。ドメインエキスパートが専門とする&lt;/li&gt;
&lt;li&gt;【ドメインモデル】 特定の問題に関わるドメインの側面を単純化したものの集合。ドメインモデルは解決領域の一部で、ドメインは問題領域。&lt;/li&gt;
&lt;li&gt;【ユビキタス言語】 ドメインと関連し、チームメンバーとソースコードに共有される概念や語彙の集合&lt;/li&gt;
&lt;li&gt;【境界づけられたコンテキスト】 明解な境界によって他と区別される、解決領域の子要素。&lt;/li&gt;
&lt;li&gt;【コンテキストマップ】 境界づけられたコンテキスト間の関係を示した高次の概念図&lt;/li&gt;
&lt;li&gt;【ドメインイベント】 システム内で起こった事実の記録。必ず過去形で記述され、何らかのコマンド（原文では &amp;ldquo;activity&amp;rdquo; と書いてある）をトリガーすることが多い&lt;/li&gt;
&lt;li&gt;【コマンド】 何らかのプロセスへの開始要求。人やイベントによってトリガーされる。プロセスが成功するとシステムが変化し、一つ以上のドメインイベントとが記録される&lt;/li&gt;
&lt;/ul&gt;
&lt;h1 id=&#34;まとめ&#34;&gt;まとめ&lt;/h1&gt;
&lt;h2 id=&#34;イベントとプロセス&#34;&gt;イベントとプロセス&lt;/h2&gt;
&lt;p&gt;イベントストーミングによって、ドメイン内の全ての主要なドメインイベントが見えるようになった。
受注プロセスは注文書を郵便で受け取るところから始まり、見積書の作成、新規顧客の登録などのワークフローがあることもわかった。&lt;/p&gt;
&lt;p&gt;受注チームが注文を処理し終わると、そのイベントが配送部門の配送プロセスをスタートさせ、請求部門の請求プロセスをスタートさせることも学んだ&lt;/p&gt;
&lt;p&gt;他にもプロセスはたくさんあるが、本書では一つに着目する。&lt;/p&gt;
&lt;h2 id=&#34;サブドメインと境界づけられたコンテキスト&#34;&gt;サブドメインと境界づけられたコンテキスト&lt;/h2&gt;
&lt;p&gt;ここまでで、3 つのサブドメインが見つかった（受注、配送、請求）。
これらに対応する 3 つの境界づけられたコンテキストを作り、それぞれの関係を示したコンテキストマップをつくった。&lt;/p&gt;
&lt;p&gt;どれが注目すべきコアドメインだろう？
&lt;strong&gt;どこを自動化すると価値が最大化されるか&lt;/strong&gt;？ → 現時点では受注ドメインからとりかかることにしておく&lt;/p&gt;
&lt;p&gt;必要ならば、受注ドメインの仕事を紙に出力することで、他のチームが今まで通りの方法で仕事を続けることもできる。&lt;/p&gt;
&lt;h2 id=&#34;ユビキタス言語&#34;&gt;ユビキタス言語&lt;/h2&gt;
&lt;p&gt;ここまでで、「注文フォーム」「見積もり」「注文」という言葉を見つけた。
設計を深める過程でもっと見つかるだろう。
ユビキタス言語は、wiki などのライブなドキュメント（印刷した書類は固定化されてしまうが、wiki は更新が前提）でメンテナンスするといい。
チームメンバーの足並みが揃うし、新しいメンバーもキャッチアップしやすい。&lt;/p&gt;
&lt;h2 id=&#34;次にやることは&#34;&gt;次にやることは？&lt;/h2&gt;
&lt;p&gt;本章によって&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;問題の全体像と、解決策の概略が見えた &amp;#x1f4a1;&lt;/li&gt;
&lt;li&gt;しかし詳細設計やコーディングを始めるにはまだ多くの疑問がある &amp;#x1f4ad;
&lt;ul&gt;
&lt;li&gt;&amp;#x2753; 注文処理ワークフローでは何が行われているのだろう？&lt;/li&gt;
&lt;li&gt;&amp;#x2753; 入出力はどんな感じ？&lt;/li&gt;
&lt;li&gt;&amp;#x2753; このワークフローが連携する他のコンテキストはある？&lt;/li&gt;
&lt;li&gt;&amp;#x2753; 配送チームの「注文」と請求チームのそれはどう違う？&lt;/li&gt;
&lt;li&gt;などなど&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;次の章では注文確定ワークフローに着目して、これらの問いに答えていく&lt;/p&gt;</description>
    </item>
    
    <item>
      <title>第 2 章: ドメインを理解する</title>
      <link>https://rindrics.com/books/domain-model-made-functional/understanding-the-domain/</link>
      <pubDate>Sun, 17 Nov 2024 00:00:00 +0900</pubDate>
      
      <guid>https://rindrics.com/books/domain-model-made-functional/understanding-the-domain/</guid>
      <description>&lt;p&gt;&amp;ldquo;2. Understanding the Domain&amp;rdquo; from &amp;ldquo;&lt;a href=&#34;https://rindrics.com/books/domain-model-made-functional/&#34;&gt;Domain Modeling Made Functional: Tackle Software Complexity with Domain-Driven Design and F#&lt;/a&gt;&amp;rdquo; &amp;#x1f4da;&lt;/p&gt;
&lt;p&gt;一つのワークフローに着目して掘り下げていく&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;何がそれをトリガーするのか？&lt;/li&gt;
&lt;li&gt;どんなデータが必要？&lt;/li&gt;
&lt;li&gt;どんな境界づけられたコンテキストと協働する必要がある？&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;ここでは注意深く耳を傾ける能力が重要。
自分のメンタルモデルをドメインに押し付けないようにしたい。&lt;/p&gt;
&lt;h1 id=&#34;ドメインエキスパートへの聞き取り&#34;&gt;ドメインエキスパートへの聞き取り&lt;/h1&gt;
&lt;p&gt;ドメインエキスパート（文中では受注部門のメンバー）に聞き取り調査をしていく。&lt;/p&gt;
&lt;p&gt;ポイント:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;イベントストーミングで洗い出したコマンド・イベントを手がかりに、聞き取り調査をいくつかの小さい単位に分ける &amp;#x2705;
&lt;ul&gt;
&lt;li&gt;忙しいドメインエキスパートに時間をとってもらいやすい&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;インタビューの初期は高次の視点を維持し、ワークフローの入出力だけに着目する &amp;#x2705;
&lt;ul&gt;
&lt;li&gt;詳細に囚われすぎずに済む&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;顧客がシステムをどう使うかの結論を急がず、人類学者のようにとにかく耳を傾ける &amp;#x2705;
&lt;ul&gt;
&lt;li&gt;文中では「顧客が注文フォームをオンラインで入力してくれたらいいのに」というドメインエキスパートの言葉に対して、開発者は一般的なカート &amp;amp; チェックアウトパターンのシステムを想像しかけた
&lt;ul&gt;
&lt;li&gt;しかしドメインエキスパートが求めるのはそのパターンではなかった &amp;#x1f4a3;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;理想的には、設計の前にドメインエキスパートが働く様子を観察したりするとよい &amp;#x2705;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;非機能要件を理解する&#34;&gt;非機能要件を理解する&lt;/h2&gt;
&lt;p&gt;一歩下がって、ワークフローのスケールとコンテキストについて議論する。&lt;/p&gt;
&lt;p&gt;文中では、後のアーキテクチャ決定に関わる下記の要件についてドメインエキスパートに尋ねている:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;1 日あたりのリクエスト数&lt;/li&gt;
&lt;li&gt;リクエスト数の年間変動&lt;/li&gt;
&lt;li&gt;ユーザーの属性（ビギナー or プロフェッショナル）&lt;/li&gt;
&lt;li&gt;サービスに期待されているレスポンス&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;ワークフローの残りを理解する&#34;&gt;ワークフローの残りを理解する&lt;/h2&gt;
&lt;p&gt;ドメインエキスパートへの聞き取りによってドメインの知識を獲得していく。&lt;/p&gt;
&lt;p&gt;文中では「受け取ったそれぞれの注文フォームで何をするのか？」という質問から始めて、下記のドメイン知識を発見した:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;検証作業&lt;/li&gt;
&lt;li&gt;協調すべき境界づけられたコンテキスト&lt;/li&gt;
&lt;li&gt;集計作業&lt;/li&gt;
&lt;li&gt;注文確認の返信作業&lt;/li&gt;
&lt;li&gt;同じ入力を利用する別のワークフロー（見積もり作業）&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;入出力について考える&#34;&gt;入出力について考える&lt;/h2&gt;
&lt;p&gt;ワークフローの入出力について、聞き取りから学んだことを書き留める&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;入力: 明らかに注文フォーム &amp;#x2705;&lt;/li&gt;
&lt;li&gt;出力:
&lt;ul&gt;
&lt;li&gt;「処理済みの注文」という概念があることはわかった。しかしそれ自体は特に出力として使い道がない &amp;#x274c;&lt;/li&gt;
&lt;li&gt;注文確認はどうか？これは注文確定ワークフローの副作用であり、出力ではない &amp;#x274c;&lt;/li&gt;
&lt;li&gt;出力は常に生成イベントであるべき &amp;#x2705;
&lt;ul&gt;
&lt;li&gt;他の境界づけられたコンテキストのアクションをトリガーするもの&lt;/li&gt;
&lt;li&gt;ここでは&lt;code&gt;注文が確定された&lt;/code&gt;イベント&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h1 id=&#34;データベース駆動設計したくなる衝動と戦う-warning&#34;&gt;データベース駆動設計したくなる衝動と戦う &amp;#x26a0;&amp;#xfe0f;&lt;/h1&gt;
&lt;p&gt;&amp;#x1f4dd; この節は目からウロコだった・・・！
この節を読むべきなのは初学者よりむしろ、ビジネス要件から DB スキーマを設計することに慣れている人だと思う。&lt;/p&gt;
&lt;p&gt;ある程度経験を積んだプログラマなら、もう詳細設計して実装に入りたくなるだろう。
テーブル間の関連について、下記のように設計を始めるかもしれない:&lt;/p&gt;
&lt;pre class=&#34;mermaid&#34;&gt;erDiagram
    Customers ||--o{ Orders : places
    Customers {
        string name
        string custNumber
        string sector
    }
    Orders ||--|{ OrderLines : contains
    Orders {
        string customerId
        string shippnigAddressId
        string billingAddressId
        bool isQuote
    }
    OrderLines {
        string orderId
        string productId
        float quantity
    }
    Addresses ||--|{ Orders : referred
&lt;/pre&gt;

&lt;p&gt;・・・しかしこれをやってしまったらオシマイ &amp;#x1f4a3;（マジか）&lt;/p&gt;
&lt;p&gt;DDD ではドメインを正確にモデリングすることに集中するために「永続性の無視」というアプローチで設計する:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;ドメインに設計を駆動させる &amp;#x2705;&lt;/li&gt;
&lt;li&gt;DB スキーマではない &amp;#x274c;
&lt;ul&gt;
&lt;li&gt;実際、現実の紙ベースのシステムではデータベースなど存在しない&lt;/li&gt;
&lt;li&gt;&lt;code&gt;データベース&lt;/code&gt;はユビキタス言語には入らない
&lt;ul&gt;
&lt;li&gt;ユーザーはデータがどう永続化されているかには興味がない&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;なぜこれが重要？ → データベースの視点からデザインすると、&lt;strong&gt;データベースモデルに合うようにデータを歪めないといけなくなる&lt;/strong&gt;から&lt;/p&gt;
&lt;p&gt;例:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;既に先ほどの ER 図では&lt;code&gt;注文&lt;/code&gt;と&lt;code&gt;見積り&lt;/code&gt;の違いを無視してしまっている &amp;#x1f4a3;
&lt;ul&gt;
&lt;li&gt;同じ外部キー（&lt;code&gt;orderId&lt;/code&gt; は &lt;code&gt;quoteId&lt;/code&gt; としても使われる）が二つのリレーションで責務を持つ状況は、データベースの機能ではサポートできない &amp;#x1f4a3;（なるほど・・・！！）&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;とにかく、技術的な先入観なしにドメインエキスパートの言葉に耳を傾けることに集中するのが重要。
ドメイン駆動で設計したモデルを関係データベースに永続化する方法は第 12 章で見ていく。&lt;/p&gt;
&lt;h1 id=&#34;クラス駆動デザインへの衝動と戦う-warning&#34;&gt;クラス駆動デザインへの衝動と戦う &amp;#x26a0;&amp;#xfe0f;&lt;/h1&gt;
&lt;p&gt;&amp;#x1f4dd; 本節のおかげで、私は&lt;a href=&#34;https://github.com/Rindrics/success-notification-aggregator/blob/08c3a71da7cf27b0126798617cc285c1d7214b54/README.md#architecture-overview&#34;&gt;個人開発でこの過ちを犯していた&lt;/a&gt;ことを自覚した&lt;/p&gt;
&lt;p&gt;オブジェクト指向の経験があるなら、特定の DB 実装に縛られないことの重要性は理解していることだろう。
しかし、ドメインではなくオブジェクトの観点で考えることによって、モデルに偏見を持ち込んでしまう（マジか）。&lt;/p&gt;
&lt;p&gt;クラス駆動デザインも、要求をちゃんと聞いていないという点でデータベース駆動デザインと同じくらい危険 &amp;#x1f4a3;&lt;/p&gt;
&lt;p&gt;こんなふうに設計するかもしれないが&lt;/p&gt;
&lt;pre class=&#34;mermaid&#34;&gt;classDiagram
    OrderBase &lt;|-- Order
    OrderBase &lt;|-- Quote

    OrderBase &#34;n&#34; -- &#34;1&#34; Customer
    Order &#34;n&#34; -- &#34;1&#34; Address : shipping address
    Order &#34;n&#34; -- &#34;1&#34; Address : billing address

    OrderBase &#34;1&#34; *-- &#34;n&#34; OrderLine
    OrderLine &#34;1&#34; -- &#34;n&#34; Product
&lt;/pre&gt;

&lt;p&gt;&lt;code&gt;注文&lt;/code&gt;と&lt;code&gt;見積り&lt;/code&gt;の違いは表現できているできているものの、&lt;strong&gt;現実世界に存在しない&lt;code&gt;OrderBase&lt;/code&gt;という人工的なクラスを持ち込み、ドメインを歪めてしまった&lt;/strong&gt; &amp;#x1f4a3;
（まぁ「ドメインモデルと詳細設計は区別しましょう」で片付く話ではあるか）&lt;/p&gt;
&lt;p&gt;心をオープンに保て。
技術的な思考をドメインに持ち込むな。&lt;/p&gt;
&lt;h1 id=&#34;ドメインを文書化する&#34;&gt;ドメインを文書化する&lt;/h1&gt;
&lt;p&gt;技術的な実装による偏見に陥らず、要求をどのように記述すべきだろう？
UML のような可視化ダイアグラムを使うこともできるが、労力がかかる割にドメインの機微をとらえられるほどの表現力がない。&lt;/p&gt;
&lt;p&gt;正確なドメインモデルをコードで表現する方法は後ほど学ぶとして、今は、ドメインモデルを捉えられる簡単なテキストベース言語を使う:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;ワークフローの表現法: 入出力を記述し、ビジネスロジックを記述するシンプルな疑似コードを使う&lt;/li&gt;
&lt;li&gt;データ構造の表現法
&lt;ul&gt;
&lt;li&gt;二つの部分が共に必要なときには &lt;code&gt;かつ&lt;/code&gt;を使う&lt;/li&gt;
&lt;li&gt;どちらかだけでいい時には&lt;code&gt;または&lt;/code&gt;を使う&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;このミニ言語を使って、注文確定ワークフローを記述すると下記のようになる:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;境界づけられたコンテキスト: 受注
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;ワークフロー: 注文確定
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  トリガー: &amp;#34;注文フォームを受け取った&amp;#34; イベント（&amp;#34;見積もり&amp;#34; にチェックが入っていたとき）
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;初期入力:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  注文フォーム
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;その他の入力:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  商品カタログ
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;出力イベント:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &amp;#34;注文が確定した&amp;#34; イベント
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;副作用:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  注文確定に伴って注文確認が顧客に送信された
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;データ構造は下記のように表現される:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;境界づけられたコンテキスト: 受注
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;注文 データ =
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  顧客情報
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  かつ 配送先住所
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  かつ 請求先住所
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  かつ 注文明細 のリスト
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  かつ 請求金額
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;注文明細 データ =
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  商品
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  かつ 数量
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  かつ 価格
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;顧客情報 データ = ???   // まだ不明
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;請求先住所 データ = ??? // まだ不明
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;これはドメインを少し構造化された方法で捉えただけで、クラス構造もデータベーステーブルもない:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;ノンプログラマにとってもこわくない &amp;#x2705;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;ドメインエキスパートにも見てもらい、一緒に取り組んでもらえる&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&amp;#x2753; コードもこれくらいシンプルにできるのか？
&lt;ul&gt;
&lt;li&gt;→ 第 5 章で学ぶ&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h1 id=&#34;受注ワークフローを掘り下げる&#34;&gt;受注ワークフローを掘り下げる&lt;/h1&gt;
&lt;p&gt;入出力を記述できたので、聞き取り調査を再開してワークフローを掘り下げていく。&lt;/p&gt;
&lt;p&gt;下記のドメイン知識が明らかになった:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;注文&lt;/code&gt;か&lt;code&gt;見積り依頼&lt;/code&gt;かを判別
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;見積り依頼&lt;/code&gt;はあとで処理する
&lt;ul&gt;
&lt;li&gt;売上げを生む&lt;code&gt;注文&lt;/code&gt;のほうが重要だから&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;検証作業におけるサードパーティサービスの利用&lt;/li&gt;
&lt;li&gt;用途の異なる書類の山
&lt;ul&gt;
&lt;li&gt;実装ではキューになるだろうが、聞き取り調査では技術的詳細からは距離を保っておく&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;商品コード&lt;/code&gt; のチェック
&lt;ul&gt;
&lt;li&gt;技術的に言うとシンタックスチェック&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;商品コード&lt;/code&gt; の照合
&lt;ul&gt;
&lt;li&gt;技術的に言うとデータベースルックアップ&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;依存管理
&lt;ul&gt;
&lt;li&gt;「もし、商品部門のだれかがあなたの問い合わせに即座に答えられるとしても、商品カタログをやはり手元に持っておきたい？」「もちろん。電話が使えなかったり、彼らが忙しかったりしても、私の仕事が止まらずに済むから。」&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;数量単位の仕様&lt;/li&gt;
&lt;/ul&gt;
&lt;h1 id=&#34;複雑さをドメインモデルに反映させる&#34;&gt;複雑さをドメインモデルに反映させる&lt;/h1&gt;
&lt;p&gt;ワークフローを掘り下げたことによって、ドメインモデルがかなり複雑になった&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;これは良いこと &amp;#x2705;
&lt;ul&gt;
&lt;li&gt;実装する段になって複雑性が判明するのは大きな時間の無駄になる&lt;/li&gt;
&lt;li&gt;初期段階で、ドメインの理解に時間をかけるべき
&lt;ul&gt;
&lt;li&gt;「数週間のコーディングによって設計にかかる時間を数時間節約できる」という皮肉がある&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;制約を表現する&#34;&gt;制約を表現する&lt;/h2&gt;
&lt;p&gt;聞き取り調査の結果、商品コードと数量が単なる文字列あるいは整数でないことがわかった。
これをドキュメントに反映する。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;ウィジェットコード&lt;/code&gt;、&lt;code&gt;ギズモコード&lt;/code&gt;、&lt;code&gt;商品コード&lt;/code&gt;の制約は下記のように表現される:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;コンテキスト: 受注
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;ウィジェットコード データ = &amp;#34;W&amp;#34; から始まり 4 つの数字からなる文字列
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;ギズモコード データ = &amp;#34;G&amp;#34; から始まり 3 つの数字からなる文字列
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;商品コード データ = ウィジェットコード または ギズモコード
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ul&gt;
&lt;li&gt;&amp;#x2753; この制約は厳しすぎるだろうか？
&lt;ul&gt;
&lt;li&gt;&amp;#x2753; 新しい商品コードを追加したくなったらどうする？
&lt;ul&gt;
&lt;li&gt;正しい答えはコンテキストによるが、設計をドメインエキスパートの視点からすることが重要&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;ここでは、この制約はドメインの設計に反映させるべきだろう。
なぜなら、商品コードのチェックは注文確認プロセスの重要な工程であることを学んだので、最終的には何らかの方法でドキュメント化が必要。
&lt;strong&gt;別のドキュメントに分散するより設計として書いた方が良い&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;ちなみに、設計が厳しいからといって必ずしも実装まで厳しくする必要はない。
不正な入力があったときの実装例:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;注文全体を即座に拒否（これは厳しすぎるだろう）&lt;/li&gt;
&lt;li&gt;ユーザーに「要確認」の通知を出す（よい落とし所かもしれない）&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;数量に関する制約もドキュメントにしておく:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;単位数量 データ = 1 から 1000 までの整数
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;キログラム数量 データ = 0.05 から 100.00 までの小数
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;注文のライフサイクルを表現する&#34;&gt;注文のライフサイクルを表現する&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;注文&lt;/code&gt;（型）に着目し、学んだことを記述する。&lt;/p&gt;
&lt;p&gt;初期のバージョンでは下記のように表現したが、これはドメインを表現し切れていないことがわかった。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;境界づけられたコンテキスト: 受注
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;注文 データ =
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  顧客情報
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  かつ 配送先住所
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  かつ 請求先住所
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  かつ 注文明細 のリスト
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  かつ 請求金額
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;聞き取り調査の結果、下記を学んだ:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;注文&lt;/code&gt;にはライフサイクルがある
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;注文&lt;/code&gt;は初めは&lt;code&gt;請求金額&lt;/code&gt;を持たないが最後には必ず持つようになる&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;未検証の注文や未集計の注文は他と区別できる
&lt;ul&gt;
&lt;li&gt;紙ベースの現在の業務では進捗をフォームに記録しているため&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;これらをモデルにも反映する必要がある。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;未検証の注文 データ =
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  未検証の顧客情報
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  かつ 未検証の配送先住所
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  かつ 未検証の請求先住所
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  かつ 未検証の注文明細 のリスト
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;未検証の注文明細 データ =
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  未検証の商品コード
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  かつ 未検証の注文数量
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;各情報のステータスがわかるようになった（&lt;strong&gt;DB のフラグで済ませない！！！！&lt;/strong&gt;）。&lt;/p&gt;
&lt;p&gt;注文が確認されると下記のようになる:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;検証済の注文 データ =
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  検証済の顧客情報
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  かつ 検証済の配送先住所
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  かつ 検証済の請求先住所
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  かつ 検証済の注文明細 のリスト
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;検証済の注文明細 データ =
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  検証済の商品コード
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  かつ 検証済の注文数量
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;次は注文に金額を入力する:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;集計済の注文 データ =
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  検証済の顧客情報
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  かつ 検証済の配送先住所
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  かつ 検証済の請求先住所
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  かつ 集計済の注文明細 のリスト  // 知識が反映されている
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  かつ 請求金額                   // 知識が反映されている
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;集計済の注文明細 データ =
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  検証済の商品コード
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  かつ 明細料金                   // 知識が反映されている
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;ドメインモデルがビジネスロジックを反映するようになった &amp;#x1f389;
初めに考えていたよりも複雑になったが、逆に言えばもしモデルがこのように複雑でなかったとしたら、それは要件を正しく捉えられていなかったということ。&lt;/p&gt;
&lt;h2 id=&#34;ワークフロー中のステップを肉付けする&#34;&gt;ワークフロー中のステップを肉付けする&lt;/h2&gt;
&lt;p&gt;ワークフローが小さいステップに分割されることがわかった:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;確認作業&lt;/li&gt;
&lt;li&gt;集計作業&lt;/li&gt;
&lt;li&gt;などなど&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;これらのステップにも入力出力アプローチを適用しよう&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;ワークフロー: 注文確定
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  入力: 注文フォーム
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  出力:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &amp;#34;注文が確定した&amp;#34; イベント（他のチームに送るための山に置く）
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    または &amp;#34;不正な注文&amp;#34; （それ用の山に置く）
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  // ステップ 1
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  do 注文を検証
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  if 注文 が 不正 then:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    add 不正な注文 to 山
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    stop
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  // ステップ 2
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  do 注文集計
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  // ステップ 3
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  do 顧客に確認を送る
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  // ステップ 4
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  return 注文が確定された イベント（if エラーがないならば）
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;それぞれのサブステップもドキュメント化する&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;ワークフロー: 注文を検証
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  入力: 未検証の注文
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  出力: 検証済の注文 また 検証エラー
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  依存: 商品コードの存在チェック, 住所の存在チェック
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  顧客の名前を検証する
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  配送先住所と請求先住所が存在することを確認する
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  for each 注文明細:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    商品コードをチェック
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    商品コードが商品カタログに存在することをチェック
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  if 全て OK ならば, then:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    return 検証済の注文
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  else:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    return 検証エラー
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;ほぼ丸写しになってしまうのであとのステップは割愛。&lt;/p&gt;
&lt;h1 id=&#34;まとめ&#34;&gt;まとめ&lt;/h1&gt;
&lt;p&gt;次のモデリングフェーズでたくさんやることがあるので、要件の収集はここまで。&lt;/p&gt;
&lt;p&gt;ここで学んだことをまとめる:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;設計時には実装の詳細に立ち入らないことが重要&lt;/li&gt;
&lt;li&gt;代わりに、先入観やコーディング手法にとらわれずにドメインを捉えることに集中しよう&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;本文中で例として使われている受注システムのようなはかなり単純化されているが、それでもドメインエキスパートの言葉に耳を傾けると、多くの複雑性が明らかになることがわかる。
例えば一口に注文といっても、ライフサイクルを通して、データや振る舞いの異なるのバリエーションがあった。&lt;/p&gt;
&lt;h2 id=&#34;次にやることは&#34;&gt;次にやることは？&lt;/h2&gt;
&lt;p&gt;受注ワークフローを F# の型システムを使ってどのようにモデリングするかをみていきたい。
次章ではその前に、一歩下がって全体像を見て、システム全体をどうソフトウェアアーキテクチャに翻訳するかを議論する。&lt;/p&gt;</description>
    </item>
    
    <item>
      <title>第 3 章: 関数型アーキテクチャ</title>
      <link>https://rindrics.com/books/domain-model-made-functional/functional-architecture/</link>
      <pubDate>Thu, 21 Nov 2024 00:00:00 +0900</pubDate>
      
      <guid>https://rindrics.com/books/domain-model-made-functional/functional-architecture/</guid>
      <description>&lt;p&gt;&amp;ldquo;3. A Functional Architecture&amp;rdquo; from &amp;ldquo;&lt;a href=&#34;https://rindrics.com/books/domain-model-made-functional/&#34;&gt;Domain Modeling Made Functional: Tackle Software Complexity with Domain-Driven Design and F#&lt;/a&gt;&amp;rdquo; &amp;#x1f4da;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;次の課題&lt;/strong&gt;: これまで得てきたドメインに対する理解を、どのように関数型プログラミングに基づいたソフトウェアアーキテクチャに翻訳するか&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&amp;#x26a0;&amp;#xfe0f; 注意点: 現時点ではまだ具体的なアーキテクチャについて考えすぎないようにすること&lt;/li&gt;
&lt;li&gt;時間を使うならば: 引き続きイベントストーミングや聞き取り調査などによってドメインと要件をより深く理解する&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;本章で学ぶ内容:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;関数指向ドメインモデルのための典型的なソフトウェアアーキテクチャ&lt;/li&gt;
&lt;li&gt;DDD の概念（境界づけられたコンテキスト、ドメインイベントなど）がどのようにソフトウェアに翻訳されるのか&lt;/li&gt;
&lt;li&gt;ラフな実装方針&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;本書が使うアーキテクチャ概念:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;システムンテキスト&lt;/strong&gt;: 業務システム全体
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;コンテナ&lt;/strong&gt;&lt;sup id=&#34;fnref:1&#34;&gt;&lt;a href=&#34;#fn:1&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;1&lt;/a&gt;&lt;/sup&gt;: システムコンテキストを構成するデプロイ可能単位
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;コンポーネント&lt;/strong&gt;: 各コンテナを構成するコードの大きい構成単位
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;クラス&lt;/strong&gt;（あるいは&lt;strong&gt;モジュール&lt;/strong&gt;）: コンポーネントの構成要素&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h1 id=&#34;自律的なソフトウェアコンポーネントとしての境界づけられたコンテキスト&#34;&gt;自律的なソフトウェアコンポーネントとしての境界づけられたコンテキスト&lt;/h1&gt;
&lt;p&gt;DDD の概念である &amp;ldquo;境界づけられたコンテキスト&amp;rdquo; とソフトウェアアーキテクチャとの関連を示す。&lt;/p&gt;
&lt;p&gt;コンテキストとは: うまく定義された境界に基づく自律的なサブシステム&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;このように定義を定めたとしても、アーキテクチャとしての構成方法はいくつものパターンをとりうる
&lt;ul&gt;
&lt;li&gt;コンテナとドメインのマッピング例:
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;コンテナ = 業務&lt;/strong&gt;: システム=コンテナでもある。このとき、境界づけられたコンテキストはインターフェイスによって定義された個別のモジュール（.NET でいう &amp;ldquo;アセンブリ&amp;rdquo;）&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;コンテナ = 境界づけられたコンテキスト&lt;/strong&gt;: 伝統的なサービス指向アーキテクチャ&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;コンテナ = ワークフロー&lt;/strong&gt;: こうなるとマイクロサービスアーキテクチャ&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;ただし、ドメインについて理解しきれていないフェーズでは、まだ特定のアーキテクチャパターンを選ぶ必要はない。
最重要なのは&lt;strong&gt;境界づけられたコンテキストを疎結合に設計し、自律可能に保つこと&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;境界を正しく定めることの重要性は述べたが、実際にはプロジェクト初期から正しく定めるのは難しい&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;理由: 知識を得るにつれて境界は変わりうるから&lt;/li&gt;
&lt;li&gt;よって、開発初期にはまずはモノリスパターンで始めることをおすすめする
&lt;ul&gt;
&lt;li&gt;マイクロサービスパターンを使う明確なメリットが見えているならばこの限りではない&lt;/li&gt;
&lt;li&gt;本当に疎結合なマイクロサービスアーキテクチャを実現するのは難しいからでもある
&lt;ul&gt;
&lt;li&gt;あるマイクロサービスを止めたときに別の場所が壊れるならば、それはただの &amp;ldquo;分散モノリス&amp;rdquo;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h1 id=&#34;境界づけられたコンテキスト間におけるコミュニケーション&#34;&gt;境界づけられたコンテキスト間におけるコミュニケーション&lt;/h1&gt;
&lt;p&gt;どうコミュニケーションする？→ ドメインイベントを通じて&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;注文処理を終えた受注部門が配送部門に対して配送を依頼する場合の例:
&lt;ul&gt;
&lt;li&gt;受注コンテキストは&lt;code&gt;注文が確定された&lt;/code&gt;イベントを発行する&lt;/li&gt;
&lt;li&gt;&lt;code&gt;注文が確定された&lt;/code&gt;イベントはキューに入るか、直接発行される&lt;/li&gt;
&lt;li&gt;配送コンテキストは&lt;code&gt;注文が確定された&lt;/code&gt;イベントをリッスンする&lt;/li&gt;
&lt;li&gt;配送コンテキストがイベントを受け取ると&lt;code&gt;注文を配送せよ&lt;/code&gt;コマンドが作られる&lt;/li&gt;
&lt;li&gt;&lt;code&gt;注文を配送せよ&lt;/code&gt;コマンドは注文配送ワークフローをトリガーする&lt;/li&gt;
&lt;li&gt;注文配送ワークフローが完了すると&lt;code&gt;注文が配送された&lt;/code&gt;イベントが発行される&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;このように設計すれば、受注・配送の両コンテキストは&lt;strong&gt;互いについて知る必要なく&lt;/strong&gt;、イベントを通じてコミュニケーション可能。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;イベントを実際に発行する方法
&lt;ul&gt;
&lt;li&gt;選定したアーキテクチャによる
&lt;ul&gt;
&lt;li&gt;キューや関数呼び出しなどが考えうるが、今はまだ決めなくていい&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;各コンテキストが自律できるよう、設計を疎結合に保つことが重要&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;イベントをコマンドに変換するハンドラ
&lt;ul&gt;
&lt;li&gt;これも複数の実装パターンが考えうる
&lt;ul&gt;
&lt;li&gt;下流コンテキストの一部としたり、独立したルータやプロセスマネージャにしてもいい&lt;/li&gt;
&lt;li&gt;選定するアーキテクチャや、イベントとコマンドの対応づけをどこでしたいかによって決めればいい&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;#x1f4dd; イベントを利用したコミュニケーションの特徴は&lt;strong&gt;互いを意識せずに済む&lt;/strong&gt;ところ。
確かにイベント経由通信を使わなくとも、ある程度の疎結合性は「インターフェイス契約+非同期コミュニケーション」で獲得できる。
それでもやはり、通信時には相手にデータを投げるなり、相手のメソッドを呼び出すなり、コード中に必ず「相手」が出てきてしまう。&lt;/p&gt;
&lt;h2 id=&#34;境界づけられたコンテキスト間におけるデータのやりとり&#34;&gt;境界づけられたコンテキスト間におけるデータのやりとり&lt;/h2&gt;
&lt;p&gt;コミュニケーションに使われるイベントは、下流コンテキストが処理に必要としている全てのデータを含む。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;例:
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;注文が確定された&lt;/code&gt;イベントは注文データ全体を含む
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;注文を配送せよ&lt;/code&gt;コマンドを作るのに必要な情報全てを配送コンテキストに渡すため&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;データがイベントに含めるのには大きすぎるなら、共有データストレージを利用してもよい。&lt;/p&gt;
&lt;p&gt;このデータオブジェクト──Data Transfer Objects（DTO）と呼ぶ──は、境界づけられたコンテキスト内で定義されたオブジェクトと同じに見えるが、厳密には違う。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;シリアライズしてコンテキスト間で共有できるよう設計されている
&lt;ul&gt;
&lt;li&gt;上流コンテキストの境界付近でドメインオブジェクトは DTO に変換される
&lt;ul&gt;
&lt;li&gt;JSON や XML などにシリアライズされる&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;下流コンテキストでは、このプロセスが逆方向に実行されてドメインオブジェクトが作られる&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;（例） &lt;code&gt;注文が確定された&lt;/code&gt;イベントの DTO の構造:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;イベント DTO
&lt;ul&gt;
&lt;li&gt;注文 DTO
&lt;ul&gt;
&lt;li&gt;注文明細 DTO のリスト&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;信頼性境界と検証&#34;&gt;信頼性境界と検証&lt;/h2&gt;
&lt;p&gt;境界づけられたコンテキストの輪郭は信頼性境界としてはたらく&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;境界の内側: 全て信頼可能で、有効であるとみなされる&lt;/li&gt;
&lt;li&gt;境界の外側: いかなるものも未検証で、不正になり得る&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;このような境界が機能するように、ワークフローの入口と出口にゲートを設置する必要がある。&lt;/p&gt;
&lt;h3 id=&#34;入力ゲート&#34;&gt;入力ゲート&lt;/h3&gt;
&lt;p&gt;渡された DTO がドメインの制約（例: ある属性が 非null かつ 50 文字以内であること）を満たしていることを保証するために、入力を必ず検証する。
検証失敗時には、残りのワークフローはスキップされて例外が送出される。&lt;/p&gt;
&lt;h3 id=&#34;出力ゲート&#34;&gt;出力ゲート&lt;/h3&gt;
&lt;p&gt;非公開情報が境界の外に出ないようにする役割を担う。&lt;/p&gt;
&lt;p&gt;目的:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;コンテキスト間の意図しないカップリング（なるほど！）&lt;/li&gt;
&lt;li&gt;セキュリティ向上&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;これらの目的を果たすため、出力ゲートはドメインオブジェクトを DTO に変換する際に、情報を意図的に捨てる。&lt;/p&gt;
&lt;h1 id=&#34;境界づけられたコンテキスト間の契約&#34;&gt;境界づけられたコンテキスト間の契約&lt;/h1&gt;
&lt;p&gt;境界づけられたコンテキスト間のカップリングをできるだけ減らしたい&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;しかし共有コミュニケーションは常に何らかの結合をもたらす
&lt;ul&gt;
&lt;li&gt;イベントと、関連する DTO は境界づけられたコンテキスト間に一種の契約をもたらす&lt;/li&gt;
&lt;li&gt;コミュニケーションを可能にするにはコミュニケーションに使う共通形式について両コンテキストが同意している必要がある&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;誰が契約の締結を決定する？→コンテキスト間の関係性による&lt;/p&gt;
&lt;p&gt;関係性の例:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;共有カーネル: 二つのコンテキストが共通のドメイン設計をいくつかもつ
&lt;ul&gt;
&lt;li&gt;よって関係するチームは協働する必要がある
&lt;ul&gt;
&lt;li&gt;例: 受注コンテキストは配送先住所を受け取り、検証する。配送コンテキストは配送にその住所を使う&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;イベントや DTO の定義を変えるときには関係者のレビューが必要&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;顧客/供給者（または顧客駆動契約）: 下流のコンテキストが上流コンテキストに供給してほしいものを定義する
&lt;ul&gt;
&lt;li&gt;上流が契約を守っている限り、両ドメインは独立に進化できる
&lt;ul&gt;
&lt;li&gt;例: 請求コンテキストが契約を定義し、受注コンテキストがそれを供給する&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;順応者: 顧客駆動とは逆に、上流の定義を下流が受け入れ、自らのドメインモデルをそれに合うように適応させる
&lt;ul&gt;
&lt;li&gt;例: 受注コンテキストは製品カタログが決めた契約に従っている&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;腐敗防止層&#34;&gt;腐敗防止層&lt;/h2&gt;
&lt;p&gt;外部システムと通信したいとき、利用可能なインターフェイスが自コンテキストのドメインモデルと全く合わないことがある&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;腐敗防止層（入力ゲートがこの役割を果たすことが多い）を使って、やりとりやデータをコンテキストに合うものに変換する必要がある
&lt;ul&gt;
&lt;li&gt;理由: ドメインモデルが外部のシステムに合わせようとして「壊れて」しまうのを防ぐため&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;コンテキストマップと関係性&#34;&gt;コンテキストマップと関係性&lt;/h2&gt;
&lt;p&gt;設計が進み、コンテキスト間の関係が決まったとする:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;受注・配送コンテキスト間: 共有カーネル
&lt;ul&gt;
&lt;li&gt;なのでコミュニケーション契約を一緒に決める&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;受注・請求コンテキスト間: 顧客駆動契約
&lt;ul&gt;
&lt;li&gt;請求コンテキストが契約を決め、受注コンテキストがそれを提供する&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;受注・製品カタログ間: 適合者パターン
&lt;ul&gt;
&lt;li&gt;受注コンテキストは製品カタログが定めたモデルを使う&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;外部の住所確認サービス: 本システムのドメインモデルと全然合わない
&lt;ul&gt;
&lt;li&gt;腐敗防止層を介して利用する&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;コンテキストマップ&lt;strong&gt;コンテキストをもつチームがどのように連携すべきか&lt;/strong&gt;も示す&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;技術的な課題であると同時に組織的な課題でもある&lt;/li&gt;
&lt;/ul&gt;
&lt;h1 id=&#34;境界づけられたコンテキスト内におけるワークフロー&#34;&gt;境界づけられたコンテキスト内におけるワークフロー&lt;/h1&gt;
&lt;p&gt;ビジネスワークフローの表現:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;DDD の概念に基づく表現: コマンドにより起動され、いくつかのドメインイベントを発するミニプロセス&lt;/li&gt;
&lt;li&gt;関数型アーキテクチャにおける表現: コマンドオブジェクトを入力とし、イベントオブジェクトのリストを出力とする一つの関数&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;本文中の模式図を使った説明:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;ワークフローは常にただ一つの境界づけられたコンテキストに含まれる&lt;/li&gt;
&lt;li&gt;公開ワークフロー（コンテキスト外からトリガーされる）は境界の外側に少しはみ出している&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;ワークフローの入力と出力&#34;&gt;ワークフローの入力と出力&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;入力: 常にコマンドとひもづくデータ&lt;/li&gt;
&lt;li&gt;出力: 他のコンテキストと通信するためのイベントのリスト&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;受注ワークフローにおける例:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;入力:
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;注文を確定せよ&lt;/code&gt;コマンド&lt;/li&gt;
&lt;li&gt;紐づくデータ&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;出力: &lt;code&gt;注文が確定された&lt;/code&gt;イベントなどのイベントのリスト&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;請求コンテキストにおける例: （顧客/供給者パターンであることに注意）&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;ただの&lt;code&gt;注文が確定された&lt;/code&gt;イベントを出すだけでなく、イベントに含めるデータはは&lt;strong&gt;請求に必要なデータのみ&lt;/strong&gt;に限る必要がある
&lt;ul&gt;
&lt;li&gt;例: 請求先住所と合計金額だけ。配送先住所と商品リストは入れない&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;つまり、我々は新しいイベント（言うなれば&lt;code&gt;請求可能な注文が確定した&lt;/code&gt;イベント）を発行する必要があることを意味する。&lt;/p&gt;
&lt;p&gt;また注文確認が送信されたイベントも送りたい
なので前に作ったダイアグラムはもう古いので新しくしたい&lt;/p&gt;
&lt;p&gt;ワークフローの模式図の中で重要なのは、関数はイベントを&lt;strong&gt;発行して&lt;/strong&gt;はおらず、単に返しているだけであるところ&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;理由: どのように発行するかは別の関心事なため&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;境界づけられたコンテキスト内のドメインイベントを避ける&#34;&gt;境界づけられたコンテキスト内のドメインイベントを避ける&lt;/h2&gt;
&lt;p&gt;オブジェクト指向設計でよく見られるパターン: 境界づけられたコンテキスト内で、ドメインイベントを受けたリスナーが別のドメインイベントを発行 &amp;#x26a0;&amp;#xfe0f;&lt;/p&gt;
&lt;p&gt;関数型アプローチではやらない&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;理由: 隠れた依存を生み出してしまうから
&lt;ul&gt;
&lt;li&gt;リスナーが必要な場合にはワークフローの終端に置く
&lt;ul&gt;
&lt;li&gt;効果:
&lt;ul&gt;
&lt;li&gt;ミュータブルなグローバルイベントマネージャがいないことが明示される &amp;#x2705;&lt;/li&gt;
&lt;li&gt;理解・メンテナンスしやすい &amp;#x2705;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h1 id=&#34;境界づけられたコンテキスト内のコード構造&#34;&gt;境界づけられたコンテキスト内のコード構造&lt;/h1&gt;
&lt;p&gt;伝統的な「レイヤーアプローチ」では、コードは下記のような層に分けられる:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;コアドメインまたはビジネスロジック&lt;/li&gt;
&lt;li&gt;データベース&lt;/li&gt;
&lt;li&gt;サービス&lt;/li&gt;
&lt;li&gt;API またはユーザーインターフェイス&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;ワークフローは表面のインターフェイスから始まって最深部のデータベースに到達し、また表面に戻る&lt;/p&gt;
&lt;p&gt;この手法の問題点 &amp;#x26a0;&amp;#xfe0f;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;「同時に変更されるコードは同じ場所に属するべき」という設計原則を破っている&lt;/li&gt;
&lt;li&gt;層が「水平に」組み合わさっているので、ワークフローの動きを変えようとする場合には全ての層を変更する必要がある&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;よりよい方法 &amp;#x2705;: 「垂直な」構成に移行&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;それぞれのワークフローは各自の仕事を終えるために必要なコードを全て含む&lt;/li&gt;
&lt;li&gt;ワークフローに対する要求が変わったら特定の垂直スライスだけを変えればいい&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;これは理想論ではなく実際に可能&lt;/p&gt;
&lt;h2 id=&#34;オニオンアーキテクチャ&#34;&gt;オニオンアーキテクチャ&lt;/h2&gt;
&lt;p&gt;オニオンアーキテクチャ:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;ドメインを真ん中に置く&lt;/li&gt;
&lt;li&gt;その周りにその他の側面を配置する
&lt;ul&gt;
&lt;li&gt;配置ルール: それぞれの層は外側にでなく、内側にだけ依存する&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;この構造に対して関数型の依存注入を使う方法は第 9 章で述べる&lt;/p&gt;
&lt;h2 id=&#34;io-を外側に保つ&#34;&gt;I/O を外側に保つ&lt;/h2&gt;
&lt;p&gt;関数型プログラミングの主な狙い: 予測可能で、内部を見ることなく簡単に説明できる関数を使って仕事をすること&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;そのために、関数を下記のように設計する:
&lt;ul&gt;
&lt;li&gt;可能な限りイミュータブルなデータを使うことで依存を明示する&lt;/li&gt;
&lt;li&gt;ランダム性・関数外の変数の更新などの副作用や、いかなる I/O も持たないようにする&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;DB やファイルシステムを使って読み書きするシステムは「不純」とみなされる → そのような関数はコアドメインから取り除く&lt;/p&gt;
&lt;p&gt;&amp;#x2753; データの読み書きはどうやったらいい？ → I/O をオニオンアーキテクチャの最外殻に保つ&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;データベースにアクセスするのは、ワークフローの内部ではなく最初と最後のみ
&lt;ul&gt;
&lt;li&gt;ドメインモデルに DB が登場しないのなら、ワークフローでも内部から DB にアクセスできてはいけない&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;利点:
&lt;ul&gt;
&lt;li&gt;関心が異なるもの同士を分離できる
&lt;ul&gt;
&lt;li&gt;コアドメインはビジネスロジックのみに集中&lt;/li&gt;
&lt;li&gt;永続化や他の I/O はインフラが管理する&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h1 id=&#34;まとめ&#34;&gt;まとめ&lt;/h1&gt;
&lt;p&gt;DDD に関する概念と定義をいくつか紹介した&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;ドメインオブジェクト: BC 内側でのみ使われるオブジェクト&lt;/li&gt;
&lt;li&gt;DTO: シリアライズされて bc 間で共有されるためのオブジェクト&lt;/li&gt;
&lt;li&gt;共有カーネル、顧客/サプライヤ、適合者: 境界づけられたコンテキスト間の関係パターン&lt;/li&gt;
&lt;li&gt;腐敗防止層: あるドメインの概念を他のドメインのものに翻訳するコンポーネント。カップリングを減らし、各ドメインが独自に進化できるようにする&lt;/li&gt;
&lt;li&gt;永続化の無視: ドメインモデルはドメイン自体の概念のみ基づくべきであり、永続化メカニズムへの意識は含むべきでない&lt;/li&gt;
&lt;/ul&gt;
&lt;h1 id=&#34;次に学ぶこと&#34;&gt;次に学ぶこと&lt;/h1&gt;
&lt;p&gt;ドメインを理解し、解決策を設計するアプローチを理解したことで、それぞれのワークフローの設計と実装に取り組めるようになった。&lt;/p&gt;
&lt;p&gt;続く数章でやること:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;F# の型システムを使ってワークフローとデータを定義する&lt;/li&gt;
&lt;li&gt;実際にコンパイル可能ながら、ドメインエキスパートや非開発者にも理解可能なコードを書く&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;そのために次の章で学ぶこと:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;関数プログラマにとって型とは何を意味するのか&lt;/li&gt;
&lt;li&gt;クラスとどう違うのか&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&#34;footnotes&#34; role=&#34;doc-endnotes&#34;&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id=&#34;fn:1&#34;&gt;
&lt;p&gt;Docker などの仮想環境技術の文脈における &amp;ldquo;コンテナ&amp;rdquo; ではないので注意&amp;#160;&lt;a href=&#34;#fnref:1&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;</description>
    </item>
    
    <item>
      <title>義元左文字</title>
      <link>https://rindrics.com/projects/yoshimoto-samonji/</link>
      <pubDate>Thu, 04 Dec 2025 00:00:00 +0900</pubDate>
      
      <guid>https://rindrics.com/projects/yoshimoto-samonji/</guid>
      <description>&lt;h2 id=&#34;概要&#34;&gt;概要&lt;/h2&gt;
&lt;p&gt;frasyrを利用したStrangler Figアプローチで、資源評価事業全体のwebアプリケーション化を試みるプロジェクト。&lt;/p&gt;
&lt;h2 id=&#34;開発経緯&#34;&gt;開発経緯&lt;/h2&gt;
&lt;p&gt;本プロジェクトの前身となった&lt;a href=&#34;../projects/mikazuki-munechika/&#34;&gt;三日月宗近&lt;/a&gt;では、パラメータ最適化など、科学計算部のパフォーマンスが問題となった。
そこで、frasyrをモノリスアプリケーションとして動かしたうえで、その周囲に関心領域の分かれたマイクロサービスを追加していくアプローチを検証する。&lt;/p&gt;
&lt;h2 id=&#34;現在の状態&#34;&gt;現在の状態&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;進行中&lt;/code&gt;&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>村正</title>
      <link>https://rindrics.com/projects/muramasa/</link>
      <pubDate>Thu, 01 Jan 2026 00:00:00 +0900</pubDate>
      
      <guid>https://rindrics.com/projects/muramasa/</guid>
      <description>&lt;h2 id=&#34;概要&#34;&gt;概要&lt;/h2&gt;
&lt;p&gt;Excel/CSVのシートから、空行で区切られた複数の表を検出し、それぞれをタイトル行、ヘッダー行、データ行に分けて読み取るJavaSrcipt用ツール。&lt;/p&gt;
&lt;p&gt;印刷物の表のようなレイアウトでデータが配置されたシートでは、1枚に複数の表が並び、表の上にタイトルが書かれていることが多い（いわゆる&lt;a href=&#34;https://cir.nii.ac.jp/crid/1050855522055934976&#34;&gt;ネ申エクセル&lt;/a&gt;）。
村正は次のルールで、こうしたシートを機械が扱える矩形のデータとして読み込む:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;空行で区切られた行のまとまりを、表の候補（ブロック）とみなす&lt;/li&gt;
&lt;li&gt;ブロックの先頭行が、ブロック内の最大列数より少なければタイトル行とみなす&lt;/li&gt;
&lt;li&gt;タイトルの次の行をヘッダー行とみなす。正規表現でヘッダー行を指定することも、ヘッダーなしと指定することもできる&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;開発経緯&#34;&gt;開発経緯&lt;/h2&gt;
&lt;p&gt;資源評価事業をドメインモデリングに基づいてアプリケーション化する&lt;a href=&#34;./mikazuki-munechika&#34;&gt;三日月宗近&lt;/a&gt;プロジェクトで、査読者用のデータアップロード機能が必要だった。
このような外部との連絡部分では、自アプリのモデルを外部のデータ型式から守るためのいわゆる「腐敗防止層」が必要となる。
腐敗防止層のうち、Excelファイルからデータを取り出す部分を、汎用パッケージとして切り出した。&lt;/p&gt;
&lt;h2 id=&#34;現在の状態&#34;&gt;現在の状態&lt;/h2&gt;
&lt;p&gt;npmで公開中（&amp;ldquo;muramasa&amp;rdquo; ではまずいのでさすがにまともな名前をつけた）: &lt;a href=&#34;https://www.npmjs.com/package/@rindrics/tblparse&#34;&gt;&lt;code&gt;@rindrics/tblparse&lt;/code&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;基本的にはnpmパッケージだが、GUIから使えるデモサイトを下記でホストしている:&lt;/p&gt;








&lt;div class=&#34;card&#34;&gt;
  &lt;a href=&#34;https://tblparse.rindrics.com/&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34; class=&#34;card-link&#34;&gt;
    &lt;div class=&#34;card-content&#34;&gt;
      &lt;h4 class=&#34;card-title&#34;&gt;tblparse Demo | tblparse.rindrics.com&lt;/h4&gt;
      
      &lt;span class=&#34;card-url&#34;&gt;https://tblparse.rindrics.com/&lt;/span&gt;
    &lt;/div&gt;
  &lt;/a&gt;
&lt;/div&gt;

</description>
    </item>
    
    <item>
      <title>recurring-backlog-item-creator</title>
      <link>https://rindrics.com/projects/recurring-backlog-item-creator/</link>
      <pubDate>Sun, 09 Nov 2025 00:00:00 +0900</pubDate>
      
      <guid>https://rindrics.com/projects/recurring-backlog-item-creator/</guid>
      <description>&lt;h2 id=&#34;概要&#34;&gt;概要&lt;/h2&gt;
&lt;p&gt;設定ファイルに基づいて、GitHub Projectsのバックログアイテム（issue）を定期的に自動作成するGitHub Actions。
作成周期（毎月、四半期、年1回など）の異なる定期issueは、種類ごとにGitHub Action workflowが必要なため嵩張る。
これを設定ファイル一つで管理できるようにした。&lt;/p&gt;
&lt;p&gt;設定できる項目:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;issueごとの作成月&lt;/li&gt;
&lt;li&gt;タイトルの接頭辞・接尾辞（&lt;code&gt;{{YearMonth}}&lt;/code&gt; などのテンプレート変数を使える）&lt;/li&gt;
&lt;li&gt;Projectのカスタムフィールド&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;目的&#34;&gt;目的&lt;/h2&gt;
&lt;p&gt;振る舞いをコードではなく、データとしての設定ファイルで表現する感覚をつかむこと。&lt;/p&gt;
&lt;p&gt;設定をデータとして扱うと、「今月どのissueを作るか」は、設定と月からissueの一覧を求める変換として書ける。
そこで、CLIとActionとに分けて設計した:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;CLI: 設定ファイルと月を受け取り、作るべきissueをJSONで返す
&lt;ul&gt;
&lt;li&gt;Goを使うことで楽に書けた&lt;/li&gt;
&lt;li&gt;副作用のない関数を中心に実装した。フィールドの検証やIDの解決でGitHubを読む部分はインターフェースで抽象化し、テスト時にモックに差し替え可能にした&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Action: GitHubへの外部副作用（issueの作成、Projectへの追加、フィールドの設定）を担当&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;現在の状態&#34;&gt;現在の状態&lt;/h2&gt;
&lt;p&gt;GitHub Marketplaceで公開中: &lt;a href=&#34;https://github.com/marketplace/actions/recurring-backlog-item-creator&#34;&gt;&lt;code&gt;rindrics/recurring-backlog-item-creator&lt;/code&gt;&lt;/a&gt;&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>ppap-cli</title>
      <link>https://rindrics.com/projects/ppap-cli/</link>
      <pubDate>Sat, 01 Jun 2024 00:00:00 +0900</pubDate>
      
      <guid>https://rindrics.com/projects/ppap-cli/</guid>
      <description>&lt;h2 id=&#34;概要&#34;&gt;概要&lt;/h2&gt;
&lt;p&gt;PPAP（Password付きZIPファイルを送ります / Passwordを送ります / Angoka Protocol）を使った添付ファイル送信を助けるCLI。&lt;/p&gt;
&lt;h2 id=&#34;目的&#34;&gt;目的&lt;/h2&gt;
&lt;p&gt;ブラックジョークのソフトウェア表現の模索。&lt;/p&gt;
&lt;h2 id=&#34;現在の状態&#34;&gt;現在の状態&lt;/h2&gt;
&lt;p&gt;リポジトリで公開している。&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>数珠丸</title>
      <link>https://rindrics.com/projects/juzumaru/</link>
      <pubDate>Mon, 01 Jan 2024 00:00:00 +0900</pubDate>
      
      <guid>https://rindrics.com/projects/juzumaru/</guid>
      <description>&lt;h2 id=&#34;概要&#34;&gt;概要&lt;/h2&gt;
&lt;p&gt;独自ドメインのメールをSlackをクライアントとして送受信するツール。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;受信: SESで受けたメールをS3に保存し、Lambdaで解析してSlackに投稿する。本文がSlack Block Kitの文字数制限を超える場合は、ファイルとしてスレッドに添付する&lt;/li&gt;
&lt;li&gt;送信: botにテンプレートを出させ、記入して投稿したメッセージのURLをbotに渡すと、プレビューが表示される。確認して送信ボタンを押すとSESから送る。同じメッセージからの二重送信は防いでいる&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;目的&#34;&gt;目的&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;独自ドメインのメール送受信環境を作ること
&lt;ul&gt;
&lt;li&gt;商用サービスほどの可用性は必要ないので作ってみた。&lt;/li&gt;
&lt;li&gt;常時稼働のサーバーがないので運用コストが低い。受信したメールはまずS3に保存するので、Slackへの投稿に失敗してもメール自体は失われない&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;「インフラがインターフェイスを実装する」という考え方が、クラスレベルだけでなくパッケージやデプロイレベルでも成り立つことを実感すること
&lt;ul&gt;
&lt;li&gt;メールの解析や送信の判断はcoreパッケージにまとめ、ストレージやメールサーバーとのやり取りはリポジトリのインターフェースとして抽象化したので、一応クラウドプロバイダの種類にも依存しない設計になっている&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;現在の状態&#34;&gt;現在の状態&lt;/h2&gt;
&lt;p&gt;coreパッケージはnpmで公開中（さすがにまともな名前をつけた）: &lt;a href=&#34;https://www.npmjs.com/package/@rindrics/slackmail&#34;&gt;&lt;code&gt;@rindrics/slackmail&lt;/code&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;独自ドメイン宛てのメールは、すべて本ツールで受信している。&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>三日月宗近</title>
      <link>https://rindrics.com/projects/mikazuki-munechika/</link>
      <pubDate>Wed, 01 Jan 2025 00:00:00 +0900</pubDate>
      
      <guid>https://rindrics.com/projects/mikazuki-munechika/</guid>
      <description>&lt;h2 id=&#34;概要&#34;&gt;概要&lt;/h2&gt;
&lt;p&gt;資源評価のプロセス全体をwebアプリケーション化する試み。
計算部だけでなく、業務プロセスの進行: 評価ステータスの遷移などもドメインモデリングされている
。&lt;/p&gt;
&lt;p&gt;デモとドキュメントは下記から利用できる:&lt;/p&gt;








&lt;div class=&#34;card&#34;&gt;
  &lt;a href=&#34;https://mikazuki-munechika.rindrics.com&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34; class=&#34;card-link&#34;&gt;
    &lt;div class=&#34;card-content&#34;&gt;
      &lt;h4 class=&#34;card-title&#34;&gt;資源評価Web | mikazuki-munechika.rindrics.com&lt;/h4&gt;
      
      &lt;span class=&#34;card-url&#34;&gt;https://mikazuki-munechika.rindrics.com&lt;/span&gt;
    &lt;/div&gt;
  &lt;/a&gt;
&lt;/div&gt;









&lt;div class=&#34;card&#34;&gt;
  &lt;a href=&#34;https://docs.mikazuki-munechika.rindrics.com&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34; class=&#34;card-link&#34;&gt;
    &lt;div class=&#34;card-content&#34;&gt;
      &lt;h4 class=&#34;card-title&#34;&gt;資源評価Web 📖 | docs.mikazuki-munechika.rindrics.com&lt;/h4&gt;
      
      &lt;span class=&#34;card-url&#34;&gt;https://docs.mikazuki-munechika.rindrics.com&lt;/span&gt;
    &lt;/div&gt;
  &lt;/a&gt;
&lt;/div&gt;

&lt;h2 id=&#34;開発経緯&#34;&gt;開発経緯&lt;/h2&gt;
&lt;p&gt;下記はプロジェクト開始時のモチベーション文:&lt;/p&gt;
&lt;div class=&#34;info&#34;&gt;
&lt;p&gt;資源評価事業をwebアプリケーション化する実験をしている。
資源計算だけでなく、計算・執筆・提案・議論・合意形成の流れからなる資源評価「事業」全体の話だ。&lt;/p&gt;
&lt;p&gt;この事業の難しさは、科学的・政治的要求にもとづく課題を解決する数値計算方法を開発するところにあるのはもちろんだが、そもそもコミュニケーションの難しさのほうがより重要なのではないか: 立場や知識背景がまったく異なるステークホルダーが、資源評価という関心領域（ドメイン）の下に集まって、漁獲可能量という一つの数値について議論し・意思決定しないといけない。&lt;/p&gt;
&lt;p&gt;ソフトウェア設計者たちは、これと同じ問題が（広義の）ビジネスがある場所には必ず潜んでいることに気づいており、その解決のために「&lt;a href=&#34;https://books.google.co.jp/books/about/Domain_Driven_Design.html?id=hHBf4YxMnWMC&amp;amp;redir_esc=y&#34;&gt;ドメイン駆動設計（Domain-Driven Development. DDD）&lt;/a&gt;」を2000年代前半に確立した。ドメイン駆動設計では、開発過程における思考資源の投資先を、プログラミングのような技術的詳細ではなく、ビジネスの営みを抽出した「ドメインモデル」をつくることに集中する。ドメインモデルは一度作って終わりではなく、ステークホルダー間で認識の齟齬が見つかるたびに、その新たな知識はモデルに反映される。このようにして磨き上げたドメインモデルを中心としてアプリケーションを開発するこの手法は、資源評価事業においてもヒントになると考えている。&lt;/p&gt;
&lt;/div&gt;
&lt;h2 id=&#34;現在の状態&#34;&gt;現在の状態&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;開発停止（検証完了）&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;資源評価プロセスを型で表現し、コード補完などやコンパイル時チェックで開発を支援できることを確かめられた。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://mikazuki-munechika.rindrics.com/reference/maiwashi_pacific&#34;&gt;計算のフローチャート&lt;/a&gt;を実際のコードから生成できることがわかった&lt;/li&gt;
&lt;li&gt;日本語を使うことで、細かいニュアンスの違いも吟味しながらドメインモデリングできることがわかった
&lt;ul&gt;
&lt;li&gt;参考: &lt;a href=&#34;../stock-assess-code-with-japanese/&#34;&gt;ドメインモデリングに日本語を使う&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;検証の過程でわかったこと&#34;&gt;検証の過程でわかったこと&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Node.jsではパラメータ最適化部分で速度が出ず、科学計算に適した言語を呼び出す必要がある
&lt;ul&gt;
&lt;li&gt;frasyrを入れたRを呼び出すのが自然
&lt;ul&gt;
&lt;li&gt;frasyrモノリスを使ったStrangler Figアプローチの後継プロジェクト&lt;a href=&#34;../yoshimoto-samonji&#34;&gt;義元左文字&lt;/a&gt;で検証する&lt;/li&gt;
&lt;li&gt;三日月宗近と義元左文字は背反ではなく、必要であれば両者をつなぐこともできる&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;ドキュメントとしてエクスポートする型は、ドメインエキスパートに見せるものに絞るのがよさそう
&lt;ul&gt;
&lt;li&gt;あまり考えず生成したら&lt;a href=&#34;https://docs.mikazuki-munechika.rindrics.com/references/&#34;&gt;こうなった&lt;/a&gt;が、これを見たドメインエキスパートを圧倒してしまうと思う&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;評価のステータスを型で表すこと自体はいいが、状態をオブジェクトに持たせる旨味はなかった
&lt;ul&gt;
&lt;li&gt;ユースケースを考慮しても、状態はイベントから導出するほうがよいだろう&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
</description>
    </item>
    
    <item>
      <title>データ登録処理に関する技術選定: Revision IDの生成アルゴリズム</title>
      <link>https://rindrics.com/articles/tech-selection-revision-id/</link>
      <pubDate>Mon, 28 Sep 2026 00:00:00 +0900</pubDate>
      
      <guid>https://rindrics.com/articles/tech-selection-revision-id/</guid>
      <description>&lt;p&gt;data-registryによるRevision ID生成アルゴリズムにUUID v7を選択した。&lt;/p&gt;
&lt;h2 id=&#34;背景&#34;&gt;背景&lt;/h2&gt;
&lt;p&gt;資源評価に利用するデータの永続化を担うdata-registryの開発をすすめている。








&lt;div class=&#34;card&#34;&gt;
  &lt;a href=&#34;https://rindrics.com/tags/data-registry/&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34; class=&#34;card-link&#34;&gt;
    &lt;div class=&#34;card-content&#34;&gt;
      &lt;h4 class=&#34;card-title&#34;&gt;Tag &amp;#34;data-registry&amp;#34; | rindrics.com&lt;/h4&gt;
      
      &lt;span class=&#34;card-url&#34;&gt;https://rindrics.com/tags/data-registry/&lt;/span&gt;
    &lt;/div&gt;
  &lt;/a&gt;
&lt;/div&gt;
&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;../tech-selection-idempotency-key-hash-algorithm/&#34;&gt;前回の記事&lt;/a&gt;では、冪等性キー生成に用いるハッシュアルゴリズムを選んだ。
本記事では、永続化処理が実行される際のRevision IDの生成方法について考える。&lt;/p&gt;
&lt;h2 id=&#34;要件&#34;&gt;要件&lt;/h2&gt;
&lt;p&gt;現時点で、data-registryには下記のユースケースが見つかっている:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;版の履歴閲覧: あるデータセットの変更履歴を時系列で表示したい&lt;/li&gt;
&lt;li&gt;データ登録要求の並行受信: 複数人のユーザーが複数シナリオを並列計算し、それらのクライアントからの登録要求を受信した&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;上記から、Revision IDの生成は、以下の要件を満たす必要がある:&lt;/p&gt;
&lt;table&gt;
  &lt;thead&gt;
      &lt;tr&gt;
          &lt;th style=&#34;text-align: left&#34;&gt;要件&lt;/th&gt;
          &lt;th style=&#34;text-align: left&#34;&gt;説明&lt;/th&gt;
          &lt;th style=&#34;text-align: left&#34;&gt;要件ID&lt;/th&gt;
      &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
      &lt;tr&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;一意性&lt;/td&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;各Revisionは一意のIDを持つ&lt;/td&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;SR001&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;不変性&lt;/td&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;一度生成されたら変更されない&lt;/td&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;SR002&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;時系列順序付け可能性&lt;/td&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;同じRevision chain内で自然にソート可能&lt;/td&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;SR019&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;分散生成可能性&lt;/td&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;Coordinatorなしで各プロセスから生成可能&lt;/td&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;SR020&lt;/td&gt;
      &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;ここで、不変性（&lt;code&gt;SR002&lt;/code&gt;）については実装の責務として考慮外とし、残りの要件を満たすID生成アルゴリズムを選定する。
アルゴリズムには複数の候補が考えうるが、詳細な検討の手間を軽減するには、まず時系列順序付け可能性を根拠に選択肢を刈り込むとよさそう。&lt;/p&gt;
&lt;h2 id=&#34;候補アルゴリズム&#34;&gt;候補アルゴリズム&lt;/h2&gt;
&lt;p&gt;ID群が時系列で順序付けされるためには、IDは時刻情報を含むtimestamp-basedな手法で生成される必要がある。&lt;/p&gt;
&lt;p&gt;これに着目すると、例えば&lt;a href=&#34;https://github.com/twitter-archive/snowflake&#34;&gt;Nanoid&lt;/a&gt;やUUID v3, v4, v5, v8は検討対象から除外でき、候補としては下記が残る:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;連番&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/twitter-archive/snowflake&#34;&gt;Snowflake ID&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://www.rfc-editor.org/info/rfc9562/#section-5.7&#34;&gt;UUID v7&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;なお、timestamp-basedな手法としてはUUID v1およびv6も含まれるが、&lt;a href=&#34;https://www.rfc-editor.org/info/rfc9562/#section-5.6:~:text=6-,UUIDv6,instead&#34;&gt;これらと比較した場合にはv7が推奨されている&lt;/a&gt;（スーパー意訳）ため検討候補から除外した。&lt;/p&gt;
&lt;h2 id=&#34;候補アルゴリズムの比較検討&#34;&gt;候補アルゴリズムの比較検討&lt;/h2&gt;
&lt;h3 id=&#34;連番-不採用&#34;&gt;連番（✘ 不採用）&lt;/h3&gt;
&lt;p&gt;その名の通り、連番の整数をIDに使う方法。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;ID: 1, 2, 3, 4, ...
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;この手法の利点はストレージのオーバーヘッドが少ないことだろう。
表現はシンプルだが、それを実現するアーキテクチャや実装は必ずしもシンプルとは限らない。
2種類の実現方法「データベースレベル」「アプリケーションレベル」それぞれについて、pros/consを整理してみる。&lt;/p&gt;
&lt;h4 id=&#34;方法a-データベースレベル&#34;&gt;方法A: データベースレベル&lt;/h4&gt;
&lt;p&gt;PostgreSQL の SERIAL/BIGSERIAL（自動採番機能）を利用する方法。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;pros:
&lt;ul&gt;
&lt;li&gt;✔ 採番の責務をデータベースに持たせられる&lt;/li&gt;
&lt;li&gt;✔ ロック時間が短い&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;cons:
&lt;ul&gt;
&lt;li&gt;✘ 単一のprimary writerに依存&lt;/li&gt;
&lt;li&gt;✘ スケーラビリティ: 並行タスクが全て同じDBに集約&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&#34;方法b-アプリケーションレベル&#34;&gt;方法B: アプリケーションレベル&lt;/h4&gt;
&lt;p&gt;採番の責務をアプリケーションで持つ構成。
この方法をとるとデータベースについては水平スケールが自由になるが、結局ほアプリケーションレベルに移ってきた採番の責務がボトルネックになる。&lt;/p&gt;
&lt;p&gt;つまり、連番形式は宿命的に「最後の値は何か」というグローバルな状態管理に依存する方法で、この状態は必ずどこかに集約されることになる。
結果として、&lt;code&gt;SR020&lt;/code&gt;（分散生成可能性）を満たさないため不採用とする。&lt;/p&gt;
&lt;h3 id=&#34;snowflake-id-不採用&#34;&gt;Snowflake ID（✘ 不採用）&lt;/h3&gt;
&lt;p&gt;&lt;a href=&#34;https://blog.twitter.com/engineering/en_us/a/2010/announcing-snowflake&#34;&gt;Twitter&lt;/a&gt;などの大規模分散システムで運用実績のある手法。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;ID: 175638572750991360
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;     ├─ timestamp(ms)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;     ├─ machine id
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;     └─ sequence
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ul&gt;
&lt;li&gt;pros:
&lt;ul&gt;
&lt;li&gt;✔ 大規模分散システムでの運用実績あり&lt;/li&gt;
&lt;li&gt;✔ ストレージ効率がよい&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;cons:
&lt;ul&gt;
&lt;li&gt;✘ Machine IDの事前割り当てが必須（新規マシン追加時に管理が必要）&lt;/li&gt;
&lt;li&gt;✘ 各machineが自身のsequenceを管理する必要がある&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;ストレージ効率の良さは魅力的だが、プラットフォームの潜在ユーザーの規模を考慮すると、machine IDの割り当てなどの運用オーバーヘッド増加がペイされるとは考えにくい。
data-registryにおける採用は見送ることにする。&lt;/p&gt;
&lt;h3 id=&#34;uuid-v7-採用&#34;&gt;UUID v7（✔ 採用）&lt;/h3&gt;
&lt;p&gt;UUIDの一つ。
タイムスタンプベースのフィールドを備えつつ、同タイプの先行バージョン（v1およびv6）に比べてランダム特性が向上している。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;ID: 0189a5c5-a92b-7b47-b5ad-e6c3cb6c41e9
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   └─ from timestamp ─┘
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ul&gt;
&lt;li&gt;pros:
&lt;ul&gt;
&lt;li&gt;✔ 決定的に生成できるため各ノードで独立生成可能&lt;/li&gt;
&lt;li&gt;✔ 高いB-tree局所性による高速書き込み&lt;/li&gt;
&lt;li&gt;✔ 標準性（cf: &lt;a href=&#34;https://www.rfc-editor.org/info/rfc9562/#section-5.7&#34;&gt;RFC 9562&lt;/a&gt;）&lt;/li&gt;
&lt;li&gt;✔ エコシステム充実: Clojureでも&lt;a href=&#34;https://github.com/danlentz/clj-uuid&#34;&gt;ライブラリ&lt;/a&gt;を利用可能&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;cons:
&lt;ul&gt;
&lt;li&gt;✘ タイムスタンプ衝突にはランダム&lt;sup id=&#34;fnref:1&#34;&gt;&lt;a href=&#34;#fn:1&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;1&lt;/a&gt;&lt;/sup&gt;部分のみで対処する必要がある
&lt;ul&gt;
&lt;li&gt;TODO: ランダム部分のビット長が最悪ケースをカバーするか確認する&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;短所もあるものの、これらはdata-registryのユースケースにおいては問題にならないかもしれないので、実際に試算してみる。&lt;/p&gt;
&lt;h4 id=&#34;同タイムスタンプ内における一意性違反リスク&#34;&gt;同タイムスタンプ内における一意性違反リスク&lt;/h4&gt;
&lt;p&gt;UUID v7では、同じ&lt;a href=&#34;https://www.rfc-editor.org/info/rfc9562/#section-5.7:~:text=Layout-,unix%5Fts%5Fms,5%29%2E&#34;&gt;ミリ秒&lt;/a&gt;内に複数のIDを生成する場合、一意性の保証はランダム部分（74ビット）で対処する必要がある。
この問題をキューイングなどのアーキテクチャで解決しない場合において、UUID v7のエントロピー性能がdata-registryの最悪ケースをカバーできるかを見積もる。&lt;/p&gt;
&lt;p&gt;この問題は&lt;a href=&#34;https://en.wikipedia.org/wiki/Birthday_problem&#34;&gt;誕生日問題&lt;/a&gt;と同じなので、並行プロセス数を\(n\)は、ランダム部分のビット長を\(b\)とすると、衝突確率は下式で表される:&lt;/p&gt;
$$
P(\text{1組以上が衝突}) = 1 - \frac{1}{e^{\frac{n (n - 1)}{2^{b+1}}}}
$$&lt;p&gt;いま1,000,000の並行プロセス（1,000ユーザー × 1,000シミュレーション）が同ミリ秒内にIDを生成する場合、\(n = 10^6\)、\(b = 74\)なので、衝突確率は&lt;/p&gt;
$$
\begin{aligned}
P(\text{1組以上が衝突})
  &amp;= 1 - \frac{1}{e^{\frac{10^6 (10^6 - 1)}{2^{75}}}} \\
  &amp;\approx 1 - \frac{1}{e^{\frac{10^{12}}{3.8 \times 10^{22}}}} \\
  &amp;\approx \frac{10^{12}}{3.8 \times 10^{22}} \\
  &amp;\approx2.63\times10^{-11} \\
  &amp;= 2.63\times10^{-9} \text{\%}
\end{aligned}
$$&lt;p&gt;となり、ピーク時リクエストに対してアーキテクチャによる緩衝を挟まない場合にも、UUID v7のエントロピー性能は充分な安全マージンを持つことがわかった。&lt;/p&gt;
&lt;p&gt;以上から、data-registryにおけるRevision IDの生成したアルゴリズムにはUUID v7を採用する。&lt;/p&gt;
&lt;h2 id=&#34;所感&#34;&gt;所感&lt;/h2&gt;
&lt;p&gt;本記事でも、&lt;a href=&#34;../tech-selection-idempotency-key-hash-algorithm/&#34;&gt;前回の記事&lt;/a&gt;と同様に、最悪ケースに対するキャパシティ見積もりに基づいて技術選定した。
実際には、最悪ケースに対してアーキテクチャ的な解決を挟まない場合にはID衝突以前に別の問題（スループットなど）が生じそうだが、ひとまずの物理的な猶予は把握できた。
大規模アーキテクチャにおける成功例: Snowflake IDも一応は検討したが、個人開発における運用性を考えると選べなかった。&lt;/p&gt;
&lt;p&gt;次回は、Revision IDと冪等性キーの永続化方法について考える予定。&lt;/p&gt;
&lt;div class=&#34;footnotes&#34; role=&#34;doc-endnotes&#34;&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id=&#34;fn:1&#34;&gt;
&lt;p&gt;疑似乱数であり、実際は決定的に生成する。&amp;#160;&lt;a href=&#34;#fnref:1&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;</description>
    </item>
    
  </channel>
</rss>
