マイクロソフト株式会社 萩原正義
Microsoft社は2004年よりソフトウェアファクトリーを次世代のソフトウェア開発基盤技術として推進してきた。2009年以降はWindows Azure Platformを始めとするクラウドが新しいソフトウェアの開発および実行基盤と位置付けられる段階になり、クラウドのアプリケーション開発へのソフトウェアファクトリーの適用の機会が増えると予想される。そこでここでは、ソフトウェアプロダクトラインの構築、特性モデルによる可変性(ソフトウェアの要求変更能力)の提供、ソフトウェア開発プロセスの自動化と効率化、DSLによるモデル駆動型開発、開発資産の管理などソフトウェアファクトリーが取り組む技術を考慮し、クラウドのアプリケーション開発へのソフトウェアファクトリーの適用を解説する。
1 クラウドのアプリケーション開発の基本
1.1 クラウドのアプリケーション開発上の制約
クラウドのアプリケーション開発に特有の制約条件を以下に示す。
(1)非同期通信
部分的な障害に対応するため、同期通信によるトランザクションは参加者のライフタイム同期が全体の稼働を停止させるため許容されない。
(2)常時稼働
常時データ更新の可能性があり、スケーラビリティの観点で書き込みロックをしておくことも不可能なので古いデータの読み取りが前提となる。
(3)CAP(Consistency,Availability,Partition tolerance)定理
可用性とスケーラビリティの確保を前提とするとデータの一貫性が確保できないことを提示する。従来のACID(Atomicity, Consistency, Isolation, Durability)トランザクションとは異なるデータ一貫性の確保が必要となる。その一手法がeventual consistencyと呼ばれるデータの一貫性要求の強さを表現したモデルとその実装法である。
(4) キーバリューストア
オンプレミスで主流のRDBに変わり、キーとバリューをセットにしたデータサービスが基本となる。その結果、RDBとは異なるデータ設計、トランザクション実装法、データ管理方法が必要となる。キーバリューストアは行データ毎に異なる物理ノードに配置され、行データ単位でトランザクションを実行するため、スケールアウト設計が基本となる。
1.2 クラウドのアプリケーション開発手順
クラウドのアプリケーション開発(スケールアウト設計)の手順の概要を以下に示す。
(1)スケーラビリティのボトルネックとなるデータ層にキーバリューストアを利用する場合、スケールアウト設計のためにアプリケーションのワークロードパターンからデータアクセス法の最適化(データ非正規化)を決定する。そのため、ワークロードパターンの特定のための機能分割を行う。
(2) (1)と並行して個別アプリケーション機能に依存しないデータアーキテクチャを設計する。具体的にはマスターデータとトランザクションデータを識別し、マスターデータを論理的に一元管理する。トランザクションデータはマスターデータを一方向参照する。
(3)アプリケーションアーキテクチャを設計する。非同期通信、並列処理を前提とした参照アプリケーションアーキテクチャを採用する。必要に応じてマスターデータ分割、その複製をトランザクションデータ側に置き、アプリケーションのワークロードパターンに合わせて非正規化する。キーバリューストアでは行データ単位で異なる物理ノードに配置されるため、非正規化データは水平パーティション化して行データ毎に異なる物理ノードに配置し、物理ノードの並列処理を活用する。データ分割、トランザクション分割によるスケーラビリティの向上を考える一方、分割したデータ間での一貫性の確保のために、eventual consistencyのための実装を追加する。スケーラビリティと一貫性の確保は常にトレードオフの関係にある。
(4)アプリケーションアーキテクチャ上でコンポーネント指向によるアプリケーション開発をする。アプリケーションの要求変更(可変性)に対応してマルチパラダイム設計を行う。マルチパラダイム設計はドメイン毎に設計や実装のためのパラダイムの選択を変えることで、可変性に対して柔軟性を与える設計方法である。たとえば、ロジックの実装、データ構造の振る舞いの変化にはオブジェクト指向、分散バッチ処理の実行には関数型、変更箇所の配置にはコンポーネント指向、機能の提供にはサービス指向というパラダイムの選択と組み合わせを使って設計を進める。
1.3 ユースケースに関連する設計上の問題
クラウドのアプリケーション開発におけるスケールアウト設計では、アプリケーションのデータアクセス方法を決めるユースケースに従ってデータの非正規化、水平パーティション化を行うことが重要である。クラウドのアプリケーション開発ではユースケースが多くの問題に関連づけられている。ここでは、ユースケースに関連する主要な5つの問題を示す。
(a)ユースケースがデータの非正規化,水平パーティション化の物理設計を決定
(b)更新系と参照系にユースケースを分離し,eventual consistencyによるデータ一貫性を実現
(c)1ユースケースはサービスの窓口になるboundaryと,共通性の高いコアモデルのdomain modelの論理レベルの設計モデルに分割
(d)1ユースケースはWebアプリケーションやWebサービスのためのフロントと,ビジネスロジックのためのバックの物理レベルのプログラミングモデルに分割
(e)1ユースケースはクラウド上のトランザクションシステムのフロントと,オンプレミスのトランザクションシステムのバックに配置
(a)(b)はユースケース単位で見た外部仕様での問題である。(c)(d)(e)はユースケースの実装や配置に関する問題で内部仕様での問題である。
2 クラウドのアプリケーション開発へのソフトウェアファクトリーの適用
2.1 ソフトウェアファクトリーの位置づけと現状
クラウドを前提としたソフトウェア開発は大規模、複雑である。クラウドのアプリケーション開発では、DOA(データ中心アプローチ)の分析方法、SOA(サービス指向アーキテクチャ)によるドメインや機能分割、オブジェクト指向分析設計によるオブジェクト指向開発言語を前提とした設計手法、コンポーネント指向による保守や配置、開発プロセス全体を駆動するユースケース駆動などを組み合わせる。たとえば、DOAでデータを分析、定義し、ユースケースを基準にして開発プロセスを駆動する場合や、プロセス分析でビジネスプロセスを分析、定義し、サービスを基準にして開発プロセスを駆動する場合などがある。開発すべき対象、ドメイン、要素技術、実行環境に応じて分析法と開発アプローチを適切に組み合わせてリスクを軽減することが求められる。これに加えて、並列実行や非同期通信の実装のための関数型パラダイム、モデル駆動型開発のためのUML/DSLの抽象化したモデルによる保守とコード生成、可変性に対するドメイン分析とマルチパラダイム設計などを併用する。このように複雑に複合化した技術を用いるクラウドのアプリケーション開発では、技術の複合化にみられる複雑さの問題にとらわれることなく,非同期通信とデータ非一貫性などの根源的な問題を見ていく必要がある。
一方、このような取り組みはもちろんプロジェクトの成功のためには重要ではあるが、ビジネス価値やソフトウェア技術の可能性を十分に高めた革新的なソフトウェアを開発するためには、プロジェクトの駆動や管理、実装やテストをできるだけ効率化し、設計上の意思決定など、より根源的な問題に時間を割くべきであろう。
ソフトウェアファクトリーが目指すソフトウェア開発の工業化は、ソフトウェア開発を完全に自動化するような創造性のない開発を意味していない。むしろその逆であり、繰り返される煩雑な作業を自動化し、創造的なソフトウェアの開発に注力することが目的である。クラウドは技術的な革命であるが故に可能性が高く、他方では複雑な技術課題の克服が命題でもある。ソフトウェアファクトリーのようなソフトウェア開発基盤がまさに求められる開発と実行の環境である。しかし、現状のソフトウェアファクトリーには次の3つの問題が存在する。
(1)ソフトウェアファクトリーは、ソフトウェアプロダクトラインを基本とし、プロダクトラインのアーキテクチャの定義範囲をドメイン分析で決定する。しかし、SOA、クラウドでは広域で時として動的なアプリケーション間連携が前提となるので、アーキテクチャ定義範囲の固定化を要求するドメイン分析は困難な作業となる。ただし現実には、WebやXMLなどが広域アーキテクチャの基盤となっている実績が存在するので不可能なわけではない。
(2)ソフトウェアファクトリーの実現は開発環境やツールの実装には原則として依存しない。ただし、Microsoft社のVisual Studioがその主要な開発環境であることに間違いない。Visual Studio 2005以降、DSL、開発プロセスのガイダンス、変更管理や工程管理の機能が提供されている。しかし、ソフトウェアファクトリーに必要なプロダクトライン開発のためのドメイン分析、設計、実装のガイダンスとその工程管理、特性モデルの作成と実装へのマッピングなどの機能が不足している。こうしたツール上の制約はソフトウェアファクトリーの普及の阻害要因となっている。
(3)アプリケーションの可変性に応じたマルチパラダイム設計、そのための要素技術の習得が不足している。しかし、昨今の開発言語の高度化によりマルチパラダイム言語が普及しており、メタプログラミングなどの利用頻度も多くなってきている。クラウドのアプリケーション開発はこうした機能を利用する機会を増すので、高度なソフトウェア開発基盤の必要性が再認識されるだろう。
2.2 ソフトウェアファクトリーの適用方法
ソフトウェアファクトリーの適用可能なクラウドのアプリケーション開発の分野の項目を以下に示す。従来のソフトウェアファクトリーのソフトウェアプロダクトライン、ドメイン分析、特性モデル、マルチパラダイム設計の適用範囲を広げる。仮想化したアプリケーションのステージング、可変性部分のSaaSへの展開とカスタム化機能の向上、アプリケーションの一貫性の要求の実現、キーバリューストアのデータモデルの活用などを考慮する。
ソフトウェアファクトリーは原則として、アプリケーション依存する個別ユースケースから、アプリケーションに非依存で再利用可能な特性モデルへのマッピングにより可変性に対応する。特性モデルの選択による可変性への対応ではソフトウェアプロダクトラインが実装法を提供するので限定したカスタム化(アーキテクチャ拡張)の範囲で対応可能となる。
(1)アプリケーションの可変性
(a)ユースケースの追加、変更、削除の可変性: クラウドのアプリケーション開発におけるユースケースの変更は、スケールアウト設計に基づく非正規化データとその水平パーティション化したデータモデルの変更、更新系ユースケースの変更に対し、その変更データを参照するユースケースの変更、サービスの窓口となるboundaryモデルの変更、より根源的な場合はdomain modelの変更、プログラミングモデル上の実装技術の選択肢あるいは/および物理配置の変更、クラウドとオンプレミスのシステムの役割分担や両者のシステム間連携方法の変更などにより対応する。
(b)アドホックな(実行時まで未定の)操作による可変性: (a)のようなユースケースが決定しないと定義できないデータ非正規化は採用できないので、再利用可能な分割したトランザクションをサービス単位としたビジネスプロセスで対応する。たとえば、1つのACIDトランザクションは複数のトランザクションに分割し、それらを動的に組み合わせたビジネスプロセスを1つのACIDトランザクションに相当させる。あるいは、ユーザ(サービスコンシューマ)が協調するクライアント/サーバーシステムで共有データを更新して状態を遷移させる。いずれの場合も、ACIDトランザクションが保証する一貫性と同程度の一貫性の保証が可能な実装を行う。
(c)その他の可変性(ビジネスプロセス、ビジネスロジック、データ、ビジネスルールの変更など): 通常の非クラウドでの可変性と同様な対応。ドメイン毎に最適なパラダイムによる設計を選択するマルチパラダイム設計が基本となる。ただし、スケールアウト設計によるデータの分割(水平パーティション化)、プロセスやロジックの並列実行、非同期通信による一貫性要求の実現、クラウドとオンプレミスのシステム間連携に関するSSO(Single Sign On)などのセキュリティやトランザクション分担など、可変性の影響を考慮すべき範囲は大きく依存関係も複雑化しているので、対応はより困難となる。
(2)アプリケーションの一貫性の要求の実現
ACIDトランザクションに代わり、スケールアウト設計では疎結合、非同期通信を前提とした一貫性の要求の実現が必要となる。そのためにアプリケーションの持つ一貫性の要求の強さの表現を一貫性モデルで表現し、一貫性モデルを実現する実装技術の選択肢をコンカレンシーモデルで用意する。これらのモデルをそれぞれ問題領域と解決領域の特性モデルに定義しておくことで、特定のアプリケーションに依存しない再利用可能なモデルによる可変性への対応が可能となる。一貫性モデルには要求の強さの順にsequential(時間的順序の保証)、causal(意味的関係のあるデータ間のみでの時間的順序の保証)、release(データの解放時に一貫性を保証)、entry(データの次回利用時に一貫性を保証)、eventual(いつか将来に一貫性を保証)などが存在する。コンカレンシーモデルには、ACIDトランザクションのsnapshot隔離レベル、キューイング、各種のトランザクションモデルのレプリケーション、楽観的ロック、reader/writerロックなどが存在する。
(3)SaaSのカスタム化
(1)の(c)と同様な対応となる。しかし、SaaSがマルチテナントで動作する場合、マルチテナントの実装法を提供する固有のデータ構造とその構造に依存したアプリケーション設計を前提とした可変性への対応となる。SaaSのカスタム化機能の制約がクラウドの利用機会を低下させている問題に対して、特性モデルとソフトウェアプロダクトラインの活用は解決の一助となるだろう。
(4)コンテキスト(動的可変性)
ソフトウェアプロダクトラインおよび特性モデルは主に静的可変性(設計時)の対応で、動的可変性(実行時)の対応は限定的である。そのため、ダイナミックリンク、Dependency Injection、動的アスペクト、ワークフローなどの基本的な機構を土台として、ソフトウェアプロダクトラインのアーキテクチャ上に動的可変性を支援する機構を作らなければならない。コンテキストとは、種々の実行時環境に応じた動的な振る舞いの変更を可能とする動的可変性の一種である。たとえば、マルチテナントによるSaaSでユーザの位置情報や時刻に依存して振る舞いを変更する場合である。こうした高度な可変性の要求に対して、上記の支援機構に加えてクラウドのストレージサービスであるキーバリューストアの柔軟なスキーマ定義の変更機能を組み合わせて対応することが考えられる。
(5)クラウドのアーキテクチャに対応した開発環境の可変性
クラウドのアプリケーションは常時稼働で、常時データ更新の受信可能が基本である。この条件で漸進的に機能や非機能要求の変更を行うためには、コード/データ/構成定義などの変更とビルド、テスト、コンポーネント保守と配布、DSLによるモデル駆動、開発プロセスの支援、プロビジョニングなどを迅速に繰り返し実行できる開発環境が必要となる。この開発環境自体の可変性の要求には、仮想環境単位にソフトウェアプロダクトライン、開発ツール、必要とされるコンポーネントなどを用意して対応する。すなわち、仮想環境毎のソフトウェアファクトリーを提供する。リリースに関しては従来の製品リリースをクラウドへの配置を前提としたものに置き換える。
© 2009 Microsoft Corporation. All rights reserved.
Microsoft、Visual Studio, Visual Studioロゴ、Azure、Windows は、米国 Microsoft Corporation の米国および/またはその関連会社の商標または登録商標です。
その他、記載されている会社名、製品名は、各社の商標または登録商標です。
アーキテクチャの基本的な考え方は、機能要求に先行して構築されてることです。
この原則は、EA(Enterprise Architecture)、DOA(データ中心アプローチ)、プロダクトライン開発のアーキテクチャでもほぼ同じ思想を持っています。
一方、アジャイル開発ではTDDなどを使い、要求を定義しその実現を検証しながら開発を進めます。その結果、開発される成果物は必然的に要求との依存性が高くなります。要求との追跡性の重視や、ビジネス価値の追求を重視すれば、その傾向はさらに強くなります。それ自体は悪いことではなく、顧客の満足を考えればいい結果をもたらすでしょう。
リファクタリングを行うことで得られる結果は、インターフェイス定義の明確化(変化する部分と変化しにくい部分の分離)、コンポーネントやクラスやサービスの粒度の適正な決定を行えること、拡張性のためのより良い解を得られることなどです。
アーキテクチャをコンポーネントの関係でとらえるとリファクタリングはこの構造を決める上で有効な手段と感じるでしょう。
ただし、ここでアジャイル開発におけるアーキテクチャの構築で2つの注意点があります。
(1)アーキテクチャは現在の要求だけで構築すべきでない。
(2)コンポーネントの構造だけがアーキテクチャではない。
たとえば、(1)で要求がゴールの達成やそのための戦略に基づいて定義されていたとすれば、アーキテクチャは直接的な要求を満足するだけでなく、ゴールの達成のために構築されるので、より合理性を持ちます。ただ、その場合であっても、EAにあるように、システムのアーキテクチャが存在する以前からビジネスアーキテクチャは先行して存在しており、その一部がシステムアーキテクチャになることを考えると、アーキテクチャはシステムへの要求とは独立する存在と考えたほうが適切といえるでしょう*1。これは、教育システム、法令システムの社会構造が、個別のそれらへの要求を超えて存在しているのと同じ状況です。個別の要求はアーキテクチャに対して変化を及ぼすことはあっても、アーキテクチャは要求とは独立して存在すべきなのです。より分かりやすい例は、開発言語やRDBを大きなアーキテクチャとみなせば、これらの技術が大きくは社会的な要求によって変わったにしても、個別プロジェクトの要求くらいでは変わらないことでも分かるでしょう。
*1 ZachmanもEAはシステムアーキテクチャというより、Enterprise(企業経営、活動)のためのと言っている。
(2)は概念や哲学などの発想を言っています。たとえば、トランザクションモデルでのスナップショット分離レベルのような比較的新しいトランザクションモデルを概念として取り入れることもアーキテクチャの一部です。しかし、こうした技術は、コンポーネントの粒度の決定、インターフェイス定義の明確化とは独立しています。発想できることは、開発プロセスにアジャイルを使うかどうかとは別の次元です。アジャイルを使うことにより、多くの概念の発想ができる可能性が高くなるのであれば、アジャイルはアーキテクチャ構築に有効な手段となるでしょう。
一般には、(1)では要求の裏付けとなるゴールまで考えたアーキテクチャよりは、要求を直接実現するアーキテクチャになりやすく、(2)では、発想の探求よりも要求の実現を第1と考える傾向が強くなるので、この場合には、アジャイル開発におけるアーキテクチャの構築はうまくいかないでしょう。
現実には、アジャイル開発は、既存のフレームワークや開発基盤技術の上で行うのが前提であるので、アーキテクチャの大半はアズイズで与えられていて、その上でロジックの構造だけを考える場合が多いので有効となっているとみられます。MVCやデータアクセスの方法が与えられている場合か、プロジェクトリーダの頭の中に基本的なアーキテクチャのアイディアがアジャイルとは関係のない次元に存在している場合が多いといえます。
さて、皆さんの意見はどうでしょうか?
平鍋氏のアーキテクチャとはを読んで私のアーキテクチャに対して感じていることを以下に書きます。
http://blogs.itmedia.co.jp/hiranabe/2008/10/grady-booch-dd7.html
Booch氏のアーキテクチャはデザインであるが、すべてのデザインがアーキテクチャであるわけではない、という深い意味を持つ言葉は、アーキテクチャは暗黙知を表現する1形態であって、連続する意思決定から構成されていると、解釈します。暗黙知だから、創発されることもあり、また、パターン化のような形式化に限界があることが説明できます。またすべての意思決定の意図が表現できるものでもないことが分かります。こうした表現形態は形式的にUMLなどのモデルで表現できたとしても、意図を正確に伝えることには限界があります。
では、我々はアーキテクチャをどう扱えばいいのでしょう。僕はできる限り科学に基づくべきだと考えています。そうであるなら、ある程度の合理性と客観性を持つことができるでしょう。アーキテクチャは多くの場合、箱と線による抽象的なモデルで表現されるので、分かりにくく、あいまいだと考えてしまうことも多いのですが、抽象化はあいまいとは違います。抽象化したとしても、厳密な定義は可能です。
いつもはモデルを見せて、アーキテクチャは「はい、これです」みたいなトップダウンのパターンですませてしまい、深い意味を犠牲にしてしまうので、ここではアーキテクチャを科学に基づかせる例を具体的に示しましょう。
例1: .NETのコードアクセスセキュリティのアーキテクチャ
コードアクセスセキュリティ(CAS)の詳細な説明はここをご覧ください。
http://www.atmarkit.co.jp/fdotnet/technology/idnfw11_index/index.html
アーキテクチャとして、コンポーネントとOOPを採用する前提でコードアクセスセキュリティを提供する機構を考えてみれば、コード実行に信頼性を上げる方法は、物理的に以下の段階でのセキュリティチェックに決まることになります。
(1)コンポーネントのロード時のチェック
(2)クラスのロード時のチェック
(3)メソッドがJITコンパイル時のチェック
(4)メソッドの呼び出し実行時のチェック
この段階は、コンポーネントとOOPを組み合わせてソフトウェアを開発するという前提で、ローダーやJITコンパイラのような機構を使うとしても、実行のタイミングとして必然的に現れます。いわば物理法則で決まるタイミングです。そして、それぞれのタイミングでのチェックは、チェックにかかる計算コストをできるだけ減らすという指標をとれば、また、おのずと決まってくるのです。後は、(1)ではコンポーネントのメタデータを記述する宣言、(2)はクラス間の継承関係の制約、(3)はリンク時のチェック、(4)はスタックの呼び出し順に従う制限を考えればいいことになります。
例2: IIS の拡張アーキテクチャ
現在のCPUアーキテクチャは、スレッドの実行予想やキャッシュの有効化を考えるとパイプライン処理が有効となります。IISの拡張性はこのパイプラインを前提としたアーキテクチャです。パイプラインアーキテクチャを採用する場合、パイプラインを構成する各ステージは時間順序の制約から物理法則で決まります。たとえば、ページの実行前には、認証や認可のチェック、キャッシュが利用可能かどうかのチェック、URLのマップ、各種サーバ状態(コンテキスト)の設定などが必要です。こうしたプログラムの実行、リソース間の制約関係、で決まる一貫性の制約を守ることは、誰が考えても必然的な結果を得ることができます。
http://learn.iis.net/page.aspx/496/iis-url-rewriting-and-aspnet-routing/
は、IIS 7.0の画面遷移のアーキテクチャの比較の例です。URLの書き換えによっても、ルーティング制御を持つ機構とマッピングを組み合わせた機構を使ったとしても、それぞれの優劣の比較はありますが、根本的には物理法則に依存していることが分かるでしょう。
アーキテクトの意思決定は非常に多くの技術選択肢の中から最良と思うものを選択します。属人的な決定かもしれませんが、科学の世界に基づくことで、ブレを少なくすることは可能です。
以上のような考慮と意図を具現化したアーキテクチャは、しかし、多くの仮定、前提、制約条件から成り立つ暗黙知の一種であることには変わりません。
前回の説明の中でモジュール化という言葉を出しましたが、この定義について誤解を生まないように少し説明を加えましょう。モジュール化とは、モジュールを作るという意味ですが、ここでのモジュールは.NETでのアセンブリー、あるいは、JavaでのJARのように物理的な配布のためのパッケージではありません。これらは、配布、インストール、運用時の構成管理(セキュリティ設定など)、物理レベルの資産のバージョン管理のための単位です。一般的には、機能の単位、保守の単位、再利用の単位、配置の単位に利用されます。これらは、物理的形式(ファイル)ですので、プラットフォームや実装に依存します。
一方、ここでのモジュールは、論理レベルの成果物(モデル)に対する単位をいいます。主に可変性の単位で、内部のモデルはほぼ同一の変更の度合いを持っています。分かりやすく言えば、コードの変更の例で、データベースのスキーマにカラムの追加を行う場合、それを処理するデータアクセスコードやロジック、UIの変更も必要となる場合があります。これらの変更はすべて一貫性を持ってなければならず、テスト検証の上、チェックインされます。変更はアーキテクチャのレイヤーにまたがる複数の箇所で起こり、この範囲を表現するのがここでのモジュールです。
モジュールは適切は配布のためのパッケージに関連づけられます。まずモジュールが定義され、そして配置のためのパッケージ化に対応づけられます。それぞれが別の構成定義、バージョン管理されます。
このモジュールはおわかりのように、アーキテクチャのレイヤー(論理的なコンポーネント)構造とは複雑な関係にあります。両者は依存関係は持っていますが、モジュールからアーキテクチャに対して一方向の依存関係があり、その点では、アーキテクチャを先行的に設計しますし、モジュール化の定義の仕方にアーキテクチャの設計は影響を受けません。
このモジュールはまた別の特徴としてパラメータをとります。パラメータによりモジュールを構成定義可能として可変性を持たせるといってもいいでしょう。実際には、パラメータはアーキテクチャの拡張部分に作用してアーキテクチャの構成を可変にしたりもします。このパラメータの設定をXMLやプログラムで行ってもいいですし、DSLのようなツールを使って行ってもいいです。
さて、こうして作られたモジュールをアーキテクチャを使うことを前提としたプロジェクトで再利用していきます。あるプロジェクトでは、モジュールAとBをそれぞれパラメータ設定して再利用し、別のプロジェクトではモジュールAとCをまた別のパラメータ設定して再利用します。
ここで疑問なのは、こうしたモジュールの再利用が有効となるかどうかという点です。いままでもコンポーネントの再利用があまり有効でなかったのに、モジュールにしたからといって同じように有効とならないのではという疑問です。もっともな疑問でしょう。
モジュールがコンポーネントと異なる点は3つあります。
1つは、要求に対してあらかじめドメイン分析を行い、どのような可変性が必要かを分析してスコープを決めている点です。したがって、ここでもモジュールはこのスコープ内に有効性は限定されます。この限定により、再利用性を高めようとします。
第2として、可変性を持たせることにより再利用を決め打ちにしていないことです。完成したコンポーネントは適用範囲を狭めますが、モジュールは可変性をプログラム可能な領域まで広げることで再利用性を高めようとしています。
第3として、これが一番大切なことですが、モジュールは再利用の状況を見てリファクタリングされるという点です。当初のプロジェクトでモジュールの定義があまり有効でない場合は、その後のプロジェクトでは、モジュール定義を変更して再利用性を高めます。しかし、この場合でもアーキテクチャとの一方向性の依存関係により、アーキテクチャには変更がないようにします。アーキテクチャの保守は別の観点で行うべきだからです。
こうしたモジュールを可変性を表現するフィーチャ(特性)モデルに対応づけます。つまり、要求からフィーチャを構成定義します。フィーチャはモジュールに対応づけられているので、フィーチャの構成定義はつまり実装の構成定義になります。要求は実現されるわけです。
この前提にはアーキテクチャはドメイン分析で作られること、そして、プロジェクトで繰り返し利用される長期の資産であることが求められます。いままでのようなワンオフ開発ではありません。ですので、ワンオフ開発が投資的に有利な場合の話ではありません。
モジュールは要求を構成定義するフィーチャから使われ、適切にリファクタリングされるという前提では、その存在理由から100%の再利用性を持ち得ます。
これがソフトウェアプロダクトラインです。請負型のワンオフ開発をやっている限り再利用性を高めるには限界があることを理解されたでしょうか。ソフトウェアプロダクトラインは請負型をプロダクト(パッケージ)開発型にする手法です。また、そうでないければ、大部分のソフトウェア開発の未来は明るくないと思っています。
要求に時間をかけることは大切ですが、同じくらい設計にも時間をかけるべきでしょう。日本の現状は、要求やプロジェクト管理に行き過ぎていて、設計や開発手法の有効性について見落としているのではないでしょうか。この方面ではまだやることが多くあり、それらの改善はプロジェクト管理よりも生産性に寄与し、品質にも大きく貢献できると思っています。
アーキテクチャはコンポーネントの構造として定義されます。この定義を見て、アーキテクチャはコンポーネントをモジュールにして実装すると思っている人が多いのではないだろうか?
しかし、ここで指摘したいのはアーキテクチャの設計、その構造定義と、モジュール化は分離して考えなければいけないことです。たとえば、Webアプリケーションやマイクロソフトのエンタープライズシステムでの参照アーキテクチャ、JavaEEで見られる多層モデルによるアーキテクチャの構造はモジュールではありません。
アーキテクチャのそれぞれの役割に応じて論理的なコンポーネントを定義し、それらが複合構造となっています。UI、UIプロセス、ビジネスロジック、データアクセス、データハンドラ、サービスエージェントなどと呼ばれる論理的なコンポーネントを構造化してアーキテクチャを定義します。このアーキテクチャに対して、保守の単位、品質の確保のための検証の単位、ユーザに対する有効な機能の単位、再利用の単位、などがどうなるかを考えてみましょう。これらが、モジュール化の対象となります。
現在のアーキテクチャはフレームワークは前提としますが、一回毎にカスタム(ワンオフ)開発する請負型の開発が大部分で、UI部品など物理的なコンポーネント、抽象クラス、UIスタイル、コーディングコンベンション、開発プロセスモデルなど、断片的、局所的な再利用に限定されています。つまり、モジュール化の対象として、再利用の単位、ユーザに対する有効な機能の単位を考えるほどの洗練されたレベルになっていません。このことは、アーキテクチャのコンポーネントの構造とモジュール化が分離できていない理由の1つでもある。また、現在の主流のオブジェクト指向が、再利用の単位やユーザに対する有効な機能の単位を分離するには、制約が大きいことももう1つの理由となっています。
たとえば、再利用の単位としてクラスを考える場合、単一責務の原則でクラスを定義したとしても、、非機能要求を含めるとクラスには多くの関心が含まれます。その結果、オブジェクト指向特有のもつれ合いの問題が発生します。このもつれ合いを回避し、再利用性を高めるには、クラスより小さい言語構造、あるいは、モデル要素が必要となるのです。Scala やC++にある”trait”という言語構造はこの問題を改善するために存在します。
ユーザに対する有効な機能の単位としては、Jacobson氏が提唱するユースケーススライスがあります。ユースケーススライスは、クラスだけでなく、既存クラスに挿入するメソッド、属性などをまとめて定義するパッケージです。これはユースケースがクラスに横断的になっており、やはりオブジェクト指向特有の散乱の問題を発生させるからである。
http://www.amazon.co.jp/%E3%83%A6%E3%83%BC%E3%82%B9%E3%82%B1%E3%83%BC%E3%82%B9%E3%81%AB%E3%82%88%E3%82%8B%E3%82%A2%E3%82%B9%E3%83%9A%E3%82%AF%E3%83%88%E6%80%9D%E8%80%83%E3%82%BD%E3%83%95%E3%83%88%E3%82%A6%E3%82%A7%E3%82%A2%E9%96%8B%E7%99%BA-Object-Oriented-Selection%E3%82%B7%E3%83%AA%E3%83%BC%E3%82%BA-Jacobson/dp/4798108960/ref=pd_bbs_2/250-7397366-0246623?ie=UTF8&s=books&qid=1190189497&sr=8-2
以上から、アーキテクチャの構造からモジュール化を分離できないのは、既存の技術制約が問題となっていることがわかります。
しかし、それ以上にワンオフ開発では、この技術課題を開発プロセスの改善などで補ってきた(*)ことで、本質的な問題の解決を避けてきたともいえます。再利用の問題1つにしても、あまり進んでいないのは、技術的制約にあるのではなく、ワンオフという開発の仕方が再利用を強制していないことによる面が大きいのです。
(*) クラスのリファクタリングで開発フェーズ、リビジョン、ビルドなどで調整する。
前回の補足です。
アスペクト指向がソフトウェアプロダクトラインに本質的に親和性を持たない理由は、ソフトウェアプロダクトラインのアーキテクチャの拡張点には、拡張点に応じて「異なる」拡張コードを導入しなければいけないからです。一方、アスペクトの定義は、この拡張点をjoin pointで定義する場合、「同一」のadviceのコード定義を導入することになります。これでは、ソフトウェアプロダクトラインのプロダクトに応じた拡張点の拡張コード、プロダクトのバージョンに対する拡張コードの進化がうまく表現できません。
Software Factoriesの教科書には、factoryの実装技術としてアスペクト指向コンポーネントが有望ととられるような記述がありますが、厳密には現在のAOPは適切な実現法とは言えません。Microsoft社がAOPに消極的である理由は優先度の問題もありますが、このあたりに理由があります。
歴史的にみれば、AHEADなどソフトウェアプロダクトラインと親和性を持つFOP(Feature Oriented Programming Language)は、mixinを拡張法に持ちます。mixinの方がアスペクトに比べて、base クラスに対する局所的な拡張性、バージョン管理に対して有効な技術と考えられるからです。また、AHEADは直交する関心を代数学的に扱うことで、特性の複合化の組み合わせ数を減らすためのプラットフォームとなっています。正確にいえば、直交する関心の個数(次元)をN、各関心のとりうる実現法の数をkとすると、O(N**k)がO(N*K)になります。
さて、mixinとアスペクトを組み合わせて相補的にソフトウェアプロダクトラインを構築する方法はないでしょうか?
アスペクトが持つpointcut定義を使い、アーキテクチャの拡張点をjoin pointで定義します。次に各join pointではadviceを使わず、間接的にmixinを呼ぶようにします。こうすれば双方の利点を得ることができます。これにより、特性間の依存関係は、特性を含むシナリオをサポートするユースケーススライス、つまり、アスペクト間の依存関係となり、最終的にmixinの依存関係になります。つまり、mixin間の複合化の制約関係になります。
こうしてみると、ソフトウェアプロダクトラインに有効な複数の複合化アーティファクト(設計モデルやパラダイム)を考えていくと、技術が複雑化することがわかります。まして、それらの設計が正しく行われるための分析プロセスなど方法論自体が複雑化します。これをドメインエンジニアリングで行うアーキテクトの技術力いかんにソフトウェアプロダクトラインの成功がかかわるということになります。
私はこうした複雑化した技術は好きではありません。単純化こそ有効な手段と考えています。しかし、企業システムの領域に限っても、ドメインをストレートに表現するドメインモデルは、ビジネス構造をできるだけ忠実に表現しているようでも、そこには多くのビジネスの振る舞い、シナリオが暗黙のうちに仮定されていることは理解しなければなりません。つまり、構造的にみれば、抽象化が可能な比較的単純な表現になっているかもしれませんが、ビジネスルールや要求の検証プロセス、状態遷移などが隠されていて、本質的にはかなり複雑なはずです。ですので、ドメインモデルと使うかどうかによらず、現実世界のシステムはかなり複雑で、それを体系化するソフトウェアプロダクトラインも必然的に複雑さを扱う必要が生じます。その結果、抽象化や単純化のための技術の適切な選択が重要となります。AHEADの代数学的アプローチ、AOPのpointcut言語、mixinの複合化技術、アスペクト指向分析設計など、技術の採用が正しいかどうかの判断を今一度考える必要があります。
ソフトウェアプロダクトラインの開発ではアーキテクチャに適切な拡張性を持たせ、要求に応じてアーキテクチャを再利用しつつ、その拡張性を使い個別の可変性に対応していくことが求められます。
コアのアーキテクチャの開発は、オブジェクト指向を使ったフレームワーク開発に代表されるように、アーキテクチャスタイルを考慮して進められます。拡張性の実現法は、用いるパラダイムに応じて多様な選択肢がありますが、典型的にはコンポーネントの接続を用いた設計がとられます。ここでいうコンポーネントは.NETアセンブリなどの特定プラットフォームの物理レベルのコンポーネントを指すのではなく、UMLのコンポーネントで表現される論理レベルでインターフェイス定義を持つモデルをいいます。このモデルをオブジェクト指向を用いて実現するならば、Open-Closed Principleに従う、複数のクラスで構成される物理的配布単位とみなすことができます。しかし、ソフトウェアプロダクトラインの可変性の観点では、こうした静的タイプだけによる実現法では限界があります。より一般的にはUMLパッケージを用いる方法があります。UMLパッケージは物理的配布単位で得られる変更の一貫性の単位を論理レベルで表現しているとみなせます。トランザクションスコープといってもいいでしょう。これにはクラスのような静的な構造だけでなく、シーケンス図や状態遷移図のような動的な振る舞いを含むことができます。こうして複数のモデルをまとめて1つの一貫性のある変更単位を定義するのがUMLパッケージです。内部にあるモデルが論理レベルの表現であるのなら、UMLパッケージ全体として実装に依存しない定義が可能となります。UMLパッケージはパラメータを取ることができます。たとえば、クラスのフィールド定義、シーケンス図の操作の一部をパラメータ化することができます。こうして、UMLパッケージを定義しておき、その実現法を変更の一貫性を含めて、モデル変換やモデル言語のコード生成に任せることで、可変性への対応をモデルベースで行うことができます。
たとえば、UMLパッケージとして最近注目を浴びているのが、Jacobson氏のユースケーススライスです。ユースケーススライスとは、その名前のとおり、ユースケースに関する追跡性のあるモデルです。ユースケースの実現をオブジェクト指向で行うと、クラスに横断的な構造となるので、アスペクト指向を使い横断的な関心としてユースケースを表現します。ただし、AOPとは異なりプログラミング言語の物理レベルではなく、概念、論理レベルを主に対象としたアスペクト指向の表現です。
さて、ソフトウェアプロダクトラインの可変的な特性をユースケースとして表現し、それをユースケーススライスを使ってモデル駆動型で実現しようとする試みは、一見すると非常に有望な手段と一頃は思われていました。しかし、研究が進むにつれて多くの技術制約や問題が明らかになり、現在はあまり有力な手段とは思われていません。私も1年ほど前、こうした検討をかなり進めましたが、実現可能性という点で採用しませんでした。
現在のこの分野の動向は、AOSD Conference 2007の動向などから、2つの方向性を持つと考えています。
- 1つは、ソフトウェアプロダクトラインの特性と親和性のある形に実現法を変えてアスペクト指向の技術を適用していくこと。たとえば、アスペクトはベースクラス(OO)に対して横断的にコードの挿入やインターセプトを行うために、アスペクトとベースを関係づけるポイントカット言語が1対nの関係を持つ特徴があります。一方、ソフトウェアプロダクトラインでは、1つの可変点に対して要求に応じて複数のバージョンのコンポーネントやパッケージが対応づけられるので、パッケージとベース(可変点)はn対1の関係を持ちます。これは、関係づけにおいて本質的な違いで、アスペクト指向をソフトウェアプロダクトラインへ適用するうえで大きな制約となります。このためアスペクト指向はフィーチャ指向に置き換える必要があります。
- もう1つは、モデル駆動型開発に用いるDSL自体にアスペクト指向を入れるというアプローチです。これをDomain Specific Aspect Language(DSAL)といいます。たとえば、トランザクションモデル、例外処理などに特化したDSLの開発での適用が考えられます。こうして汎用DSLに対して、そのDSLを補完するDSALを用意して可変性に対応します。
一般にスケーラビリティを得るためには、分散させないことがアーキテクチャの原則です。
プロセス間通信のマーシャリングコストはインプロセスのローカルコールに対して桁違いのオーバーヘッドになるからです。また、分散トランザクションのロックもこの状況を悪化させます。それにもかかわらず、GoogleやAmazonなど大規模サイトは分散アプローチをとり、スケーラビリティを確保しています。この事実は非分散アーキテクチャの原則を否定しているともいえます。
企業システムや大規模Webアプリケーションを想定する場合、非分散のアーキテクチャの原則は有効でしょうか。分散させない原則は企業システムではなかなか現実的に実行は困難です。性能面だけでなく、管理コスト面で分散システムには不利益がありますが、本当でしょうか?性能面と管理面の2つを考えてみましょう。
- 性能面
分散システムのスケーラビリティを考えるとき、マスターデータをどう扱うかが最も大きな問題と考えます。冗長性を排除した論理的に統合されたデータ定義の原則、"one fact in one place" はシステムが分散か集中化によらず有効な原則です。問題は、論理的に統合されたデータ定義を物理的にどう配置するかです。これには2つの考え方があります。いずれもスケーラビリティの観点から、shared nothingを原則とします。
1つはドメインモデル(論理データモデル)に対してユースケースで決定されるトランザクションスコープを単位としていく考え方。トランザクションスコープが分散しないように、独立したサービス、権限の範囲を単位とした配置とします。たとえば、注文に対して、製品出荷、決済に関する部分は別トランザクションとして切り離します。もう1つはマスターデータのパーティショニングを単位とする考え方です。たとえば、Web 2.0系のアプリケーションではユーザ単位に配置を決定します。この2つの考え方を物理的な配置の設計モデルとします。
性能的に最も重要なのは書き込みの帯域幅です。物理的にマスターデータを統合すると、書き込みに対する一貫性を確保しつつ、読み取りに対してはレプリケーションを使った性能改善ができます。しかし、これは誤ったアーキテクチャです。レプリケーションはバッチ型でそれ自体のオーバーヘッドが大きく、書き込みに対するスケーラビリティの改善となりません。書き込みの帯域幅を上げるには、マスターデータの物理的なパーティショニングを行い、それぞれパーティションを処理するホストに対して書き込み処理を並列に実行することが重要です。また、各ホストが処理すべきデータ量は統合ホストに比べて減るのでキャッシュのヒット率が上がります。ここで、ホストが書き込み処理に対して並列な動作をするためには、ホスト間にまたがるデータ操作ができるだけ減るようなデータ設計をユースケースから考える必要があります。たとえば、あるユーザへの別ユーザのコメントの追加などのデータ処理をどうするかです。これと類似の問題に、特定のパーティションに対する負荷が大きくなり、パーティションの切り直しが必要になる場合の対応があります。この対応ができるだけ少ないという前提での設計を考えるべきです。物理パーティショニングでは、これらが満たされる制約条件があるので、万能というわけではありません。
以上の考慮から、分散トランザクション処理と物理パーティニングは相反する要求であることがわかります。物理的に分割すれば一貫性に対するトランザクション処理が必要となり、分散させないために集約すれば、負荷が集中するからです。この相反では物理パーティショニングを優先します。スケーラビリティに対して、線形的な拡張が期待できるからで、また、安価なサーバハードウェアによる構築、耐障害性の利点があるからです。また、レプリケーションとパーティショニングも相反します。ここでは上記の説明のとおり、パーティショニングをとります。
- 管理面
サーバ集約がもてはやされているように、システムが分散することによる管理コストの上昇があります。しかし、一方では、集約による障害対応のコスト高が伴います。集約によるデータ量の増大もバックアップコストを上げます。分散による管理コストの上昇はあるのですが、物理分割以前にデータの論理的な統合に伴うコスト削減効果が高いので、論理と物理をいっしょに考えるべきではありません。特に現状の企業システムでは、論理的に統合されていない、データのサイロが多数あることによる管理コストの負担が大きいからです。
スケールアウトの分散システムにおいての鉄則は分散トランザクションを使わないことです。しかし、現在のプログラミングモデルでは.NETもJavaでもトランザクション伝搬がプログラミングの位置の透過性から防止することができません。つまり、システム設計上トランザクションを利用するとき、開発プロセスの後半の配置の決定の段階で分散配置を選択すると、プログラムはそのまま分散トランザクションを実行してしまいます。この機能は位置の透過性という点では優れているのですが、アーキテクチャのスケラビリティの要求からは、このプログラミングモデルは不都合なのです。
業務プロセスの実行単位のサービス化、DOAなどによるマスターデータの論理的な一元管理、マスターデータに付随したトランザクションデータの業務プロセスにローカルな配置、あるいは、マスターデータ自身のパーティショニングやレプリケーションなど。これらはそれぞれ、アプリケーションポートフォリオの自律的管理、データの保守性の改善やデータの冗長性を減らすことによるアプリケーションの保守性の改善、マスターデータアクセスの性能や可用性の改善を目的にしています。
しかし、これらはいずれもトランザクションの分散化を招くという点では、スケーラビリティとはトレードオフの関係にあるのです。両者をいかに両立してアーキテクチャを構築するかが意思決定のポイントです。
Martin Fowler氏はeBayのアーキテクチャを論じています。
http://capsctrl.que.jp/kdmsnr/wiki/bliki/?Transactionless
ここでは、スケーラビリティをとるアーキテクチャを選択しています。つまり、分散トランザクションを捨てているわけです。
さて、両者をどのように両立するでしょうか?
両立には大きくわけて2つの考慮点があります。1つは設計モデルの決定、もう1つは分散に対するプロトコル上の考慮です。
設計モデルに関しては、責務の配置が決定した概念モデルと、サービスやオブジェクトのようなパラダイムに依存する設計モデルとの間に、トランザクションスコープを定義する設計モデルを新たに考えます。この設計モデルは、概念モデルと同じ概念に関するキーで識別されるモデルであって、DDDで導入されたaggregateに近い、ドメインモデルの部分構造を持っています。概念モデル全体に対して動的スコープで切り取られる部分構造といってもいいかもしれません。この設計モデルは、トランザクションを分散させない、ローカルなサーバないし密結合のクラスタに配置されるものです。また、概念が設計上パーティショニングされるのであれば、パーティションされた部分となります。
この設計モデルに対して、次に、サービスやオブジェクト指向などの実装パラダイムが選択され、それらの論理レベルの設計モデルにマップされます。
もう1つのプロトコル上の考慮ですが、疎結合が前提となる上記の設計モデル間の連携では、メッセージパッシングが有効です。メッセージは通常はat-least-onceのセマンティックスを持っているので、それを処理してローカルトランザクションを起動するstatelsssプログラムは、idempotentでなければなりません。そうでなければ、2重送りのメッセージに対して同一結果の応答ができなくなっています。このidempotent処理では、連携する相手の概念モデルをベースとした上記のキーを用いた状態管理が必要です。これは従来カンバセーション管理を概念レベルまで引き上げたものです。そうプロトコルレベルでも、ADO.NET Entity Frameworkで見られた概念レベルのデータモデルの導入が行われるわけです。その他、仮予約や確認応答のようなロングトランザクションのためのプロトコルが必要となります。
このような設計モデルとプロトコルの導入により、レイヤー化に伴うスケーラビリティの課題を改善しながら、SOAやDDDとの両立を果たすことが可能となります。
アーキテクチャのもう少し詳細な説明はTechEd 2007 Yokohama でお話ししたいと思っています。
http://www.event-registration.jp/events/te07/SessionDetail.aspx?sessionid=T1-501
ソフトウェアは概念、論理、物理レベルで定義されます。もっとも論理と物理レベルの境界の曖昧性から、概念(ビジネス、what)と論理/物理(IT技術、how)で分類する場合もあります。
いずれにしても概念レベルでソフトウェアがカバーする範囲、コンテキストを明確化することが大切です。従来の概念レベルでの定義は、概念モデルと呼ばれるモデルで、ビジネスの仕組み(ビジネスアーキテクチャ)、知識とその体系化を示すことが中心でした(これ以外にビジネスプロセスなどの動的記述もありますが)。しかし、この定義をベースとしたソフトウェア開発は概念と論理レベルの間に深いギャップがあるため、概念モデルは仕様というには抽象的過ぎました。この抽象的という意味には、
- 概念モデル(そのモデル要素)とその実現技術との関連性が直接的に理解しにくいという意味
- 概念モデルの表現の実現にはステークホルダーの関心の違いがあり一意に決まらないという意味
があるように思えます。1は実現技術の選択肢が無数にあり、プラットフォームや非機能要求、プロジェクトの持つ制約(納期、コスト、開発者のスキルなど)が前提とならないと決められないことです。また、2は概念モデルのビューに相当し、ステークホルダーが持つ価値観、要求の相違により、実現する場合にどの部分が優先され、どういった実現手段が有効かが決められることによります。逆に、ステークホルダーを特定し、その価値観や要求を特定しない限りは、実現法は一意には決められません。いずれの場合も、要求と実現した成果物との追跡性の問題を引き起こします。
さて、こうした概念モデルの特徴を見ると、概念モデルをベースとしてモデル駆動型開発を行うことは非常に困難と言えるでしょう。私は、実績により技術の有効性を判断する立場に立つので、OMGのMDA(Model Driven Architecture)の芳しくない実績を考えると、この判断は正しいと思えます。モデル駆動型開発では、モデルを変換して最終的にソースコードないしバイナリーコード(中間言語を含む)に落とすか、モデル自体を仮想マシンで動作させるかの手段をとります。いずれの場合も、モデルは、実行可能になるために十分な記述能力を持っている必要があります。
ビジネス分野において概念モデルに要求される実現機能には、データ関連に限れば、クエリー、分析、報告、統合、同期などの代表的な操作があります。もしこれらの操作が、十分に汎用性が高く、かつプラットフォームに依存しない、ないし、特定の実装との関連づけを制約なく行うことができるのであれば、概念モデルに適切なアノテーションを追加することで、実行可能となりうると想像できます。
データに関しては従来、論理設計でのERDが主流でしたが、保守性や実装技術の選択肢の自由度を考えると、概念モデルを中心とした実装技術への移行が有利と考えられます。ただ、ここでは、概念モデルが実行可能であることが前提となります。
概念モデルによるアプリケーション設計の流れは、Microsoft社がVista(Longhorn)で実現しようとして一度は頓挫したWinFSの流れを組むものです。現在のOOPのクラスやオブジェクト、RDBのテーブルと関係をベースとした、O/Rマッピング、データバインディング、プロキシー生成などを根本的に変化させる技術です。この技術は、ADO.NET Entity FrameworkでOrcas以降に提供される予定です。
こうした流れはC#などOOPの言語仕様に関数型パラダイム(匿名関数、匿名メソッド、ラムダ式、クロージャ)などを導入したマルチパラダイム言語の登場とあいまって次世代の開発基盤を構成します。
哲学は自然科学と違い、万人が同じ認識に至るという点での普遍性が乏しいと言われています。また、哲学は難解というだけで敬遠されています。忙しい現代人にとって実務で覚えるべきことが山のようにある状況では哲学までは手が回らないのでしょう。IT分野は変化が激しく実務的な側面が強いので、哲学は無関係な分野、直接的な効果は期待できないと思われているかもしれません。第一、哲学とIT技術のどこに接点があるか、学問的にも確立されているとは言い難いです。
今回、紹介するカントの純粋理性批判は、難解さではかなりなレベルだという一般の認識ですが、ここにはソフトウェア分析やアーキテクチャの原則が多く隠されています。もちろん、当のカント自身、将来のIT技術の到来を予見できていたとは思えませんが、人間が持つ普遍的な認識や理論の体系化という題材に左右されるIT技術もまたこの哲学の配下にあると言えるでしょう。
人間の理性で作られる世界、たとえば、数学やオブジェクト指向は、過去の経験がなくてもわかる普遍性があります。たとえば、小学生に1+1=2の原理を教えても、過去の経験がなくても理解できるのは、人間の持つ理性の性質です。一方、そうでない理解のために経験な必要な分野も多くあります。この2つを分けて考えることを前提とします。
カントは自然科学自体は否定していません。しかし、その有効範囲については独断の世界が存在すると説明しています。
ソフトウェア工学でいえば、パターンの存在は工学的に有効な普遍性を持つけれど、それが有効となるコンテキストに関しては独断な部分が排除できないとする点です。ソフトウェア工学が属人性を排除し、その有効性を獲得する最後の適用の部分は、科学的ではなくなるということです。この峻別を明確化している点は現在でもまた将来にわたっても生き続けるでしょう。
これに加えて、ソフトウェア工学に対する直接的なこの哲学のメリットは人間の認識における部分です。
人間の認識は、空間と時間における直観から、思考への流れを取ります。この思考には4つのカテゴリーが存在します。そのカテゴリーとは、量、質、関係、様相です。このうち、最初の3つがソフトウェアの基本的構造に対応します。UMLクラス構造、DOAの概念モデル、アーキテクチャなどです。最後の様相が役割や用途による基本構造に対するアプリケーション機能と言えるでしょう。たとえば、概念や知識には定量的な定義と定性的な定義が存在します。すべての概念や知識が、厳密な定義ができるのではなく、定性的な定義により、定義スコープが集合的に扱われる場合も存在します。これはドメイン工学におけるフィーチャ(特性)の定義にも現れます。むしろ、フィーチャの場合、現在では定性的定義が主流です。
「すべての存在は関係を持って認識される」、仏教の教えに従うまでもなく、我々の認識は相対的なものです。ですから、基本的構造に対して、関係のカテゴリーを持つことは普遍的です。関係には3つの種類が存在します。実体と属性、相互作用、因果です。クラスやエンティティなどの実体はインスタンスの所属関係を記述する関係の一種です。通常は、属性の発見が最初で、その所属関係の記述は後です。DOAでのエンティティタイプの定義、あるいは、オブジェクト指向分析での責務の配置はこの原理に従います。相互作用もUMLでのオブジェクト図やシーケンス図でおなじみです。が、因果という関係については、一階論理では表現できないのでUMLでは表現されません。UMLのような図形言語は隣接度の表現ができることが利点ですが、関係を命題とみたとき、なぜその関係が起こるのかといった命題の説明は、一階論理であるUMLモデルには加えるべきではありません。隣接関係のメリットがないこうした表現には、図形言語の利点はなくなります。ですので、DSL設計においてクラスベースの図形言語だけが有利ではないのです。
量、質、関係で作られた構造に対して、最後に作用するのが様相です。すべての事象や事実は100%の必然性があるか、あるいは、確率的なのかを表現するために、量、質、関係に対して様相を加えます。ソフトウェア工学での可変性、たとえば、アーキテクチャの拡張性、Dependency Injectionやアスペクトなどの複合化機構、ロールモデリングなどの分析設計手法などはこの思考のカテゴリーの様相にしたがった機構と見ることができます。
IT技術はビジネスに則っていることで、使いやすく保守しやすいよいアーキテクチャになります。そして、その根本には、人間の思考に従うという点が重要です。純粋理性批判は1781年に初版が発表されています。この哲学は300年以上の昔に発表されていたことに驚嘆させられます。基本的なアーキテクチャのミスの根本原因は300年以上も前に明確になっていたということです。
Software Factoriesのメタモデルに関連する技術には大きく3つの分類が存在します。
1つはPractical Software Factories in .NETに示される、開発プロセス上のメタモデルです。
これはIEEE 1471をベースとし、プロダクトライン開発におけるプロダクト開発のプロセスを記述します。
一般に開発方法論は、ロール、タスク、アセットで抽象的に記述可能であって、それをプロダクトライン開発に適用し、Software Factoriesスキーマとして定義しています。ここでは、マイクロソフト社はツールに基づき開発方法論を提供するので、それをかなり意識した構造となっています。
しかし、一般的なメタモデルという意味でいうと、これは開発者が求めるメタモデルではありません。というのは、方法論で利用するアーティファクトやワークプロダクト自体を定義するメタモデルの記述はカプセル化されていて、それらを利用し、あるいは、生成するためのプロセスについてのみ扱っているからである。開発者が求めるメタモデルは、アーティファクトを記述する属性やクラス構造の方であるからです。その点をこの本は表現できていません。
次に、UML 2.0のsuper structureのメタモデルは、従来のUMLの図や、その他のプロファイル拡張での構造と属性の意味を定義する汎用的な表現法の意味を定義します。数学でいうと、道具の定義をしている部分に相当します。したがって、これだけで方法論のメタモデルとして十分であるわけではありません。UMLを利用する方法論は無数にあってもよいわけで、したがって、UML標準としてはそうした特定の方法論に特化した表現をするわけにはいきません。Software FactoriesはUMLによるモデル駆動をを使わずDSLを使うのですが、DSLのモデル定義は、このUMLの汎用性の高いメタモデルを基盤とすることはできます。
しかし、実際の開発を進める段階で、どのモデル要素を基本として定義し、その基本要素が開発プロセス上、他のモデル要素とどう関連を持つかを表現できなければならなりません。そこには、開発段階の前提条件、開発手順、モデル要素間の影響や依存関係が含まれます。こうした関係こそが開発者が意識し、ツールがガイダンスすべき指針の基礎となります。このメタモデルは方法論に依存します。
つまり、メタモデルとしては、
(1) 開発プロセスのメタモデル
(2) 方法論に非依存なモデル要素記述のための基礎的な構造や属性を定義するメタモデル
(3) 方法論に依存し、実際にモデル要素を開発でどう操作するかを規定するメタモデル
(4) 補助的に、(3)で利用するDSLの抽象構文を(2)を使い定義するメタモデル
が存在します。特に、Software Factoriesスキーマで重要なのは(3)を規定するものでなくてはなりません。この部分こそがインパクト分析、すなわち、影響波及の範囲を制御し、また、モデルの分析や定義における基準を決め、方法論の根幹となります。
レイヤーアーキテクチャを語るための事前準備。
レイヤーという言葉は開発ライフサイクル管理の中では複数の箇所に登場します。
概念レベルの分析段階で使われるレイヤーは、概念モデルの分類、責務の配置に関して使われます。したがって、分類法の一種です。一方、レイヤーアーキテクチャは、非機能要求を論理的なノード上の配置で動作させるための実現法です。ここで、論理ノードとは必ずしも物理ノードを意味しないで、ノード上のシステムのバージョンや構成定義の条件を決めます。
概念モデルは分析を経て責務がそのモデル上に配置されます。次に概念モデルを適切な分割単位である、パッケージやコンポーネント上で動作させます。このパッケージやコンポーネントの構造がアーキテクチャです。レイヤーアーキテクチャのレイヤーはここでのパッケージの一種です。概念モデルの責務は、適切な基準の下で、パッケージやコンポーネント上に配置されます。この場合、責務を純粋に動作させるのはコンポーネントの方です。
この関係をわかりやすく示すと、
概念モデル→パッケージ(レイヤー)⊃コンポーネント⊃クラス。
パッケージはそれでは何のためにあるのでしょうか?
パッケージは多くの場合、モジュールといいかえてもいいです。その役割は、本質的には、変化の単位を示します。つまり、なんらかの理由でソフトウェアを変更しなければならない場合、同時に変更を行わないとつじつまが合わない範囲を示します。同時更新の単位は、多くの場合、配布の単位やバージョン管理の単位になるので、モジュールの形で実現されます。もっとも、パッケージは論理レベルのモデルですが、モジュールはそれを物理レベルで実現しているモデル(主にファイル)です。ですから、概念モデルの責務はコンポーネントに配置され、コンポーネントの変更の単位をパッケージがささえるという関係です。変更の単位は、適切に依存関係が整理されていないといけないので、コンポーネントを構成するクラスの依存関係からボトムアップに整理して決められるのが現実的と思えます。しかし、プラクティスとして存在する、参照アーキテクチャでは、データはUIより変更頻度が少ないという経験に基づいて、データやUIをレイヤーとしておおまかなパッケージのくくりを用意しています。この程度のおおまかなくくりであれば、ボトムアップの依存性分析をしなくても大体正しいということが言えるからです。
アーキテクチャは一般には非機能要求を実現するので、概念モデルを動作させる枠をパッケージやコンポーネントで提供しているといえます。しかし最近では、ビジネスアーキテクチャがソフトウェアを動作させる色彩が強くなっているので、ビジネスアーキテクチャが決める概念モデルがシステムアーキテクチャのパッケージやコンポーネントの枠を決めると考えることもできます。
非機能要求だけではないのです、システムアーキテクチャを決めるのは。
そうした発想は、Zachmanフレームワークでのビジネスのビューがアーキテクチャとしてとらえられている点や、
ドメインドリブン設計から読み取ることができます。
コンポーネントに責務が配置されると、そのコンポーネントの持つべきインターフェイス定義を決めることができます。
もちろん、責務とインターフェイス定義は双方で調整がされて、決定されます。責務を動作させる、最適な構造は何かという観点で。
ここで、インターフェイス定義は古典的には、事前事後条件や入出力で定義されるのですが、最近では、これにセキュリティやネットワーク呼び出しの有無、同期/非同期などの条件が加わります。これらは物理レベルの話ではなく、論理レベルの話であるので誤解なく。
こうして、アーキテクチャ設計が決定されます。
SOAにおけるサービスの実装法ではカプセル化の考え方から、どんな実装法でも選択が可能です。
多くの場合は、OOPを使い、インターフェイス定義を使ってクラスを実装するとともに、インターフェイス定義をWSDLで外部に公開します。
あるいは、Contract-Firstで開発する場合には、先に契約を定義して、実装法を後で選択します。
しかし、この場合でも主流の開発言語がOOPであることや、SOAで必要となる他の非機能要求の実現のために必要な機能を提供する実行環境がOOPのフレームワークで提供されているので、OOPが現実的な選択となります。こうしたフレームワークはたいていの場合、スケーラブルなアーキテクチャを想定するあまり、レイヤー構造となっています。たとえば、.NETの場合は、asmxやWCFを選択し、HTTP実行エンジンとしてはWebサーバ(UI層)、データ処理にはデータアクセス層を利用します。
以上の現状から、SOAの実装は、一部のレガシーラッピングを除き、レイヤーアーキテクチャを前提としているといっても過言ではありません。
さて、それではSOAの実行を最適にするアーキテクチャはレイヤーアーキテクチャかと言えるでしょうか?
答えはNoです。
Microsoft社が最初にCOM(Component Object Model)を出したとき、EXE形式のサーバとDLL形式のサーバを提供しました。それぞれ一長一短の特徴を持ちますが、ActiveXと呼ばれるコンポーネント技術が主流になるころには、ソフトウェア部品の提供形式はDLL形式に落ち着きました。
これはコンポーネントをモジュールとして配布しやすい理由もありますが、一番の理由は、僕はインプロセス呼び出しにより、呼び出し性能が優れている点にあると思っています。だだし、COMの場合は、スレッディングモデルという難解なルールがあるので、DLLでの開発は難しかったハンディがありました。
現在の.NETであっても、あるいは、Javaのような別の実行環境であっても、コンポーネントの配布や実行形式はインプロセスが有利なのです。これは、SOAの実装法でも同様です。
SOAのサービスはユースケースの実現と同様にレイヤーアーキテクチャを取るとレイヤー間での呼び出しやデータ転送が発生します。もし、レイヤーがアーキテクチャとしてプロセスをまたぐ物理階層だとしたら、前述のコンポーネントモデルの実績の歴史と矛盾します。あくまで、インプロセスでなければ、歴史が証明している進化と逆行してしまいます。
それでは、現在のSOAの実装法は正しくないのでしょうか。僕は正しいとは思えません。もう1度、SOAのためにレイヤーアーキテクチャではないシンプルなアーキテクチャを作るべきと考えます。今後のシステム開発がSOAを主流と考えるのならば、これは、現在のレイヤーアーキテクチャはもう主流ではないと言えるでしょう。
それではどうすべきでしょうか。ここでは、2つの問題があります。
- SOAPで転送されるメッセージは帳票の一種であって、複数データの組み合わせからなる半構造化データであること。したがって、そのメッセージを受信処理するアプリケーション(コンポーネントや他のサービスを含む)も1つとは限らず、一般には複数からなること。
- サービスによるシステムの分割は、特に更新系の場合、マスターデータの配置の問題を解決する必要があること。"one fact in one place"の原則から論理的に単一箇所で管理すべきマスターデータを、サービスに物理的にどのようにパーティショニングすべきかを考える必要があること。
この2つを同時に解決するための新しい設計法が必要です。
その設計法が有効となる、アーキテクチャを考えていくことになります。
次回に解説したいと思います。