抽象化とは「手段」です

抽象化 (abstraction) は、設計の基本スキルですね。

とはいっても「抽象化とは何か?」とか「どうやって抽象化するか?」を、簡単に説明するのは難しい。

私の場合「抽象化」という言葉を理解してから、設計を覚えたわけではない。

ソフトウェア開発の現場で、失敗しながら覚えてきた「コツ」みたいなものが「抽象化」と呼べるかな?という程度の感じ。

超自己流「抽象化論」...

抽象化の結果


抽象化は「手段」なので、何か結果が残ります。
「あるもの」を抽象化すると「別のもの」が出てくる。

そして、抽象化した「結果」は、元の「あるもの」に比べ、

・小さい
・少ない
・短い
...

という特徴がある。

簡単でしょう?

「小さくする」「少なくする」「短くする」ことが「抽象化」なんです。

大きくなる、多くなる、長くなるのは、抽象化とは、正反対の方向。
これは、具象化。「具だくさん」にすること。

「お吸い物」が抽象化で、「ごった煮」が具象化か?

抽象化:抜粋/要約


私が、最初に "Abstract" と出会ったのは、大学で、英語の文献を読まされたとき。

文献の最初に必ず「Abstract」がある。
文の数にして、10以下で、段落ひとつ、程度が多かったかな。

数10ページある文献の内容が、とっても短く、まとめてある。

日本語でいうと

・抜粋
・要旨

とか、そんな感じでしょうか。

「論文の全体」を「ひとつの段落」に「小さく」まとめなおしたもの。
それが "Abstract" なんですね。

文献の最後にも、数段落の「Summary(要約)」がある。

これも「論文全体」を「短く」まとめたもの。

「抽象化(Abstraction)」というのは、ようする、こういうことなんです。

「大量の情報」から、大事なとこだけ抜き出して「小さく」したもの。

抽象化:短縮


今日は、「紀元後2010年1月30日土曜日」です。

日常生活では、こんな長ったらしい表現は、使いませんね。

「今日の次の次の日は、紀元後2010年2月1日である」なんて言わないですよね?

・あさって
・月曜
・週明け
・月初
・1日(ついたち)
...

状況によるだろうけど、こうやって、簡単な言い方で、済ませている。

これも「抽象化」の一種なんです。
「長い表現」を「短い」表現に短縮する。
文字の数も「少ない」ですね。

ソフトウェア技術者だという、こういうのが得意というか、好きな人が多い。

・Domain-Driven Design を DDD
・String を s
・index を i
...

これも、抽象化の一種。
元の表現に比べ、小さく、文字数が少なく、短くなっているから。

現実世界、モデル、コード


「現実世界」は、理解不能なくらい、めちゃくちゃ具だくさんで、入り組んでいる。

モデルは、現実世界を「小さく」「少なく」「短く」まとめたもの。

UML の図なんて、現実世界の、ほとんど、すべてを取り除いた、ごくごく少数の要素を絵にしたもの。
UML で描いたモデルはスーパー「抽象絵画」なんですね。

モデルをコードにすると、モデルよりコードのほうが「大きく」「多く」「長く」なる。

とは言っても、コードも、現実世界から比べれば、「超々」抽象的。

だから、モデルを描いたり、コードを書いたりしている、ということは、かなりレベルの高い「抽象化」を、毎日やっている、ということなんです。

※ Abstarct 宣言するのが、抽象化ではない。

正しい抽象化?


時々

・抽象化は難しい
・抽象化できない

という言い方を目にします。

わたし流の解釈だと、コード書く人が「抽象化が難しい」とか「抽象化できない」なんてわけがない。

コードを書くこと自体、すごい「抽象化」作業なんですから。

「正解」とか「真理」ではない


どうも「抽象化」という手段(作業)に「正解」や「真の答え」があると思っている人が多そう。

この「正解」幻想が、「抽象化」のハードルをめちゃくちゃ高くしちゃっているんだと思う。

「抽象化」は、単なる手段なんで、「元になったもの」より「結果」が

・小さく
・少なく
・短く

なっていれば、それは、「抽象化」できた、ということ。

抽象化したら、長くなった、というのは、さすがに間違い。

※ Abstract Class が 100行超えていて、具象クラスが、10行という、冗談を時々、見かけますけどねえ。

役に立つこと


抽象化の「結果」が、役に立てば、それでOK。
役に立たなければ、やりなおし。

ただ、それだけのことです。

・簡単な絵を描いてみたら、みんなの理解が一気にすすんだ
・いろいろな言葉がとびかって混乱したけど、用語を整理したら、すっきりした
・ぐちゃぐちゃしたコードからメソッドを抽出して、良い名前をつけたら、コードが、読みやすくなった
...

こういうのが「役に立つ」抽象化。

自分は「小さく」「少なく」「短く」して「わかりやすく」したつもりでも、

・伝わらない
・誤解された
・ぽかんとされた
...

というのは、「役に立たなかった」から、失敗。

でも、「まちがい」ではない。
その状況では「役に立たなかった」だけのこと。

「まちがい」なんてない


抽象化に「まちがい」なんてないんです。

別の方向に「小さく」「少なく」「短く」して、やりなおしてみるだけのこと。

「抽象化」は設計の基本。だから、ある方向の「抽象化」が「役に立たなかった」からといって、「抽象化」を放棄してはいけない。

別の方向に「抽象化」してみる。

そうやって、何回も試行錯誤をしていると、自然に、役に立つ抽象化のコツが掴めてくる。

「抽象化」の方向の引き出しが多い人、「抽象化」を状況に応じて手を変え品を変え、やり直せる人。

そういう人が「設計能力」が高い、という。

設計スキルをアップするには、

・「抽象化」の引き出しを増やす

ことです。

ものごとを、

・小さくする
・少なくする
・短くする

には、いろいろなやり方がある。
いろいろな「方向」への抽象化がある。

そういうやり方、「方向」については、次の記事で書きます。

次回予告「抽象化の引き出しを増やす」...

DDD(ドメイン駆動設計) 14章の要約

ドメイン駆動設計の14章は、規模の大きいアプリケーションシステムのモデリングと設計の「基本用語」を提示している。

これらの言葉を、共通の言語とすることで、プロジェクトの関係者が、アプリケーションの全体像を、話合うことができる。

・現状はどうなっているか?
・これから、どうしていこうか?
...

プリミティブ


ほかの用語を使うための、基本中の基本の用語。

・Bounded Context
・Continuous Integration(CI)
・Context Map
・開発チーム

使用例:

・ひとつの Bounded Context は、ひとつ開発チームが担当する
・Bounded Context は、CI で、整合性を確保する
・複数の Bounded Context 間の関係を、Context Map で描く。

連携パターン


連携パターンは、

・Separate Ways
・Conformist
・Anticorruption Layer

・Customer/Supplier Teams
・Open Host Service
・Shared Kernel

の6つが基本。

また、パターンにはなっていないがアプリケーション全体を "Single Bounded Context" にする、という選択肢もある。

Context Map 実践ガイド


典型的な開発プロジェクトとして

・2つのメインの Bounded Context
・2つのサブの Bounded Context
・2つの外部システムとの連携

という規模を想定して、システム全体の把握や、今後の進め方について、10のガイドラインを説明してる。

もちろん、上記の、4つのプリミティブな用語と6つのパターン名で説明している。

10のガイドラインで、繰り返しでてくるキーコンセプトは、

・開発チーム
・チーム間のコミュニケーション

の2つ。

開発チームの構成、チーム間のコミュニケーションの現状が、Context Map の現状である。

Boudned Context 間の連携パターンを変えようとすることは、開発チーム、チーム間のコミュニケーションを変えよう、ということ。

そこが、成功ポイントだし、いちばんの制約条件にもなる。

「開発チーム」や「チーム間のコミュニケーション」の当事者は、開発者自身。

難しい移行テーマ


14章の最後は「非常に難しいテーマ」として4つのテーマをとりあげている。
ひとつの開発プロジェクトの範囲では、達成できないほど、難しいテーマ。

・Separate Ways から Shared Kernel への移行
・Shared Kernel から Single Bounded Context へ
・レガシーシステムのフェードアウト
・Open Host Service から Publised Language への移行

それぞれのテーマを、6、7ステップで、段階的に実施していく「アイデア」が提示されている。

正直、どれもめちゃくちゃハードルが高そう。

このレベルのテーマに取り組むには、14章の考え方・やり方だけで立ち向かうのは無謀というものでしょう。

14章の Context Map パターンとは違った角度から、大きな複雑なシステムに立ち向かうための、原則と実践が、次の2つの章のテーマ

15章 Distillation ( 蒸留 )
16章 Large-Scale Structure ( 構造 )

ドメインモデルを「蒸留」する動機、効果、やりかた。
ドメインモデル内部を「構造化」する動機、効果、やりかた。

Context Map を軸にした14章のアプローチは、「全体像を把握」して「今後の進め方」を検討するための、すばらしい考え方だし、やり方だと思う。

良い地図は、必要。でも、地図だけでは、先に進めない。

大きく複雑なシステムに、立ち向かうためは、ドメインモデルを蒸留し、構造化するやり方を習得すべし。

Context Map パターン 実践ガイド

Domain-Driven Design(DDD)では、Context Map パターンを実践するガイドラインを説明している。

・現在の、Context の境界(Boundary)は、どこか?
・今後、どうしていくか?

・Context 間の、連携はどうなっているか?
・これから、どうしていくか?

Context Map で、"AS IS"モデルを造って、次のステップを話しあうための、考え方とやり方の10のガイドライン:

1.開発チーム、所属組織
2.Context の当事者
3.境界(Boundary)の調整
4.外部システムの捉え方
5.外部システムとの連携
6.Bounded Context ごとの開発チーム
7.特殊なニーズ用の Bounded Context
8.配置
9.連携パターンのトレードオフ
10. プロジェクトの途中から、Context Map パターンに取り組む場合

読み方によっては、プロジェクト管理論。
でも、エバンスの視点は、「プロジェクトマネージャ」ではない。

動くソフトウェアを開発する現場の技術者の視点に徹している。
開発技術者が、Context Map (AS IS モデル)をどうモデリングし、どう、発展させていくか、を開発者の視点から、書いている。

開発チーム、所属組織


第一の視点は、Context Map は「コミュニケーションのマップ」だということ。

チーム内のコミュニケーションと、チームをまたがったコニュニケーションは、頻度や内容がまるで違う。
チームは、目的や判断基準を共有しているが、別のチームは、別の目的、判断基準を持っている。

Context Map を描く基本要素「Bounded Context」は、ひとつのチームの担当範囲、というわけだ。

Bounded Context と Bounded Context の関係も、ようするに、チーム間のコミュニケーションの関係ということ。

Context Map で"AS IS"の関係を示すとき、それは「チーム間のコミュニケーション」とリンクする。

チーム間のコミュニケーションがまったくない Bounded Context 間に、「関係」は、今は存在しないはず。

そして、今後、どうするか、という検討をする時も、この「チーム間のコミュニケーション」は、重要な考慮ポイント。

二つのチームが同じボスの配下なら、密な連携が可能。

部門が違うチーム、所属企業が違うチーム間では、それだけ、連携が難しくなっていく。

机上の「ドメインモデル」の Context Map であれば、理想的な連携が描けるかも知れない。

でも、現実世界では、「チーム」「組織」「コミュニケーション」が、大きな決定要因になる。

Context 間の連携を、「今後はこうしたい」という提案はいろいろできるだろうけど、「チーム」「組織」の "AS IS" の構造に十分に理解し、配慮すること。

Context の当事者


Bounded Contextって、ひとごとではなく、自分たちが当事者なんだ、っていうことですね。

エバンスは、ありがちな開発プロジェクトを、

・2つのメインの Bounded Context
・2つのサブ(支援役)の Bounded Context
・2つの外部システム ( in bound と out bound )

という、4つの Bounded Context + 2つの外部インタフェース、を提示している。

1チーム1Bounded Context が基本なので、4つのチームで分担してシステム開発を進める、というわけだ。

大切なのは、全てのチーム、全てのメンバーが、

・自分たちの Context と境界(Boundary)
・自分たちが連携すべき Context

を理解し、共有していること。

Bounded Context は、絵に描いたモデルではなく、人と人のコミュニケーションが作り出すもの。つまり、開発者自身が、Bounded Context の当事者なんですね。

Context Map は、コミュニケーションの濃淡、方向性の現状を、絵にしてみたもの。

Boundary(境界)の調整


Boundary に論理的な唯一の解があるわけではない。
「決め事」という性格が強い。

だから、状況によって、Boundary(境界)を調整することは、可能だし、また、必要な作業。

調整の方向は、「広げていく」か「狭めていく」のどちらか。
ちょうどバランスが良いところ、個々のチームや、チーム間の連携がやりやすいところを、見つけにいく。

広げるメリット


単一モデルでカバーする問題領域が広ければ、ユーザにとって、わかりやすく、一連の流れで使える範囲が広がる。

「2つのモデル+マッピング」で考えるより「一つのモデル」で考えるほうがわかりやすい。

同じ言葉が仕事ができる範囲が広がる。

狭めるメリット


小さいチーム、狭い範囲ほど、コミュニケーションを濃密にできる。

CI(継続的インテグレーション)が実施しやすい。

大きなモデルでは、すぐれた設計スキル(特に「抽象化」能力)が必要。小さなモデルなら、そこそこのスキルでも対応できる。

専門特化したニーズに丁寧に対応できる。

小さく始める


個人的には、

・小さい単位にしてみる
・Bounded Context 間の関係は、最小(できればゼロ)にしてみる

というところからスタートするのが良いと思っている。

大きく入り組みはじめた、モデルとコードベースを、分割するより、小さくわかれた、単純なモデルとコードベースを、マージするほうが、簡単だし、安全だと思う。

外部システムの捉え方


既存の外部システムとの連携は、だいたい、こんな感じ;

・仕様の変更とかはできない
・相手チームがすでにいない(相談相手がいない)
・たぶん、複数の矛盾したモデルの乱雑な集合体

相手とコミュニケーションできないし、相手のモデルやその意図を確認できない。
そして、仕様も変更できない。

これは、Bounded Context のメンバーとして扱うのは無理。

Context Map 自体の外部との境界(Boundary)の外にコンポーネントとして、おいておくのが良い。

外部システムとの連携


選択肢は三つ。

・Separate Ways パターン
・Conformist パターン
・Anticorruption Layer パターン

Separate Ways パターン


最初に検討すべきパターン。

既存システムのモデルと、新システムのモデルの連携というのは、至難の技であることが多い。
相手が相手だから。

ドメイン層の連携としては「 Separate Ways パターン」は、最初に考えるべき、有力な選択肢。

ドメイン層が連携していないくても、ユーザの使い勝手を改善する方法はいろいろある。

・共通のトップページ
・お互いのトップページ間の相互リンク
・Portlet で、複数のアプリケーションを1画面に表示
・LDAP 等で、ID/PW の認証の一元化
...

UI層や、インフラ層の改善だけでも、できることはいろいろあります。

ドメイン層が Separate Ways パターンでも、サービスが完全に孤立する、というものではない。

相手が、既存システムなので、できることは限られかもしれないけど、Separate Ways パターンで妥協してもらえるなら、これが、もっとも低コストの解決策。

初期の開発コストだけではない。
モデルの異なった世界を無理やり連携すると、運用の負担や保守性の劣化、という IT 負債を確実に抱え込むことになる。そのコスト。

Conformist パターン


「目をつぶって相手の(腐った?)モデル」に合わせるのは、心あるソフトウェア技術者としては、楽しい選択肢ではない。

でも、現実問題として、Anticorruption Layer パターンを実践するための予算も時間もない、というのは現実でよくわる状況です。

Conformist パターンが現実の選択肢、というのはよくあるっこと。

Conformist パターンは、積極的に選ぶ気持ちにはなれない。

いっしょに仕事をしている、ドメインの専門家に喜んでもらえるソフトウェアを造ろうと思っても、あきらかに歪んだモデルを元に、期待にこたえるのは難しい。

でも、そういう状況でも、「良い仕事」をする余地はたくさんある。
同じ状況でも、腕の良い技術者のやる仕事とそのユーザ満足度は、かなり高いはず。

状況が厳しいけど、腕を磨く良いチャンス、くらいの気持ちで取り組みたい。

今回の開発テーマは、「既存の基幹システム」への、ちょっとしたアドオンのサービス、というような場合は、Conformist パターンの選択を考えるべき。

ちょっとしたアドオン部分に、モデリングを行い、別モデルで、サービスを構築することがにメリットと、コストのバランスを考えるべき。

ちょっとしたアドオンなのに、そこだけ別モデルにしてしまうと、これも、運用や保守の IT 負債になってしまう。

分析レポートとか、アドホックな問合せというような「消費オンリー」のアドオン的なアプリケーション開発では、Conformist パターンが、現実的な選択肢。

Anticorruption Layer パターン


最後の選択肢。
もちろん、これが必要なことも多い。

でも、作るにしても、"a small interface" にこだわること。

ミニマリズム。連携をどこまで絞り込んで、小さくできるかが、モデリングと設計の腕の見せ所、というわけだ。

業務の理解が甘く、モデリングも設計も、いまひとつの技術者に限って、「モンスター変換モジュール」を、がーっと書いちゃったりする。

プログラミング腕自慢だったら、なおさらですね。

「大は小をかねる?」。
いえいえ。「大きい」ことは、それだけで、負債なんです。ソフトウェアの場合は。

"a small interface" 、ミニマリズムこそ、良い設計。

ひとつのチームで、一つのBounded Context


一つの Bounded Context の開発は、一つのチームが担当する。

一つのチームは、複数の Bounded Context の「保守」を担当することは可能。

複数のチームが、一つの Bouded Context に手を出すことは、完全な、アンチパターン。

当たり前のことだけど、プロジェクトが進行していくと、"AS IS" では、びっくりするようなことが起きているかもしれない。

例えば、2つの Bounded Context を一つのチームが「並行開発(?)」。
線表がこうなっているプロジェクトは、いまでもたくさんありそうです。
で、実際も、並行して開発できているかは、わかりませんが...

Context Map で、現実を素直に見つめなおして、実態を絵にしてみて、関係者で共有してみましょう。(あるべき姿ではなく、「現実」)

現実を絵に描くだけで、いくつか、自明の改善ポイントが見つかるかもしれない。

特殊なニーズ


企業の中には、少人数で、特殊な業務を行っている人たちがいるものです。

使っている言葉がかなり特殊。聞いていて違和感があるので、わりと見つけやすいことが多い。

どの会社にもいて、必ず別の世界の言葉を使っているのは小数派は、「経営者」です。

経営者自身が求めるニーズは、とても特殊。会社の中の役割が特殊なわけですから、ニーズも、もちろん特殊。

「経営者向けの情報サービス」は、難易度の高いドメインの一つですね。

切り分ける


基本は、こういう分野は、小さな Bounded Context として切り出して、開発も小数の専任チームで対応する。連携は、 Separate Ways パターンでスタートする。

実は、業務全体の理解が進むと、「特殊な別世界」ではなく、合理的な連携相手、ということが見えてくる。

でも、開発の初期に、そういう「深い理解」の基づいたモデリングは、無理です。

いろいろなモデリングをやって、設計・実装して、フィードバックをもらいながら、リファクタリングしてながら、ソフトウェアを発展させていく。その結果として「深い理解」にたどり着き、別世界だと思っていた世界が、「なんだ、こういうつながりだったんだ」とわかるようになる。

小さく、とにかく始めてみる


「深い理解」にたどり着くには、まず、最初の一歩を踏み出すこと。
小さな Bounded Context にして、まずは、やりはじめてみる。

隣にヒントがころがっている


特殊な Bounded Context を担当するチームは、関連しそうな隣接の Bounded Context のチームと、コミュニケーションをこころがけること。

特殊だと思っていたが、実は、基本は、単純でわかりやすい構造でつながっている、という発見をできるチャンスが確実に増える。

特に、そのチーム内では、それほど、重要でないと思っているモデルの済みや、そこの現れている概念が、解決の糸口になることも多い。

今は、見えてないけど「なにかあるはずなんだよな」という発想を持ち続けることが、たいせつ。

配置


本番環境への配置(Deployment)作業は、Bounded Context 間の関係を考える、重要なフィードバック。
改良バージョンの配置を、他のチームとの調整なしにできるなら、かなり独立した Bounded Context ということ。

配置や配置前のテストに、他チームとの連携が必要になるほど、Bounded Context 間に強い依存性がある、ということ。

相手の作業待ちとか、連絡モレで、配置がトラブルことが多いなら、Context の Boundary(境界)を引きなおしたり、連携パターンを改良すべき、明確なサインですね。

チーム間の「コミュニケーション」の問題もあるだろうけど、「Bounded Context」の設計の問題、という視点も重要です。

トレードオフ


関連システムの統合の度合いと、それを実現する負担とには、明確なトレードオフがある。

Single Bounded Context


システム全体を、ひとつの単独モデルとして設計・実装する。
連携はいちばんスムース。
でも、モデル(とチーム規模)が大きくなると、コミュニケーションのロスが急激に増え始め、やがて崩壊する。
また、モデリングや設計能力も、高いスキルが必要になる。

Shared Kernel, Customer/Supplier, Open Host


連携のある程度、高度に実現できる。
コミュニケーションの問題と、スキルの問題は大きい。

Anticorruption Layer, Conformist, Separate Way


連携の達成レベルは低い。
でも、コミュニケーションの問題、スキルの問題も小さい。

選択の問題


どのパターンを選ぶかは、状況に応じた、選択の問題。
どれが正しく、どれが間違っている、ということはない。

Single Bounded Context があるべき姿、というような思い込みは、失敗の臭いがぷんぷんとただよう。

プロジェクトの途中から取り組む場合


プロジェクトの途中からでも、Context Map パターンは実践できる。
というか、思い立ったら、ぜひ、取り掛かるべき。

その場合のやり方のガイド:

・現実の Bounded Context と Context Map を絵にしてみる
・個々の Bounded Context 内の CI(継続的インテグレーション)の整備と実施
・すべての Bounded Context の名前を関係チームで共有(ユビキタス言語)

改善に取り組むために、この3つをきちんとやること。

今を描く


「現在」の絵を共有することが、まず、最初にやるべきこと。
「あるべき姿」や「計画時の姿」ではなく、「今」を描くこと。

CI の整備と実施


今後の、境界(Boundary)の調整や、連携の改善をするための足場を固める。
個々の Bounded Context の CI 、特に、自動テストを充実させ、熟成させることが、変更を行う、大前提。

Bounded Context の名前の共有


チームは、自分の Context 、それから別の Context に名前をつけている。
別のチームは、同じ Context を別の名前で呼んでいる。

この状態で、境界(Boundary)の調整や、連携パターンの改善の話しをしてはいけない。

まず、現状を元に、同じ言葉で、 Bounded Context と Context Map を話し合えるようにすることが、絶対に必要。

小さなリファクタリングの繰り返し


Bounded Context ごとのCIが安定し、みんな同じ絵を同じ言葉で語り合えるようになったら、改善活動を開始する。

たいせつなのは、「小さな単位」で、少しずつ変更することを繰り返すこと。

やり方は、クラスレベルでのリファクタリングのパターンは、ほぼそのまま使える。

・クラスの抽出
・メソッドの抽出
・名前の変更
・メソッドの移動
・フィールドの移動
...

最初は、それぞれの Bounded Context 内で、

・中核概念
・外部の連携に関係する概念

を少しずつわけていく。

まず、薄い、Anticorruption Layer を造ってしまって、外部との連携に関連したロジックやデータを少しずつ、そちらに分離して寄せていく。

こういう整理をしていくと、データやロジックの本来あるべき場所が、しだいに見えてくる。

この部分は、連携相手側に移動する。移動すると、連携インタフェースが簡単になる。

これは、クラス間の責務の移動と同じです。

債務の割当なおし


リファクタリング前:

...
value = order.getValue() ;
tax = this.tax( value ) ;
order.setValueWithTax( value + tax );
...

valueWithTax = order.getValueWithTax();
...

get して、加工して、set する、というパターン。
Order クラスが「データの入れ物」の場合の典型的なコード。

税金を計算する、tax() メソッドを order に移動すれば、

order.getValueWithTax();

だけになる。

Bounded Context 間でも、相手にデータやロジックを移動することで、インタフェースが単純にできるネタはどこにでもころがっている。

最初から細部まできれいに設計・実装できる技術者なんて、どこにもいない。

誰が設計・実装したコードでも、こういうリファクタリングのネタは、たくさん残っているものです。
こういう小さなリファクタリングを地道に繰り返していくと、あるところで、突然、ぱっと見通しがよくなる。

これは、クラス間の責務の割当直しでも、Bounded Context 間の責務の割当なおしでも、同じ話しですね。

「全体を見ろ」、とはいうけれど...

「6人の盲人が象を語る」という話しがある。

触った場所によって、それぞれが「へび」「壁」「木の幹」「綱」と主張する、という話し。

一般的には、全体が見えていない愚かさの例え話として使われている。

でも、エバンスは、Domain-Driven Design(DDD)のなかで、別のとりあげかたをしている。

いろいろなアプローチを説明している。

・4つの Bounded Context and 連携なし
・4つの Bounded Context and 最小限の連携
・ひとつの全体コンテキスト 荒削りバージョン
・ひとつの全体コンテキスト 改良バージョン

「何がゴールか?」によって、どのアプローチが良いかが決まる。

「ひとつの全体コンテキスト 改良版」が唯一の正解、他はまちがい、という考え方とは正反対。

どのアプローチも、目的や制約条件によっては、合理的な解である、というのがエバンスの考え方。
ここらへんが共感するんですよねえ。

4つのコンテキスト 連携なし


Separate Ways パターンですね。

それぞれの役にたっていて、それ以上の要求がないなら、「へび」「壁」「木の幹」「綱」としてモデリングと設計・実装すれば、必要十分。

個別の課題への最適解。そして「全体最適」のニーズがないなら、この個別最適が、もっとも良い。

開発やメンテナンスのコストを抑えられるから。

4つのコンテキスト 連携あり


簡単な、Anticorruption Layer を、それぞれに4つ用意して、連携する。

ただし、連携の関心事は「場所」「位置」だけに限定。

この場合も、モデルは、「へび」「壁」「木の幹」「綱」のまま。

ただし、お互いの場所や位置を表す情報を、相互に確認できるようにする、というモデル。

それぞれ、「場所」や「位置」の表現が違うので、それを、変換する機能は必要、ということ。

お互いの位置情報さえを知ることで、連携ニーズ満たせるなら、これで十分でしょ、という考え方。

ひとつの全体コンテキスト 荒削りバージョン


4つの Bounded Context とは別に「象」という概念の登場。

モデル変更の動機は、位置情報の交換だけでは、しっくりこない、連携ニーズがでてきたからでしょうね。

全体は、「象」に関連する「部分」という、 Whole-Part モデルを導入した。
(「象」の発見!)

この場合も、「部分」のモデルは、「へび」「壁」「木の幹」「綱」のまま。

大きな変化は、

・「木の幹」は、「象」を支えている。
・「へび」が「象」にくっついている。
・「綱」が「象」にくっついている。

・「象」は「壁」のサブクラス

という「関係性」が具体的にモデルに登場すること。

「位置」データと「場所」データの変換ではなく、「支えている」という役割(機能)が、表にでてきたこと。

これだけでも十分に役に立つシーンがあるよね、という話。

ひとつの全体コンテキスト 改良バージョン


前のモデルでも、うまく表現できないニーズがでてきた。

「象を歩かせたい」とかね。

ここで、はじめて、

・「木の幹」を(4本の)「足」
・「へび」を「鼻」
・「綱」を「しっぽ」

というモデルへ変化がおきる。

そして「壁」は「象」そのものに、吸収されて消滅。

たぶん、「移動」という概念ででてきたことで、「壁」として解決していた問題が、無意味になっちゃったんだ。

まだ、壁が必要だって? だったら、「象」とは、まったく別に、「壁」を作っておきましょう。

発見と進化


全体としては「象」を発見することで、しだいに進化していく、という話しになっている。

でも、初期の「4つのBounded Context 連携なし」が、「価値がない」とか「誤っている」という考え方ではない、ということ。

「正しい」「まちがい」ではなく、「役に立つ」「役に立たない」というだけのこと。

連携なしで「役に立っている」なら、それで必要十分。

「ひとつのコンテキストで洗練させる」のは、「ドメイン」のニーズで駆動力になる場合だけ。

ひとつの統一モデルを

・エレガント
・理想形
・あるべき姿

というようなドライブでやってはいけないよ、ということ。

6人の盲人と像


エバンスは、これを、「アンチパターン」とも言っていないし、「ひとつのコンテキストの洗練バージョン」を「パターン」だとも言っていない。

複数の Bounded Context を、どう扱うかは、目的や状況による、という、とても現実的なことを、淡々と説明している。

全体像が必要だ、ということは言っている。
Context Map パターンですね。

でも、その中で、"AS IS" だけを描け、ということを何度も強調している。

現実の全体像を、関係者で共有することはとても大事。役に立つ。

でも、"TO BE" の全体像を、関係者で議論という、パターンはないんです。

発展性は確保しておく


しかし、ビジネスが、価値連鎖であり、壮大な協働ネットワークである以上、アプリケーション間の連携ニーズは、無限です。

実際に、あちこちで、連携のための改良や開発が並行して行われる。

だから、全体像の "AS IS" をいつも、更新して、関係者で共有しておくことはとても大事。

「連携」の設計・実装では、ちょっとした工夫(配慮)をしておきたい。

・インタフェースと実装の分離
・Adapter
・Facade

などの実践を考慮しましょう。

薄い皮


こちらのモデルと相手のモデルを直接、連携できそうな場合でも、間に一枚「薄い皮」を入れておく。「ひと手間」かけておく。

Java のインタフェースの仕組み、Adpater パターン、Facade パターン、どれでも良いけど、とりあえず、相手との境界( Boundary ) には、シンプルなものを一枚はさんでおく。

入口(inbound) 側と 出口( outbound) 側の二箇所だから、「二枚」かな。

こうしておけば、お互いの内部での変更の副作用を最小限に抑えることができる。
境界部分(Boundary) 内の変換ロジックの実装の変更とかも、副作用の心配なしにできる。

個人的には、ドメインモデル内で、 Java の interface の仕組みは、それほど使う必要はないと思っている。(ドメインの概念の表現手段としては、魅力はない)

でもドメインモデル間の連携には、文字通り「インタフェース」なんで、Java のインタフェースの仕組みがぴったり来る。

ドメイン層と、UI層の間に、「薄い」アプリケーション層を設ける。
データアクセス層は、ドメインモデル側は、「Repository」を inteface で宣言して、「インフラ層」で実装する。

こういう縦のレイヤ構造と同じように、Bounded Context 間の連携でも、「薄いインタフェース」を用意しておけば、潜在的な連携ニーズの準備をしておける。

ソフトウェア全体としては、「潜在的なニーズ」を想定した、モデリングや設計・実装はさけるべきだと思っている。

でも、サブシステム間の連携というような全体アーキテクチャということでは、「インタフェース」という考え方を、きちんと実践しておくのが良いと思う。

こういう配慮をしておくことで、

・全体モデルをさらに深く理解して
・(連携の)設計・実装を改良していく

ことを、継続的に繰り返していくことができる。

Bounded Contex 内のリファクタリングと同じように、Context Map レベルも段階的なリファクタリング活動の対象。

そういう継続的なソフトウェア改良活動を、実践するには、それなりのアーキテクチャにしておくべき。

つまみぐい? : Published Language パターン

Open Host Service パターンの発展形として、「公開された言語」パターンがある。

XML で定義された標準データ交換フォーマットとか、Amazon API とか、楽天 APIとか、数え上げればきりがない。

Domain-Driven Design(DDD)では、化学分野向けの Chemical Markup Language (CML) が例としてでてくる。

私たちのドメイン、人材サービス分野では、HR-XML というのが公開されている。

使われている程度を別にすれば、あらゆる業界に XML 標準フォーマットなどの「公開された言語」が見つかるはず。

たとえば、OASIS という標準化推進の非営利団体のホームページだけでも、相当な種類の「公開された言語」が載っている。

HR-XML


求人情報とか、職務経歴書のデータを、電子データで交換したい、というニーズはたくさんあります。

私たちは、「転職支援サービス」や「紹介サービス」を提供する立場なので、求人企業とか、人材紹介会社とかと、求人に関わる電子データのやりとりのニーズはかなりある。

HR-XML だと、募集から始まって、応募、選考、選考結果通知、入社までの一連のプロセスで発生する情報を扱うための XML 定義が、一通り揃っている。

「転職」だけでなく、「派遣」とか、「勤怠管理」とかもカバーしている。

データ交換


実際のデータ交換は、自前のフォーマットで、変換プログラムを個別に書いてやっている。

HR-XML は、使っていない。使おうとも思わない。

自分たちが扱いたいモデルと一致していないんですよね。

第一の問題は、汎用すぎること。
私たちがまず使わないだろう項目が大量に定義されている。

いろいろなニーズをカバーしようとすると、仕様が肥大化しちゃう。
典型的な「汎用化」アンチパターンです。

米国と日本での人材分野の業務モデルが違う、という議論もある。
ただ、これ、国の違いより、企業の採用の考え方の違いのほうが大きいと思っている。

だから、米国中心に策定された標準だけど、日本で使うことは可能ではある。

問題は、汎用的・網羅的すぎて、扱いにくいことなんです。

モデリングの参考


モデリングの元ネタ、たたき台としては、参考にしています。
業務のプロセス、扱う情報の構造が、きちんと文書化され、XML 宣言も、XML サンプルデータも手に入るのは、ありがたい。

基本の構造設計はよくできていると思う。
自分たちの個別ニーズ用に、サブサブセットにして、使っている感じ。

英語名の参考


パッケージ名、クラス名、フィールド名、メソッド名。

コードの命名の約束は、基本は、英語にしているので、HR-XMLは、英語名のネタとして重宝している。
「福利厚生」とか「勤務形態」とかいっても、ぱっと、英語なんてでてこない。

そういうとき、HR-XML とか、英語の「人材採用の手引き」なんていう本とかを使っている。

つまみぐい


私たちは、HR-XML のおいしそうなところだけ、つまみ食いしているわけだ。

全体をじっくり調べて理解するわけでもなく、わかりやすいところを拾い読みして、手っ取り早く現場で使えそうな部分だけを、ちょこっと拝借する。

それでも、結構、役にたっている。

共通の参照情報として、メンバー間が同じ言葉を使うための道具の一つにはなっている。

「公開された共通言語」の使いどころ?


「公開された共通言語」もいろいろある。

最近は、「重量級」と「軽量級」に分けて考えている。

重量級


HR-XMLコンソーシアムとか、OASIS とか、標準化の推進団体が提供する「公開された言語」は汎用的で、網羅的で重い。

仕様全体を理解するのがたいへん。使うのも大変。

こういうのは、まあ、ちょっと見て、使えそうなところをつまみ食い、が良いと思っている。

モデリングの勉強のために、じっくり、研究するのは、ありだと思います。
開発が一段落したときに、HR-XML を 一週間ほど読み込みました。得るものはたくさんあった。

シンプルで軽量な公開API


最近、インターネット上は、ほんとうに、いろいろな公開APIがある。

しかも、意図が明確で、シンプルに設計され、実際に使われて有用性が実証されているものも多い。

エンタープライズアプリケーションで、そのまま使おうかな、と感じるものもの結構ある。

iCalendar なんかは「面接スケジュール」を扱うモデルとして、そのまま使えそう。

Bounded Context 内部のモデルとしてそのままいけそうだし、もちろん、外部とのスケジュールデータの交換に便利に使えるのは実証済み。

もっとも、RFC レベルで、すべての仕様(モデル)を本気で理解しようとすると、これは「重量級」の臭い。

自分たちのドメインの関心事というバイアスをかけて、スコープを絞ることは必要。

ただ、つまみ食いではなく、モデルを素直に、使うことができそうだと思っている。

使う価値はありそうだが...


「公開された言語」は、たくさんあります。
つまみ食い、参考モデル、本格採用まで、使い方はいろいろありそう。

でも、自分たちの今の課題に使えそうな候補をぱっと、リストアップするのはかなり大変。

Google でも、なかなかうまくヒットさせられない。
(検索キーワードが難しい)

見つかっても、内容を理解して、使い方を検討するのがまた、一苦労。
「標準化」「一般化」された説明は、わかりにくいと相場が決まっている。

ある程度、時間のある時に、一度は、自分たちに関連する分野を調べてみる価値はあると思う。
そういう地道なネタ仕込みが、ここぞというときに役にたつ(はず)。

そろそろやってみようかなあ : Open Host Service パターン

ひとつの会社で、いろいろなアプリケーションの面倒をみていると、共通化できたら、便利になるのになあ、というネタがいろいろでてくる。

・ユーザ認証(シングルサインオン)
・ユーザの連絡先(氏名、所属部署、メールアドレス、...)
・組織・役職・権限マスター

・顧客の連絡先
・商品マスター
・営業日カレンダー

・郵便番号マスター
・都道府県マスター
...

現実は...


アプリケーションごとに、個別に用意している。

× 同じものを何度も作る。
× 組織変更、入退社、異動。あちこちでマスターメンテ。
× 新商品の追加。関連システムでデータ連携時の不整合の発生。
...

まあ、総論で、共通サービス化というのは、簡単だけど、現実には、個別に作っちゃっている。

この状況は、Smart UI パターンと同じで、ある文脈では、合理的なやり方なんだと思う。

・全体の構造設計をおさえる?(インフォメーションアーキテクチャ)
・関係者全員でゴール意識の共有?
・開発チーム間の頻繁で積極的なコミュニケーション?
・中期的な計画の立案と、そを着実に実行する能力?
...

どれもこれも、現実には、かなりやっかいなテーマ。

結局、目先の顕在化した課題を、個別のアプリケーションで、個別の開発プロジェクトで、個別に解決しておくのが、コストパフォーマンスが良い、というわけだ。

でも、これだけ、不合理さが目立つのに、「まあそんなものさ」と放置はまずい。
まずいと思っているが、さて、どこから手をつけるか、という切り口も見つからず、はじめるタイミングもなく、ずるずると、放置を続けきたのが現状。

そろそろとりかかるか?


そうはいっても、さすがになんとかしようか、を考え始めた。

いくつか、きっかけが重なった。

・新年+来年度予算計画 ( この時期、毎年恒例の「ことしこそは」ごっこ)
・ドメイン駆動設計の成果の手ごたえ
・Mule ESB の再評価 ( Mule in Action 本 )
・Spring 3.0 のリリース
...

技術ネタ


直接のきっかけは、Mule ESB と Spring 3.0 かなあ。
RESTスタイルの サービス構築がだいぶ楽にできそうな気がしてきたこと。

ドメインモデルの XML 表現さえできちゃえば、あとはちゃちゃっと作れちゃう(?)。

ドメイン駆動設計の成果の手ごたえ


「ドメインの理解こそアプリケーション開発の基本」という発想の転換がだいぶできてきた感じがする。

開発メンバーの日々の会話や報告の中で「業務の言葉」は明らかに増えてきた。
量だけでなく、使い方、使い分け方が、だいぶ、いい感じになってきた。

RDRA + ICONIX で「モデル駆動設計」パターンの実践は、けっこういけてる。

RDRA も ICONIX も、「業務の言葉」を使った絵と文章がモデルの中心。
あと、初期の概念モデルが、ドメインモデルとして発展して、最後は、そのままドメイン層のクラスとして実装、という流れ。

モデルやコードに「業務の言葉」や「業務の概念」が、だいぶはっきりでてくるようになった。

新年の抱負、次年度の目標設定


まあ、毎年恒例だったけど、今年は、いままでとちょっと違った感じ。(ほんとか?)

準備やトライアルフェーズが終わって、いよいよ結果をだすところにきた。
(結果をださないと、このご時勢、こちらの首があぶない?。これが一番の強烈な動機か?)

手のつけ方


Open Host Service パターンの実践ネタは、最初に書いたようにいろいろある。
でも、どれも、アプリケーション内部にがっちり組み込まれていて、なかなか手がつけにくい。

新規の小規模のアプリケーション開発と小さなアプリケーション間連携の依頼を、いくつか抱えているので、まず、そこで、いくつかトライアルをしていこうと思っている。

たとえば、オーダーエントリーの入り口追加の案件。

ここを、

・Web アプリケーション
・REST スタイル( XML over HTTP )のインタフェース
・バックエンドサービス (ドメインモデル+DBアクセス)

という分散方式で、実現してみようかと。

ドメインモデルの設計・実装、テーブルの設計・実装は今までどおり。
それに追加して、ドメインモデルを XML データとして扱う設計・実装をやるわけだ。

インフラ層の実装は、既存のフレームワークでなんとかなっちゃいそうなので、ポイントは、「XMLデータ」ですね。

ここで、ドメインモデルの XML 表現 + REST スタイル ( POST/GET/DELETE/PUT ) がうまく動けば、そのインタフェースを、他のアプリケーションからも利用できるようになる。

まさに、Open Host Service パターンです。

今回は、通常のオーダーエントリーより、入力項目をミニマムに絞るという案件なので、このトライアルには、都合がよさそう。

小さくはじめて、だんだん、その内容を発展させたり、横展開していくことをもくろんで(妄想して)いる。

転ばぬ先の杖


Domain-Driven Design(DDD)で、エヴァンスが書いているように、Open Host Service パターンは、いろいろ落とし穴がある。

複数のシステム相手の共通サービス


連携の相手は、複数のサブシステム。

単純に考えると、 Anticrruption Layer を個別に開発するより、共通の Open Host Service を作るほうが、合理的。

でも、連携の相手が複数あるということは、利害関係者が多い。
共通サービスに、「総論」は賛成だけど「各論」でもめる、というのが世の常。

共通仕様を取りまとめること自体、かなりたいへん。

Conformist パターン?


共通仕様は、結局、誰にも不満足で、みんな、しょうがないので、それにあわせる、という 典型的な Conformist パターンになりがち。

仕様の肥大化


あるいは、それぞれの連携先のシステムの要求をなんでもかんでも、取りこんだ仕様にする、というのもありがちなパターン。

「拡張項目」とか「オプション項目」だらけの、設計・実装になっていく。

仕様の肥大化は、開発コストがかさみ、開発期間もずるずると延びる、失敗プロジェクトパターン。

発展の足かせ(硬直化)


共通サービスに依存するようになると、共通サービスの提供側も、利用する連携相手も、「共通サービス」が、ソフトウエア変更の足かせになってくる。

共通サービス側は、全部の連携先への影響を考えると、おいそれと、仕様の変更・拡張ができない。

連携相手のシステムも、新しい要望がでてきても、既存の「共通サービス」利用部分をいじることができず、柔軟性がなくなる。

小さくはじめる


エヴァンスが言っているように、Open Host Service パターンは、

・ニーズが明確
・ニーズを単純化

できるテーマだけに限定することが成功のポイント。

「共通化」ありきではなく「ここぞ」というポイントを見つけて、そこに集中して小さく始める。

これ、現実には、むずかしいんですよね。

焦点が絞れないもんだから、スコープを広げて、あちこち手を出して、時間をかけた割には、結局失敗、という失敗をなんども見てきた。

「ここぞ」というポイントに絞り込んで突破する、というのは、究極の良い設計なんであって、それが簡単にできれば、苦労はしない。

でも、「焦点を絞る」という姿勢を保つことが大切。

「スコープ広げて焦点をぼやかす」やり方を何回繰り返しても、同じ失敗を繰り返すだけ。

「焦点を絞る」ことにチャレンジすると、「はずした」ことも明確になる。

それは「良い結果」だという発想を持つことが大切。

「ここは、はずれ」が明確なら、別のところに「焦点」を当てなおせばよいこと。

小さく開発して、すぐにフィードバックを得るというアジャイルなやり方の、基本の発想ですね。

Open Host Service の開発は、利害関係者も多く、問題領域も広がっているだけに、こういう「焦点をしぼって」やってみることの効果が大きい。

撤退や方針変更になっても、失うものも小さくてすむ。(リカバリしやすい)

小さくはじめたサービスが成功しても、それを拡張してバージョンアップ、はしないほうが良い。

小さくて気の利いたサービスのままにしておくほうが扱いやすい。

一つのサービスを膨らませていくより、小さくて気の利いたサービスを、少しずつ増やして揃えていくのがよいやり方だと思う。

リファクタリング


既存の重複したサービスの中から、「共通サービス」を発掘していく、というアプローチもある。

それも、ちょっとずつ、ちょっとずつ、段階的に、共通サービス化していく。

関連する、すべてのサブシステムを横断的にリファクタリングしていくというのは、かなりたいへんな仕事ではある。

でも「白紙」に「あるべき姿」を描くよりは、「現実の動くコード」を調べながら、ちょっとずつ、共通部分を抽出・整理していくほうが、確実に前進できる方法。

スケールは違っても基本はいっしょ


Open Host Service パターン、複数のサブシステム間の連携、というと、話しはおおごとに聞こえる。

でも、小さて気の利いた Value Object の設計と、必要なスキルも、使うテクニックもいっしょだと思うんですよね。

オブジェクトの関連(依存性)を最小にしようという設計の考え方は、Value Object でも、サブシステム間連携でも、いっしょ。オブジェクトの役割を単純に明確に保つ、という考え方も同じ。

意図の明確なインタフェース、概念の輪郭、副作用の無い機能、そして、リファクタリング。
DDD の Part3 にでてくる、考えた・やり方は、Part4 の大きな設計にも、そのまま使える。

気の利いた Value Object が設計できる技術者なら、気の利いた Open Host Service が設計できるはず。

設計は設計。基本はいっしょ。

Anticorruption Layer を開発する

Anticorruption Layer は、開発がたいへん。
相手があるので、調整ごと含めてテストが、かなりたいへん。

Anticorrution Layer は、異なる Bounded Context 間の連携パターンとして、効果は大きい。
でも、コストがかかるのが、大きな問題。

ここ、数年、私がかかわってきたシステムで、やってきたこと。

Smart Converter の個別開発


ようするに、連携ニーズのたびに、変換用のスクリプトをごりごり書いていた、ということ。

似たような連携ニーズがあっても、個別に、ごりごり。

入力ファイル(テーブル)の項目定義と、出力ファイル(テーブル)の項目定義を見比べながら、変換ロジックをごりごり、ごりごり、ごりごり、...。

ソフトウェア価値の急速な劣化


入出力、ロギング、文字コード変換とかのコードの中に、暗黙的に「ビジネスロジック」や「モデル」の「変換ロジック」が埋め込まれている。

2年前に自分で作った、 Smart Converter が、いまでは、完全に「レガシー」。
ちょっとこわくて手をつけれない。

if( type == 1 && flag != valid )

なんていうコード、意味がわからん。意味不明なものを、変更できるわけもなく、...。

個別に作るたびに、コストがかかり、時間とともに、「財産」よりも「負債」という臭いが強くなってくる。

さすがに、これは、「もうやめたい」と思った。

EAI ツール


1年ほど前から、Asteria という商用の EAI ツールを導入していろいろやってみた。

あるプロジェクトで、「連携」ニーズがめちゃくちゃあるのに、人手がぜんぜんたりなかったので、ツール導入で解決することにチャレンジ。

Asteria は、前からちょっと注目していたツール。
XML と Java で、日本の技術者が作った、という素性が気に入って、選んだ。

コストダウンは、大成功


結果は、コストダウンは、大成功。

商品登録、商品更新、商品問い合わせ、問い合わせ回答、利用者登録、...を、2つのシステム間で連携する機能を、2ヶ月弱で、作り上げて本番運用しちゃった。

ちょっと、感動的なスピードでした。
たいへんだったのは、中国側のわがまま(?)に振り回されたことだけ。
こっち側の開発自体は、びっくりするくらい、簡単になった。

ある程度、ネットワークのことがわかっていて、Excel なんかは器用に使える、という感じのメンバーで、それなりに複雑な「連携」機能を開発して運用できちゃった。

・データベースを監視して、新規レコードを、XML出力
・XML 出力を監視して、HTTP で連携先に送信
・XML を HTTP で受信して、データベースに書き込み
・受信したことを、関係者に電子メールで通知
...

・ファイルサーバーを監視
・新しい画像が追加されたら、形式やサイズをチェック
・OKなら、FTP サーバーにアップして、公開
...

こんな感じの処理を、最初の雛形は、それなりの技術者に開発させた。
if/for など制御は、まったく書く必要がない。(書けない)

HTTP や JDBC の接続パラメータや、XML の要素と、DB のカラムのマッピングルールを、GUIで設定してやるだけ。

ちゃんと動くやつがひとつできれば、それを参考に、類似の処理を、ジュニアのメンバーにやらせたけど、Copy & Edit で、なんとかなっちゃう。

XML ファイルと要素名と データベースのテーブル名とカラム名を、書きかえる程度で、同じことができちゃうから。

開発用の、ブラウザベースのUIもリッチで、なかなか、しっかりしている。

運用の監視、障害の通知設定、障害発生時のリトライとかクリーンアップとかもGUI ベースで、これもジュニアなメンバーでなんとか回している。

でも、...


使っているうちに、限界というか、課題もはっきりしてきた。

初期の開発コストは、激減できた。
でも、この方向で、発展させていこうとすると、問題が山積み。

ライセンス費用と開発スタイル


CPUライセンス式の商用プロダクトなので、テスト環境とか開発環境とか増やすには、別ラインセンスが必要。

オープンソースの開発になれちゃっていて、すっかり忘れていたけど、テスト環境やローカルなビルド環境を、いつでも、どこにでもセットアップ、というわけにはいかない。

システム構成も、分散化とか、冗長化したいと思っても、そんな予算はとてもとれそうにない。
かりに予算があっても、正式の見積もり依頼・承認申請・承認・発注・納品までに、冗談みたいな時間がかかる。

Asteria の内容と値段を考えれば、割高だとは思いません。

しかし、オープンソースでやる時のスピード感、機動力、発展性を実現するには、ライセンス費用と「購入プロセス」が、めちゃくちゃネックになる。

オープンソースのありがたさ、それを使った機動力のある、スピード感のある開発スタイル、発展性のすごさを、再認識。

アンチパターン


ツールを使った、開発・運用を、ジュニアのメンバーにやるせるには、よい選択肢だと思う。

モデリングや設計スキルがないので、Asteria が提供する機能を、Asteria の設計思想とマニュアルどおりに使う。
そういう使い方をする限り、よくできている EAI ツールだと思います。

インフラ層の技術的な接続設定と、データ型変換という視点だけで、やっちゃっているのが問題。

使う範囲が広がってくると、似たような機能が、あちこちの設定で繰り返している。
どこで、何をやっているか、見通しが極端に悪い。
実装している機能が少ないうちは、なんとかなった。
でも、数が増えてくると、誰にもわからない範囲が急速に拡大しはじめる。

もちろん、あちこちで、似たような重複がはじまる。
あっちでやっていることと、こっちでやることは、本来一致すべきなんだけど、ばらばらのまま、増殖が進む。

しだいに「レガシー」の悪臭が強くなってきている。

なんてことはない、Smart UI アンチパターン を「アプリケーション間連携」でもやっちゃっていたんだ。
設計しないで、素人さんでもなんとかなっちゃう Smart UI は、ここでも、有効(?)だったわけだ。

「接続インタフェース」ごとに担当を分け、すべての設定を、そのインターフェース単位で開発。
重複があってもお構いなし。

問題あっても、特定の「接続機能」だけ作り直せばよい。副作用の心配はない。

ツールを導入したことで、Smart UI アンチパターンを、増殖させてしまった。

さすがに、これは「まずい」と猛反省。

Mule で、EIP


というわけで、

・ドメイン層とインフラ層の分離
・ドメインの知識を POJO で実装

というドメイン駆動設計流のアプリケーション連携を実現しよう、という方向に真剣に取り組み始めたところ。

実現手段は、Mule ESB

これを使えば、いろいろな連携課題を、低コストで実現できそう。

軽量でシンプル


設計思想が、「軽量」指向だし、技術方式として、Spring フレームワークベースなのも、なじみがあるので、やりやすい。

なによりも、ドメイン層の関心事と、インフラ層の関心事を分離している設計思想がすてき。
ドメイン駆動設計と相性がよさそうです。

ビジネス層の関心事を他の関心事から分離して、UMO ( Universal Message Object ) として扱うアーキテクチャが気に入っている。

もちろん、UMO は POJO。

アプリケーション間の連携方法として

・HTTP, FTP, SMTP, JMS, ...
・データベースアクセス、ファイルアクセス

などが、一通りそろっていて、

・トランスフォーマ
・ルータ

の部品も、EIP ( Enterprise Integration Patterns ) のパターンを実現しやすいように、いろいろ提供されている。

これらの部品の構成定義は、XML で、「宣言」的に記述。

小さな軽量のサービスを作りながら、現場で少しずつ使いはじめているところ。

小さくはじめてみる


ESB とか、サービスインベントリ とか、という大きな絵は将来はあるかもしれないけど、まずは、個別のニーズに、シンプルにこたえるところからはじめている。

ちょっとした処理にも、軽く使えるツールなのが、気に入っている。

昔、力仕事で書いことが、既存の部品で、XML の定義ファイルだけで、さくさく実現できちゃうのは、正直びっくりです。

書こうと思っても、忙しくて手がまわらかったような制御ロジックとか、タイムアウト、リトライ、例外処理など、安定運用のための仕組みも、十分に配慮されている。

証券業界のトランザクション処理というヘビーな環境で、生まれて、鍛えられてきたツールという素性がよくつたわってくる。

でも、基本のコンセプトは単純だし、アーキテクチャもシンプルでわかりやすい。
小さくはじめられて、いくらでも、発展させていけそうな感じ。

しっかりと発展させる


もっとも、手軽に始められるからといって、「軽い」使い方で、やみくもに、埋めよ増やせよをはじめると、とんでもないことになりそう。

Mule が担当するインフラ層とドメイン層とのアクセス部分の設計・実装も、ちゃんと考えたい。

今日は、ほんとうはお休みだけど、

・Mule の使い方の勘所
・使い方のガイドライン
・セキュリティ、性能、運用がらみのニーズと実装

を、少しまとめてみようかな、と思って、事務所にでてきた。

ここ数年は、休日は「完全オフ」と決めていたんだけど、それを破ってまで、Mule に、のめりこみはじめている。

アーキテクチャもドメインモデリングも


Oracle 時代に、データベースの設計・実装をしゃぶりつくそうと、のめりこんだ時に似てきたかなあ。
この時も、Oracle 内部のアーキテクチャにどっぷりつかりながら、データモデリングにものめりこんだ。

Mule も、アーキテクチャ面、セキュリティ・性能やスケーラビリティ・並行処理と排他制御・高可用性・拡張性など、興味深いし、なかなか奥が深そう。

同時に、Mule の上に構築する「メッセージベースのアプリケーション連携」のドメインモデルのモデリングと設計・実装に取り組みたいと思う。

エンタープライズアプリケーションのインフラ層は、

・永続化
・メッセージング

が2本の柱。

データベースは、アーキテクチャも、モデリングも、まあ、ある程度のことはできる(つもり)。

メッセージングは、素人。(経験も知識もなさすぎ)

ここしばらくは、Mule を現場で使いながら、メッセージングのアーキテクチャとドメインモデリングの知識とノウハウをためていきたいと思う。

「ドメイン駆動設計」の Context Map を、Mule で実践していってみよう。

Anticorruption Layer パターン : 己を知り、敵を知れば...

システム開発の相談が飛び込んできた。

20 の Bounded Context があって、いろいろからみあっていて、さらに外部システムとも接続する。
いきなり「アプリケーション連携」のヘビーな実践テーマが飛び出しきちゃったなあ。

開発済みの範囲は、半分以下。既存システムは、3つ(?)。
10くらいの Bounded Context を無理矢理、3つのシステムに割り振って、開発した感じ。

そもそも、10の Bounded Context ではなく、3つの Bounded Context に見えているらしい。
ここらへんの認識あわせが、骨かもしれないなあ。

いつものことだけど、「時間も予算もない」、だけど、「なんとかして」コール。

大きな絵としては、それぞれの Bounded Context に分けてアプリケーションを開発し、それを連携する基盤の整備をすれば、なんとかなりそう、という方向で話しをするつもり。

UI 層、ドメイン層、インフラ層の切り分けを明確にするアーキテクチャの改良活動も必要だな。

いちおう絵は描いたけど...

システム間接続


エンタープライズアプリケーション開発やっていれば、アプリケーション連携やシステム間接続は、必然的な開発テーマ。

新規開発の基本要件には、だいたい「既存システムとの連携」が入ってくる。

既存システム相手では、 Shared Kernel パターンや Customer/Supplier Development Teams パターンは使えない。

かといって、 Conformist パターンで、既存のシステムにあわせると、モデルが違いすぎて、新システムの本来の目的を、とても達成できそうもない。

こういう時の選択肢が「Anticorruption Layer パターン」。

ある Bounded Context と 別の Bounded Context の間に、「汚染」を防止する「変換レイヤ」を用意するわけだ。

よくある開発テーマ。

多くの場合、

・通信方式
・データ型
・文字コード

などのレベルで仕様を決めて、接続とデータ変換やれば、いっちょうあがり、というやり方が多い。

これだけでも「接続」の効果は大きい。

モデルの変換


Domain-Driven Design (DDD) では、そういう下位の通信レイヤの変換だけでなく、「ドメインモデル」の違いとその変換に注目する。
他の層から分離した「ドメイン層」が、いちばんの関心事。
"Isolationg the Domain" 。なんどもでてくる DDD の基本中の基本ですね。

別のアプリケーションになっているのは、目的も背景もこだわりポイントが違うから。
それぞれが別の Bounded Context であり、ドメインのモデルもまるで別のもの。
似ているように見えても「きっと違っているはず」という姿勢が基本。

「モデル」の異なる Bounded Context 間で、連携するには、「モデル」と「モデル」の「変換」こそ、もっとも重要な関心事だし、ソフトウェアの価値を大きく左右するポイント。

「モデルの変換」をうまくモデリングして設計・実装すれば、役に立つソフトウェアになり、コードが扱いやすくなる。将来のそれぞれのアプリケーションの変更にも柔軟に、簡単に対応しやすくなる。

「モデルの変換」の設計が怪しいと、開発中のソフトウエアは、既存システムのモデルに「汚染」され、本来の目的に、あんまり役に立たず、ぎこちなく、扱いにくいコードになっていく。

なぜ "Anticorruption" ?


もし、それぞれのドメイン(問題領域)を深く理解し、うまく設計された Bounded Context 同士の連携であれば、2つのモデルの変換も、意図が明確で、わかりやすい設計・実装になる。

現実には、既存アプリケーションのモデルと設計・実装は、いろいろ理由で歪んでいたり、不完全なことがあたりまえ。

理想的な、よくできたモデル間の連携なら "Translation" という素直な名前で良い。

エバンスが、「Anticorrution(汚染防止)」という、ネガティブな名前をつけたのは、現実のアプリケーション間連携は、「きれいごと」ではすまないことが身にしみているからでしょう。
多かれ少なかれ、連携相手からの「汚染」を防ぐという姿勢にならざるをえない。

まったく、その通り。

新しく開発するドメインで、よいモデルを発見できそうでも、既存システムの連携がでてくると、いろりろ歪み始める。

相手システムやその開発者たちに呪いの言葉を吐いても、なにも解決しない。
それよりも、自分たちでよい仕事をして解決しようね、というのが Anticorruption Layer パターンの考え方かなあ。

そのほうが、確かに健全ではある。

やり方


他の連携パターンに比べ、このパターンは、Building Block や、やり方が、具体的に書いてある。

現場で出くわす機会の多い課題だし、実践経験も豊富だから、パターン化できてきているんでしょうね。

都合の良いインタフェースを考えちゃう


手のつけどころは、簡単。

自分たちが取り組んでいる Bounded Context のためのドメインモデルに、いちばん都合の良いインタフェースをまず、設計しちゃう。

おそらく Service クラスを設計して、都合の良いタイミングで、都合の良いオブジェクトを入手したり、送り出したりできるようにする。

この段階では、相手の都合は一切無視。

自分たちのモデルをシンプルに、純粋に、意図を明確に保つことに、徹底的にこだわって設計すればよい。

相手のインタフェースの整理


次のステップは、相手側のインタフェース。

相手が提供する既存のインタフェース仕様、それからその背後にある「相手のモデル」を、調べて、検討してみる。

既存システムは、いろいろな機能やデータ項目が、からみあって、外部インタフェースも、かなり複雑になっていることも多い。

そこを整理して、相手のインタフェースの中で、自分たちの関心事だけに簡略化した、Facade クラスを設計しちゃう。

Facade は、こっちのモデルの一部ではなく、連携相手のモデルの一部。
既存のインタフェースは、いろいろな事情で複雑に膨れ上がっていても、

・必要な情報
・必要なタイミング

など、こうなっているとわかりやすいな、という視点で「相手側のインタフェース」に Facade パターンを適用しちゃう。

内部の複雑さを隠蔽して、こっちにわかりやすく単純化したインタフェースを用意する。

Service と Facade ができたら、中を作る


こちら側の出入り口に、便利な Service クラスを用意。
あちら側の出入り口に、わかりやすい、Facade クラスを用意。

ここまでくれば、Anticorruption Layer の設計・実装は、わりと素直にできる。
「イン」と「アウト」が明確に定義されていれば、話は簡単でしょう。

ただし、「関心事」の分離が鉄則。

・モデル層以外の「低レベル」の変換は、インフラ層に実装して「隠蔽」すること
・複数の変換テーマがある時は、変換テーマごとに別々のクラスを用意すること
 (GenericTranslator とか、Universal Adapter とか、ConvertUtil とか、設計しないこと)

使うパターンは、基本は二つ。

・Adapter パターン
・Translator パターン

Adapter パターンは、文字通りに「アダプタ」です。変換するわけではなく、形状の違い等の吸収。

論理的に変換するニーズは、別途 Translator クラスを設計する。
「変換」のニーズごとに、別々の 小型で、意図が明確な Translator クラスを作っていく。

小さく分けておくことで、要求の変化への対応が単純になり、それぞれのクラスを再利用できる可能性も広がる。

登場人物のまとめ


Anticorruption Layer の登場人物(オブジェクト)をまとめておくと、こちらの Bounded Context 側に近い順に、

・連携サービスのインタフェース ( Service クラス )
・Adapter ( こちらの Service と あっちの Facade をつなぐ )
・Translater ( Adapter から呼ばれる変換機能 )
・Facade ( 相手のインタフェースの理想(?)形)

双方向に連携する場合、同じ Adapter/Translator ではうまくいかないことが多い。
方向が違えば、モデルの変換ロジックは別ものになることが多い。

一般に、通信を扱うときに「上り」と「下り」は、別のクラスの責任にしておくのが良い。

オブジェクトの「配置」


Anticorruption Layer を、ひとかたまりのモジュールとして、一箇所に配置するとは限らない。
こちらと、連携相手のシステムとの間で、配置パターンには選択肢がある。

A. Facade まで含めて、こちらの環境に配置。( Facade クラスが相手と「通信」する )
B. Facade は、連携先に配置。(通信は、Adapter と Facade 間 )
C. Service まで、連携先に配置。(通信は、Service のリモート呼び出し)

どれを選択するかは、性能、コスト、システム管理ポリシー、....など、さざまなな要素を考える必要がある。

あるいは、開発時とか、移行期間中とかもかかわってくる。

いずれにしても重要なのは、

・モデル層とインフラ層の分離
・モデルとモデルの変換

をきちんと、モデリングし、設計・実装すること。

たいせつなのは、良い変換は、自分たちの Bounded Context の理解だけではダメ。

英語と日本語を翻訳するときに、両方の言葉(その背景の概念)に精通しているほど、良い翻訳ができる。

Bounded Context 間の連携も、こちらの「ユビキタス言語」と相手の「ユビキタス言語」の両方を理解して、バイリンガルになることで、仕事が簡単になるし、よい結果をだせる。

こちらの「ユビキタス言語」の理解がしっかりしていれば、相手の「ユビキタス言語」の理解も楽になる。
まずは、自分たちの「ユビキタス言語」を確立することからはじめる。

Conformist パターン : めをつぶってアリモノを使う

「アリモノ」をうまく使えば、ソフトウェア開発は、ずいぶんと楽になる。

私たちも、Web MVC, 永続化、メッセージングなど、UI 層や、インフラ層は、オープンソースで手に入る「アリモノ」を活用している。

HTTP サーバー、メールサーバー、DBサーバー、アプリケーションサーバー、メッセージングサーバー、...、どれも、「アリモノ」を使わせてもらっている。

ドメイン層でも、「アリモノ」を使う、という選択肢は、当然ある。

上流のアプリケーションから下流のアプリケーションに情報の流れが単方向の場合、上流側の開発チームが造ったソフトウェアを、そのまま、下流でも使ってしまう、というのがConformist パターン。

When to use it : 共同作業が難しい場合


Shared Kernel パターンや、Customer/Supplier Development Teams パターンは、二つのチームが協働(コラボレート)して、お互いの Context の間の Boundary (境界)を、モデリングして、設計・実装するパターン。

共通のゴール意識(WIN-WIN)、コミュニケーションが円滑で、モデルや設計・実装の基本の考え方・やり方を共有しているのが前提。

現実には、こういう状況にないケースがたくさんある。

そういう、2つのチーム間の関係が、うまくない状況の時に検討するのが「Conformist パターン」。

細かいところには目をつぶって、上流チームが作ったソフトウェアを、そのまま使わせてもらおう、というやり方。

Shared Kenel パターンでは、二つのチームの関係は対等。

Customer/Supplier Development Teams パターンでは、二つのチームは、「顧客」役と「サプライヤ」役を演じる。(協働するけど、役割や非対称)

Conformist パターンは、「協働」しない、ということ。

上流チームが作るソフトウェアを、そのまま使って、すませる。

そのまま使って、役に立つなら、これは、有力な選択肢だと思います。
実際、現場では、多いパターンかもしれない。

メリット


相手に合わせるわけだから、余計なコミュニケーションや調整ごとは、激減しますね。

もちろん、「アリモノ」使うわけだから、モデリングと設計・実装の時間をまるごと節約できる。

上流チームのモデルと下流チームのモデルは、ばらばらに開発していても、それなりに近いモデルになっているはず。特に、お互いの Bounded Context の間にある Boundary (境界)部分のモデルは大きくずれていないはず。

これは、偶然ではなく、問題領域(ドメイン)の業務が「連動」しているなら、そこを丁寧にモデリングしていけば、「連動」のためのモデルは、上流から見ても、下流から見ても、同じになってくる。(はず)

まあ、現実には、いろいろ言いたいことも出てくるだろうけど、それなりにできている「アリモノ」を使うことのメリットは大きい。

デメリット


上流のBounded Context 担当チームには、基本的にデメリットはないですね。

下流に位置する Bounded Context の開発チームにとっては、いろいろな懸念点があります。

依存性


他のチームの開発しているソフトウェアに完全に依存することになる。
そのソフトウェアが変更された場合、もろに影響をうける。

オープンソースのソフトウェアを組み合わせて使っていれば、誰でも知っている「依存性」の問題ですね。

オープンソースの場合、後方互換性への配慮( or 非互換宣言 )、依存性の管理のやり方の工夫(ツールとリポジトリの整備)など、一昔に比べれば、ずいぶん、問題は軽減してきた。

でもアプリケーション開発の現場では、他チームの作っているソフトウェアに依存するのは、かなりかなりやっかいな問題。

Conformist パターンの場合、上流側のチームは、下流にお構いなく、ソフトウェアを変更する可能性が高いので、下流側のチームは、その影響をもろに受ける。

事前に通知とかの約束ごとを申し入れることはできるかもしれないが、「Conformist パターン」を選択する状況というのは、基本的に、先方がそういう配慮をしてくれない、というのが前提なんですよね。

10年くらい前、7-11 さん相手のシステム連携で、むこうの開発チームが NRI さんだったとき、まあ、好き勝手を言われて、やられて、えらいめに会ったなあ。

モデルの不整合、汚染


自分たちの開発しているアプリケーションの価値を最大化するために、ドメインの知識を深く掘り下げながら、初期のぎこちないバージョンを、ほんとうに役にたつソフトウェアに発展させていく。

こういうドメイン駆動設計の考え方をやろうとしたときに、その一部に他のモデルが入り込んでくることは、あきらかなデメリットですね。

アプリケーションは「入力」に大きく依存する。

「ガベッジ・イン ガベッジ・アウト(ゴミを入れたら、ゴミがでてくる)」という言葉があるように、入力情報が、アプリケーションの価値を決めてしまうことが多い。

そこそこ使える、と判断したからこそ、Conformist パターンを採用したはず。
でも、インプットになる情報のオブジェクトが、よそのチームのモデルのままだと、

・できること
・発展の方向
・変更できる範囲

などに、いろいろな制約がでてくることが多い。

良い仕事をしようと思った時に、Conformist パターンは、直感的には、「避けたい」という心理が働く。

エバンスも「Conformist パターンを使ったほうが良い状況が多いはず」だけど、現実には「あまり、選択されないパターン」だと書いていますね。

エレガントな選択肢でないことは確かです。

既存のモデルに無意識にひっぱられる


Conformist パターンについて、私が、現場で感じている、いちばんの弊害がこれ。

既存の動いているソフトウェアに、技術者はとても弱い。

「動いている=正しいモデル」と、思い込んじゃう傾向がある。

「動いていない=正しくない」は、もちろん、真実です。だからこそ、日々、バグと戦っているわけだ。

でも「動いている=正しい」では、ない。

ましてや、Conformist パターンは「人様の、よその領域」のモデルを使っているんです。

動いていても、「自分たちのモデルとして正しくない」のは、最初から明白。

モデルが違うからこそ、「 Bounded Context 」ごとに、別の開発チームで、モデリングと設計・実装をしている。

他人のモデルは、そちらの Bounded Context で有効なモデルでも、自分たちの Context には、ぴったりくるはずがない。

「正しい」モデルという表現は、まずいかな?

それぞれの Context ごとに「役に立つ」モデルは異なる。

ある Context で役に立つモデルは、別の Context では、役に立たない、ということです。

「別の Context のモデル」ということを十分に意識して参考にするなら、それは良いことです。

でも「既存のモデルにひきずられて」、自分たちが取り組むべき Bounded Context の目的・背景の理解、ドメインの知識の理解が歪むリスクには、ほんとうに注意すべき。

Conformist パターンのいちばんのリスクは、この「本来のモデル」を追いかけるという方向や、そのための努力が、汚染されっていってしまうこと。

ひとつの Bounded Context をまかされたからには、その Context で、最善のモデル、設計・実装を追及すべきなんです。

防止策・改善策


Conformist パターンを、選択した瞬間に、「特別警戒警報」発令です。

この時点で、「他のモデルに依存し、影響をうける」ことを、まず、チーム全員で再確認。
「違いがある点」を十分に理解した上で「目をつぶって」使うこと。

あとは、一日一回、「モデルの汚染度」チェックを、夕方ミーティングとかに組み込む。

一言で、いいです。「あっちのモデルに汚染されている箇所は?」という質問を発することを約束事にする。

もちろん、形式的ではなく、本気でチェックすること。

モデルを描くとき、外部のモデルを使っている箇所は、当然、色分けしておくべき。

ドメインの専門家たちとの会話で、言葉のギャップを発見したら「よそのモデル」の影響の可能性をチェックする。

ドメインの専門家に手伝ってもらいながら、シナリオテストを作成して、それを、継続的インテグレーションの自動テストに組み込むのも有効そうですね。
(Conformist パターンでなくても、やるべきことですけど)

Conformist パターンは「ソフトウェアの流用」なので、汚染の危険があるのは、開発者です。

ユビキタス言語


ドメインの専門家は、影響は受けない。

Conformist パターンの悪影響の予防、改善には、やはり、ドメインの専門家との対話を頻繁に行い、かれらの言葉、その使い方に注意深く耳をかたむけることでしょう。

そして、自分たちの言葉の選び方、使い方に、ドメインの専門家が違和感を感じていないか、敏感になること。

結局、「ユビキタス言語」パターンを、丁寧に、地道に続けることが、Conformist パターンの導入による「モデルのゆがみ」の、防止策・改善策の基本ですね。

それなりに使えるモデル、ソフトウェアを、上流チームが提供してくれるなら、Conformist パターンは、検討の価値がありそうです。「それなり」という判断が、めちゃくちゃ難しそうですけど...

RDRA の神崎さんのブログ 「神崎コンサルノート」で

RDRA ( リレーションシップ駆動要件分析 ) を提唱され、要件定義マニュアル の著者である、神崎さんのブログに、この日記のこと、紹介していただきました。

光栄です。

私たちは、要求分析の段階を、RDRA 、ユースケース記述以降の設計・実装を ICONIX (ユースケース駆動開発 ) で、モデリングや設計・実装をやっています。

両方とも、表現方法は、UML ベース。
どちらのやり方も「厳密な UML 表記ルール」には、こだわっていなくて、もっと実践的です。

ドメイン駆動設計(DDD)の「モデル駆動設計(MDD:Model-Driven Design)」パターンを、具体的にやるために、RDRA + ICONIX は、実践的で良いやり方だと思っています。

それぞの解説本は、具体例が多く、現場で陥りがちな落とし穴のチェックポイント、改善方法のアドバイスが、いろいろ参考になります。

DDD の視点では、RDRA の「概念モデル」、ICONIX の「ドメインモデル」を、オリジナルのやり方に比べ、特に、力を入れてやっています。

RDRA の最初から「概念モデル」を描き始め、それを、ICONIX の「ドメインモデル」につなげ、そのまま、「実装クラス」に落としていく、というやり方です。

まだまだ試行錯誤中ですが、RDRA を導入してから、「初期のドメインモデル」をどうやって開発するかの、考え方とやり方が、なんとなくわかってきた、という手ごたえを感じています。

calendar
     12
3456789
10111213141516
17181920212223
24252627282930
31      
<< January 2010 >>
システム設計日記を検索
プロフィール
リンク
システム開発日記(実装編)
有限会社 システム設計
twitter @masuda220
selected entries
recent comment
recent trackback
categories
archives
others
mobile
qrcode
powered
無料ブログ作成サービス JUGEM