【DDDメモ】モデルの整合性を維持する:順応者

順応者

エリック・エヴァンスのドメイン駆動設計 P.371

 上流チームのモデルに隷従することで生じる、境界づけられたコンテキスト間での複雑な変換を取り除くこと。確かに下流の設計者がとれるスタイルは制限され、そのアプリケーションにとって理想的なモデルが作れなくなるかもしれないが、順応することを選ぶことで統合は大幅に簡略化されるのだ。また、ユビキタス言語を供給者チームと共有することにもなる。供給者がハンドルを握っているのだから、供給者がコミュニケーションしやすくした方がよい。人の役に立ちたいという思いから、彼らは情報を共有してくれるだろう。
 この決定により、上流への依存関係は深くなり、アプリケーションの可能性は上流モデルに今ある能力と純粋に付加的に行われる機能追加によって制限される。心情的には全く魅力的でないので、実際には選択すべき時でも、用いられないことが多い。

 順応者になる方がうまく行ったり効率的な場合はあった。短期的なプロジェクトであったり、上流が大きかったり上下関係で上の場合だ。理想は色々ありつつも、システム的にもチームとしても順応せざるを得ない、またはそのほうが良いことは多い。また、選択したくない心情もわかる。ただ、何が最も高い価値なのかという視点で見れば、順応すべき場合があるのもその通りだと思う。「順応者」という考え方は現実感がありありと伝わってくる…。

エリック・エヴァンスのドメイン駆動設計 P.372

 ここまで、境界づけられたコンテキスト同士の統合における、さまざまな共同作業のやり方を見てきた。非常に協力的な共有カーネルや顧客/供給者チームから、一方的な順応者までさまざまなものがある。次の最終ステップでは、関係性に対する、より悲観的な見方へと進む。ここでは、向こう側からは協力も得られなければ、役に立つ設計も得られないと想定される…。

【DDDメモ】モデルの整合性を維持する:顧客/供給者の開発チーム

エリック・エヴァンスのドメイン駆動設計 P.365

 蒸溜チームが自由に開発の舵をとれなくなるのは、下流のチームが変更に対する拒否権を持っている場合や、変更を要求されるための手続きがあまりに面倒な場合である。下流のシステムを壊してしまうのではないかと心配するあまり、上流のチームが抑制されることさえある。その一方で、下流のチームは蒸溜のチームがつける優先順位の言いなりになって、自分たちではどうすることもできない。
 下流は上流からくるものを必要とするが、蒸溜は下流が作り出すものに対して責任を負わない。【中略】したがって、チーム間の関係を形式化すれば誰もが楽になる。このプロセスは。、2つのユーザコミュニティの間で要求のバランスを取り、下流で必要な機能に関する作業をスケジューリングするために構成される。
 エクストリームプログラミングを行なっているプロジェクトでは、まさにそのための仕組みが準備されている。それがイテレーションプランニングプロセスだ。やるべきなのは、プランニングプロセスの観点から、2チームの関係を定義することだけだ。下流チームの代表はユーザの代表と同じような役割を果たせるので、彼らをプランニングプロセスに参加させ、必要な作業に関するトレードオフについて、この「顧客」と直接議論すると良い。

 これはスクラムのプロセスでも基本になる。このコミュニケーションが、チームの課題意識から自発的に行われるとより良い。システムのデメリットは、チームで補うことができる。もちろん、同時にシステムのデメリットも解決するように努めれば、状況はより良くなる。

エリック・エヴァンスのドメイン駆動設計 P.366

 2つのチーム間に明確な顧客/供給者という関係を確立すること。計画時には、下流のチームが上流のチームに対して、顧客としての役割をはたすようにすること。下流の要件に必要となる作業について交渉し、顧客としての役割を果たすようにすること。下流の要件に必要となる作業について交渉し、予算を立てることで、提供の約束とスケジュールが全員に理解できるようにすること。
 期待されるインターフェースを検証する、自動化された受け入れテストを共同で開発すること。そのテストを上流チームのテストスイートに追加し、継続的な統合の一部として実行されるようにすること。このテストを実施することで、下流への副作用を心配せずに、上流チームは自由に変更を行えるようになる。

 もしかすると、日本では顧客/供給者ということを明示してしまうと上下関係が発生し、強制力が発生してあまりいい結果にはならないかもしれない。ただ、別の言い方として表現すれば良い。それは、それぞれのチームに関係性があり、考慮しなければならないということだ。
 ただし、これはシステムの制約でこうしたというのもあるが、チームの力やレガシーな状態を引っ張らなければならないなどの柵があるかもしれない。このようなやり方と同時に、その問題を解決することを努力することを同時に行う必要があるのではないだろうか。

エリック・エヴァンスのドメイン駆動設計 P.367

 顧客/供給者チームが成功するのは、2つのチームが同じ経営陣の下にいて、最終的な目標を共有しているか、実際に顧客と供給者の関係である別々の企業にいる場合であるようだ。上級チームを動機づけるものが何もない場合は非常に難しい…。

【DDDメモ】モデルの整合性を維持する:共有カーネル

共有カーネル

エリック・エヴァンスのドメイン駆動設計 P.363

 まとまりのない複数のチームが、密接に関連したアプリケーションに取り組んでいると、暫くの間は作業を急いで進めることができても、それぞれが作り出すものはうまく適合しないだろう。結果的に、初めから継続的な統合を行った場合よりも多くの時間を、変換層と修復作業に費やすことになり、この二度手間のせいで共通のユビキタス言語が持つ利点を失うことになる。
 多くのプロジェクトにおいて、大部分は独立して作業している複数のチーム間で、インフラストラクチャ層が共有されているのを私は見てきた。これと似たようなことが、ドメインの中でもうまく機能することがある。モデルとコードベース全体を完全に同期させるとオーバーヘッドが大きくなりすぎるかもしれないが、慎重に選択したサブセットを同期するのであれば、少ないコストで利益のほとんどを得られる。
 それゆえ:
 2つのチームが共有することに合意したドメインモデルのサブセットを指定すること。もちろん、これには、モデルのサブセットに加え、コードのサブセットや、モデルの該当箇所に関連するデータベース設計も含まれる。この明示的に共有されたものには、特別な地位が与えられているので、もう一方のチームに相談せずに変更してはならない。
 稼働しているシステムは頻繁に統合すること。ただし、チームの中で行われる継続的な統合よりは、やや頻度を下げること。この統合の際には、両チームのテストを実行すること。

 このような状態は容易に予測できる。複数のコンテキストまたはチームで相互参照する、もしくは共通的なドメインやインフラストラクチャが存在することはあるだろう。そういう部分について、明確に定義した上で、複数チームで共有するというのは、効率的な部分があるだろう。
 ただ、3チーム、4チームと共有するチームが増えてくると話は別だろう。その場合は、「別々の道」をあえて選んだほうがうまくいく場合もあるのではないだろうか。そういう意味で、共有カーネルの考え方は、プロセスの高度化も含め、プロジェクト進行中においてもチームが判断できるような状態にする必要があるのではないだろうか。

【DDDメモ】モデルの整合性を維持する:コンテキストマップ

コンテキストマップ

エリック・エヴァンスのドメイン駆動設計 P.352

 他のチームに所属する人々は、コンテキストの境界をあまり意識せず、境目をぼかしたり相互の接続を複雑にしたいする変更を、そうとは知らずに加えるかもしれない。別々のコンテキスト同士を接続しなければならない場合、コンテキストは相互ににじみ出す傾向がある。
 境界づけられたコンテキスト間でのコードの再利用は、危険なので避けるべきである。機能とデータの統合は、変換を経て行わなければならない。混乱を減らすためには、別々のコンテキスト同士の関係を定義し、プロジェクトにあるモデルのコンテキストすべてに関する、全体的な視点を作成しなければならない。

【中略】

 プロジェクトで機能しているモデルをそれぞれ識別して、その境界づけられたコンテキストを定義すること。これには、非オブジェクト指向のサブシステムにある暗黙的なモデルも含まれる。境界づけられたコンテキストそれぞれに名前をつけ、その名前をユビキタス言語の一部にすること。
 モデル同士の接点を記述して、あらゆるコミュニケーションで必要となる明示的な変換について概略を述べ、共有するものがアレば強調すること。
 まず、「現在存在する」領域の地図を書くこと。変換にはその後でとりかかること。

 数十人で開発するシステムの場合、その中でコンテキストが分かれ、チームも分かれる。ただ、コンテキストは違っても、1つのビジョンに向け存在しているシステムのため、相互の接続を行う必要がある。そういう場合、どのようにそのバランスを維持する必要があるか。
 それには、まずは全体としてのコンテキストマップをメンバー全員が把握している必要があり、その上で自分たちが行なっているコンテキストはどこで、どのコンテキストとコミュニケーションを取るべきなのかを考え続ける必要があるのではないか。
 また、実際のコーディングの場合、どのようにクラスを作成し、その間を相互接続するためのサービスをどう作る必要が有るのか。どれがpublicかprivate(公開して良いクラスかどうか)なのか。それをどう表現するのか。まだその点についてのイメージが湧いていない。もっと深く読み込んでいきたいと思う。

【DDDメモ】モデルの整合性を維持する:継続的な統合

継続的な統合

エリック・エヴァンスのドメイン駆動設計 P.349

 多くの人々が同一の境界づけられたコンテキストで作業していると、モデルが分裂する傾向は強くなる。チームが大きければ大きいほど、問題も大きくなるが、3、4人ほどの少人数でも深刻な問題に直面することがある。しかし、システムをさらに小さいコンテキストに分割してしまうと、結局は有益なレベルで統合し、一貫させることができなくなる。

【中略】

ドメイン駆動設計にある他のパターンと同様、継続的な統合も2つのレベルで作用する。それが、①モデル概念の統合と②実装の統合である。

【中略】

実装成果物の統合は、システム的なマージ/ビルド/テストのプロセスによって実現され、モデルの分派を早い段階で明らかにする。統合に向けて多くのプロセスが用いられるが、効果的なもののほとんどは、次のような特徴を共通して持っている。

  • 段階的に行われ、再生可能なマージ/ビルドテクニック
  • 自動化されたテストスイート
  • 変更が統合されない期間に対して、妥当な短さの上限を設定するルール

 効果的なプロセスに置いて実装と対になるのが、概念の統合である。ただ、これが正式に含まれることはめったにない。

  • モデルとアプリケーションに関する議論における、ユビキタス言語の絶え間ない鍛錬

 CIで行うことは、実装の統合と概念の統合とある。なるほど…。これは深い…。概念の統合については、スクラムをうまく使ってプロセス化できないものか。

エリック・エヴァンスのドメイン駆動設計 P.350

 全てのコードと外の実装成果物を頻繁にマージさせるプロセスを策定すること。その際、分裂に対して素早く警告する自動化されたテストも一緒に用いること。ユビキタス言語の執拗な鍛錬を絶え間なく行い、モデルに対する共有された見方を練り上げること。概念は、別々の人の頭のなかで進化していくからだ。
 最後に、仕事を必要以上に大きくしないこと。継続的な統合が不可欠になるのは、境界づけられたコンテキスト内だけだ。隣接したコンテキストを巻き込んだ設計に関する問題は、変換も含め、同じペースで取り扱わなくても良い。

 最後の一文について、ここでは答えが書かれていない。まだ読んでないのか、読んでるけどわかっていないのか。この部分をどうしていければいいのか、もっと深く探求しよう。

【DDDメモ】モデルの整合を維持する

境界づけられたコンテキスト

エリック・エヴァンスのドメイン駆動設計 P.344

 複数のモデルはどんな巨大なプロジェクトにも存在する。だが、別々のモデルに基づくコードが組み合わされると、ソフトウェアは、バグの温床となり、信頼できなくなり、理解しにくくなる。メンバー間のコミュニケーションは混乱し始める。あるモデルをどのコンテキストで適用すべきでないのかについては、ほとんどの場合不明瞭である。

【中略】

 モデルが適用されるコンテキストを明示的に定義すること。明示的な協会は、チーム編成、そのアプリケーションに特有の部分が持つ用途、コードベースやデータベーススキーマなどの物理的な表現などの観点から設定すること。その教会内では、モデルを厳密に一貫性のあるものに保つこと。ただし、協会の外部の問題によって注意を逸らされたり、混乱させられたりするのを避けること。

 境界づけられたコンテキストは、特定のモデルが適用出来る範囲を制限する。そうすることで、チームメンバーは、何が一貫性を持つべきで、それを他のコンテキストとどう関係づけるかということについて、明確な理解を共有できるようになる。そのコンテキスト内では、モデルを論理的に統一された状態に保つこと。ただし、その境界の外に対して適用できるかどうかは気にする必要がない。他のコンテキストでは他のモデルが適用されるが、そのモデルとは用語法や概念とルール、ユビキタス言語の方言が異なっている。

 自分自身もやってしまったのは、コンテキストの境界がチームや組織が違う状態にもかかわらず、それを共通化することや関連を持つように考えてしまった。そうすると、関連がカオスになり、もし複数チーム数十人で開発を行った場合、一貫性が保てなくなる可能性が出てきてしまう。そういう意味で、境界を明確に分け、境界内での一貫性を保つ、境界同士ではユビキタス言語の方言が異なる可能性も含めて、明快になる。

エリック・エヴァンスのドメイン駆動設計 P.346

 さらに別のチームが、貨物船の運行スケジュールを立てるためのモデルとアプリケーションに取り組んでいる。スケジュール担当チームと予約担当チームは一緒に作業を始め、両チームとも単一の統一されたシステムを作り出すつもりでいた。この両チームは形式張らずに調整し合い、たまにオブジェクトを共有することもあるが、その方法を体系化してはいない。この2つのチームは同じ境界づけられたコンテキスト内で仕事をしているわけではないのだ。これが危険なのは、自分たちが別々のモデルに取り組んでいることに、彼れが気づいていないからである。

 こういうことはありえる。明確に分ける理由は、形でその違いを表明できるからだ。そういう意味で、どちらにしろ曖昧にするのはデメリットが大きいし、曖昧せざるを得ない理由があるのであれば、ある程度明確なプロセスでそのデメリットを補完する必要がある。

エリック・エヴァンスのドメイン駆動設計 P.347

 別々のモデルの要素を組み合わせると、2種類の問題が生じる。それが、重複した概念と偽同族語だ。概念の重複とは、実際は同じ概念を表しているモデル要素(およびそれに付随する実装)が2つあるということだ。その情報が変更されるたびに、2ヶ所で変換を伴う変更が必要になる。新しい知識によって、オブジェクトの1つが変更されるたびに、もう一方も分析をやり直して変更しなければならない。ただし、実際には分析がやり直されることはないので、結果として同じ概念の2つのバージョンが作られ、別々のルールに従い、データまで異なることもある。その上、チームメンバーは、同じ事を実行する方法を、1つだけでなく2つ学ばなければならず、しかもしれをずっと同期させておかなければならないのだ。
 偽同族語は【中略】2人の人が同じ用語(または実装されたオブジェクト)を使って、同じ事を話しているつもりでいるのに、実はそうでないというものである。

 これがはっきりと境界づけることのデメリットだ。別々にすることでモデルが明確になるし、チームが考える範囲も小さくて済むが、その分境界ごとの乖離が現れる。その際は、リファクタリングをしたりチームを再編したりする必要があると思うが、境界づけられたコンテキストを徹底する場合は、その辺のバランスを早く察知できるようなプロセスやチーム構成が必要になるのではないか。

【DDDメモ】抽象化されたコア

抽象化されたコア

エリック・エヴァンスのドメイン駆動設計 P.441

 別々のモジュールに置かれたサブドメインの間で大量の相互作用があると、モジュール間で多数の参照を生成する必要が生じて、分割したことの価値をほとんどなくしてしまうか、相互作用を間接的に行わなければならなくなって、モデルがあいまいになるかのいずれかとなる。
 分割する線を垂直にではなく、水平に引くことを検討すること。多態性によって、抽象型のインスタンス間にある詳細なバリエーションの多くを無視出来るようになる。モジュール間の相互作用のほとんどが、叩いてきなインターフェースのレベルで表現出来れば、それらの方をリファクタリングして、特別なコアモジュールに入れるのは意味のあることかもしれない。


~中略~


モデルにおける最も根本的な概念を識別し、それを別のクラス、抽象クラスまたはインターフェースにくくり出すこと。この抽象的なモデルは、重要なコンポーネント間の相互作用をほとんど表現するように設計すること。この抽象的なモデル全体を独自のモジュールに入れること。ただし、特殊で詳細な実装クラスは、サブとメインによって定義されたモジュールに残すこと。

作ることを考え始めると、そのシステムの目的を見失いやすくなる。もちろん作る際は、詳細やレアケースも含めて考える必要あるが、価値や目的の視点で見ると、それほど重要ではないことのほうが多い。そういう意味で、意識的だけではなく、モデルとしてコアドメインとそれ以外をいかに明確にしておくかというのが大切になってくるのではないか。

リファクタリング対象を選ぶ

エリック・エヴァンスのドメイン駆動設計 P.443

うまく分解されていない巨大なシステムに遭遇したら、どこから手をつければよいだろう?XPコミュニティにおいて、その答えは以下のどちらかになる傾向がある。

  1. すべてリファクタリングが必要なのだから、どこから着手しても構わない。
  2. どこであれ困っているところから始める。自分の作業をかたづけるのに必要なものをリファクタリングする

 だが、私はどちらも支持しない。前者は、トッププログラマだけで構成されたごく僅かなプロジェクトを覗いて、現実的ではない。後者は、重箱の隅をつつくことになりやすく、対処療法はしても根本原因を無視し、最もひどくもつれた場所を敬遠してしまう。結局、コードはどんどんリファクタリングしにくくなる。


 では、すべてには手が回らず、かといって困ったときだけ対応することもできない場合、どうすれば良いだろうか?

  1. 困ったときのリファクタリングをする際に、原因がコアドメインかコアと補助的要素の関係を含むものかどうかを調べる。そうであれば、歯を食いしばって、まずそれを修正すること。
  2. 自由にリファクタリングする余裕がある場合、最初に集中すべきなのは、コアドメインのより適切なくくり出し、コアの隔離の改善、補助的なサブドメインから不純物を取り除くことによる汎用サブドメイン化である。

 これがリファクタリングという出費によって、最大の効果を上げる方法なのだ。

対象が大きくても、コアドメインを如何にくくりだしていくかを考えていくというのは納得。自分としても、まずは影響の少ないところから、という風にやってしまうが、それは少し目的が違うかもしれない。こういう方向で考えていこう。