第4部 戦略的設計
システムがだんだん複雑になってくると、巨大なモデルを扱ったり把握する必要が出てくる。
そうした状況下では単一の巨大なユニットとして管理するのは難しいため、分解しなければならない。
ここで、小さなシステムに分解していくときに、陥りがちなのが全体が見えなくなること。
「木を見て、森を見ず」と述べられている。
そうならないように以下の3つのテーマを考察している
- コンテキスト
- 蒸留
- 大規模な構造
■コンテキスト
コンテキストについて見ていく前にそもそもコンテキストってなんだって感じですよね。
androidの開発をやっていても当たり前のように出てきます。
辞書を引いても何とも曖昧でややこしいです。
ロングマン先生に依ると
1 the situation, events, or information that are related to something and that help you to understand it
2 the words that come just before and after a word or sentence and that help you understand its meaning
とあります。
日本語でも文脈、前後関係、事情、背景、状況と訳されたりしています。
少し具体的な例を挙げると、
私がテレビを見ていてそこには芸能人が映っているとします。
そこで近くにいる家族が
「ちょっと手伝って~」
と言ったときに、テレビに映っている芸能人に言っているとは思わないですよね?
手伝えるのはテレビを見ている私の方です。
これはコンテキストに依るものだと思います。
もう1つ挙げると…
「ほげ」
と聞くと何を思い浮かべますか?
プログラミング経験がある人やIT系の人だとサンプルコードなどでよく見かける
「hoge」
が浮かびますよね?
そうでない人だと
「何かのリアクション?」「捕鯨?」
みたいなこと思い浮かべるかもしれません。
商品と聞いて大体の人は商品そのものを浮かべるけど、配送業の人だと中身が入った段ボールのことを思い浮かべるみたいなことを聞いたことがあります。
同じ用語でも、受け取り手や場面などによって認識が変わってくるのはコンテキストに依るものだと認識しています。
コンテキストの内容に入っていきます。
境界づけられたコンテキストによって各モデルが適用できる範囲を定義する。
コンテキストマップによってそれぞれのコンテキストとコンテキスト間の関係性を全体的に概観できる。
継続的な統合によってモデルの統一が保たれる。
・境界付けられたコンテキスト
名前の通りコンテキストはあいまいでなく明示的に境界付ける。
明示的な境界は、チーム編成、そのアプリケーションに特有の部分が持つ用途、コードベースやデータベーススキーマなどの物理的な表現などの観点から設定する。
その境界内では、モデルを厳密に一貫性のあるものに保つ、また外部の問題によって注意をそらされたり、混乱させたられたりするのは避ける。
定義することで得られるものは明確さ。
そのコンテキスト外の人達は、コンテキスト内の事は明確に区切られているのであれば考えなくて済む。
ただし、境界を知っておかなければ、知らずに踏み込んでしまう可能性があるからそれは注意が必要。
接点があるコンテキスト間も同様。
・継続的な統合
境界づけられたコンテキストを定義したらそれを安定させなければならない。
継続的な統合はコンテキスト内で、2人以上での作業をする際には頭に入れておく。
絶えずそのコンテキストチーム内で以下2つの統合を行う。
- モデル概念の統合
ユビキタス言語を継続的に練り上げる。そのためにメンバ間で絶えずコミュニケーションを行う。 - 実装の統合
段階的に行われ、再生可能なマージ、ビルドテクニック
自動化されたテストスイート
変更が統合されない期間に対して、妥当な短さの上限を設定するルール
・コンテキストマップ
コンテキストを境界づけても全体的な視点を得られたとは言えない。
境界づけた時点での全体的視点は得られているかもしれないが、それぞれのコンテキスト内で統合を絶えず行っているため、得られているとは言い難い。
変更により境界が明示的でなくなる場合がある。
境界づけられたコンテキスト間のコードの再利用もそういった理由で避けるべき。
全体的な視点を得るためにコンテキストマップを利用する。
コンテキストマップの利用にあたり、以下のルールを守る
境界づけられたコンテキストにそれぞれ名前をつけ、その名前をユビキタス言語の一部にする
現在存在する領域の地図を書く、変換にはそのあと取りかかる
モデル同士の接点を記述して、あらゆるコミュニケーションで必要となる明示的な変換について概略を述べ、共有するものがあれば強調する
マップは常にありのままの状況を表すこと
実際に変更が完了するまではマップを変更しないこと
コンテキスト内であればユビキタス言語が方言化(同じ言葉でもコンテキスト外の人が使っているものとニュアンスの差異があることがある)することもあるが、境界づけられたコンテキストそのものに名前をつけることでどのコンテキストで作業している人でもあいまいにならずに済む。
これらが保たれると以下のより効果的な戦略へと移行できるようになる。
1. 共有カーネル
2つのコンテキスト間(あるいは複数のコンテキスト間)でモデルの一部を共有すること。
カーネルというとそれぞれのモデルの中核を共有するようにとらえるかもしれないが、モデル、コード、モデルの該当箇所に関連するデータベース設計なども含む。
注意点としては、共有している部分は共有していないコンテキスト内の変更に比べて頻度は下げること、他チームの相談なしに行わないこと。
2. 顧客(下流)/供給者(上流)の開発チーム
2つのコンテキストA, Bがあって、BがAからの入力を受け取るなどAがBに何かを要求する場合に顧客/供給者の関係を取ることがある。
A(供給者) → B(顧客)
この場合に、BがAの変更に対して、一方的に受け入れるだけならば簡単だが、Bが拒否権を持っていたりやAに対して何か手続きをさせるような場合は両者が円滑に作業できるようにしなければならない。
そのために…
・顧客の要求が最優先
・供給者チームが顧客を壊してしまわないかと恐れることなくコードを変更できるようにし。顧客チームが上流チームのことを常に気にすること自分の仕事に集中できるようにするには、自動化されたテストがないといけない。
3、4、5は顧客/供給者の関係にありながら顧客の要求に応える動機がない場合、2のことができないので検討する。
顧客の要求に応える動機がないというと攻撃的だが以下のような場合もある
・供給者が多くの顧客を抱えている
・2つのチームが経営階層上で遠く離れている
・昔からの顧客に市場の方向性が変わったため価値が見いだせない
など
3.順応者
一言でいうと上流チームに順応する。
上流チームのモデルに隷従しユビキタス言語を供給する。
下流チームの自由度は低くなるがシンプルになる。
下請けとかこんな感じかなと思う。
上流チームの設計が悪いと下流チームも悪くなるのでその辺りは気を付ける。
4.腐敗防止層
上流のモデルを下流で扱うのが困難な場合に検討する。
上流と下流の間にモデルの変換機能など腐敗防止層を用意してあげる。
そのインターフェースはデザインパターンのファサードパターンやアダプタパターンなどで構成されたりする。
他のシステムとの連携でデータを変換する出入り口とかがイメージしやすい。
5.別々の道
上流と下流の関係だったが、そもそも別々で良い場合など。
これまで学んできた上流と下流の統合方法は全てオーバーヘッドが存在するため、コストが発生する。
よって、それに見合う利益を生み出さないといけない。
利益を生み出せない場合などに、統合が必要なのかを改めて考える。
それでも必要な場合は1~4のどれかを考える。
とりあえず別々の道を検討して無理または面倒ならば何らかの形で統合するとかでいいかも
6. 公開ホストサービス
とあるサービスが他の多くのサービスなどから利用される場合は、共通のサービスを用意する。
手間なので1つ1つについて変換サービスを構築しない。
特定のサービス向けに拡張することはある。
公開APIなど。
7. 公表された言語
統合するお互いのコンテキスト間の変換では共通する言語を利用する。
片方に合わせるとモデルが崩れる恐れがある。
世に回っているドキュメントなどで広く使われているものなどを見つけれるといい。
この辺なんとなく上流に合わせがちになっている気がする…
実際にこれらのパターンをどう使っていくかですが、
新しくプロジェクトを始めるにしろプロジェクトが進行中にしろ
コンテキストマップを描こうとすることが大事になってくると思います。
そのためにはコンテキストの境界を付けること、継続的な統合を行う。
その後は変換できないシステム(外部システムやレガシー過ぎて手がつけられないシステム)に線を引く。
・新しくプロジェクトを始めている場合
作成中のシステム内で継続的な統合が困難になれば共有カーネルを探す。
依存関係が一方向であれば顧客/供給者の開発チームを選択する。
外部のシステムと連携する場合は最初に検討すべきは別々の道。
コストが低いので。
統合が不可欠であるなら、順応者か腐敗防止層かを考える。
出来るだけ腐敗防止層で考えたい。
順応者は心情的によろしくないので。
・プロジェクトが進行中の場合
まず共有カーネルを見つける。
コンテキストの重複した出来るだけ小さいところから始めていく。
テストは必ず作成する。
次に継続的な統合を行い両方を理解した人を増やす。
コンテキスト間でメンバーをローテーションする。
その後にレガシーシステムを段階的に廃止する。
テスト戦略の決定は不可欠で腐敗防止層を経由してレガシーシステムと通信する
公開ホストサービスがあるなら公表された言語が使えないか検討する。