一口に「アーキテクチャを構築する」といってもどこまでやればいいのでしょう?アーキテクチャでどこまで規定すればいいのでしょうか?これも絶対的な答えのないところです。
唯一の正解がないということがアーキテクチャを難しくしているのです。その点実装技術に関しては明確な答えがあるので理解もしやすいわけです。とぼやいても仕方ないので私なりの考えを述べます。
第一話 でお話したとおり目的を達成するためにはどうするべきかという考え方がここでは重要になってきます。つまり規定されたアーキテクチャに基づいて、デザイナー、プログラマーが効率よく開発が進められることが目的です。
あまりにも現実とかけ離れたものや、難しすぎるアーキテクチャを規定してしまうと、結果として誰もそれを参照しなくなりお蔵入りになるわけです。そういった例を数多く見てきました。
|
少し話しがずれますが、日常生活(家庭)において部屋の使い方などのルールが決められていることが多いと思います。例えば「ゴミは分別して燃える、燃えないゴミごとにこの袋に入れる」、「日用品はこのテーブルのこのケースに収納する」などありますが、ある程度のルールが決まっていると家族はそれに従ってさえいれば自然と部屋はすっきり整理されます。
しかしペットボトルを捨てる際にふたを取った後に残るリングも除去してからリサイクルゴミとして出すことなど細かすぎるルールだと、家族はついていけなくなります。
しかし家族の不平をすべて受け入れてしまうとそもそもルールとしての存在意義がなくなります。
|
|
なにが言いたいかというと、ルールの厳密さにもほどよいバランスというものがあるということです。アーキテクチャにも全く同じことが言えます。何事にも"落としどころ"というものがあります、非常に重要な事です。実際に使われないものに一生懸命労力を割いてももったいないですからね。
他の例としては汎用機時代に作成されたアーキテクチャ(開発標準と呼ばれていることも多々ありますが)を全社的に遵守せよという方針で未だに縛られているケースもあります。
もちろん原理・原則といった普遍的な部分は再利用することが可能ですが、第一話でアーキテクチャの構築には個々のプロジェクトの事情を十二分に考慮するべきとお話したとおり、分散環境に対応するにはそれに最適なアーキテクチャを考えなければなりません。以上をまとめると下記のことに留意する必要があります。
- 厳密すぎないこと(原理・原則は採用しつつ、現場で使いやすいものにする)
- アーキテクチャにも多様性がある
■アーキテクチャの厳密さ
アーキテクチャは厳密すぎると使いづらいし、ゆるゆるでは効果がありません。その間で最適なポイントになるように考慮する必要があります。そのポイントへの道しるべとしてアーキテクチャ原則(Principle)があります、原則は長年にわたって多くのアーキテクトが参考にしてきた事を明確化したものです。それを守ることで効率的に適切なアーキテクチャを確立することができます。
また話がずれますが、こういう原則を理解してアーキテクチャ(詳細設計・開発していく際のルール)を規定することで、設計者、開発者は自然と正しいシステム構築に誘導されるわけです。前半で日常生活におけるルールを例として挙げましたが、システム構築上の基本ルールがアーキテクチャで、それを作成するのがアーキテクトです。アーキテクチャ原則についてご興味のある方は以下のコラムをご覧ください。興味がなければ飛ばしていただいてけっこうです。
コラム
アーキテクチャの原則のうち代表的な以下のものについてご紹介いたします。
- 正規化
RDB における正規化というとみなさんすぐにお分かりだと思いますが、正規化は他のソフトウェア要素(クラス、コンポーネント、メッセージなど)にも適用される原則です。
「正規化」の意味は冗長性の排除と一貫性の確保です。ただRDBとは若干異なる部分があります。クラスを例に挙げると、クラスは単なるデータ構造ではなく、振る舞いも含まれています。
振る舞いとはデータベースの操作だけではなく、ビジネスルール、ビジネスプロセスという込み入った概念を扱うことです。これらの振る舞いをモデリングするには、どのクラスにどの機能(役割)を持たせるか、多数のクラス間の協調関係をどう実現するか、振る舞いの変更に対応するための仕組みをいかに達成するか、また機能横断的な処理をどのように扱えば効率がいいかなどなど考慮すべき項目がたくさんあります。
これらを解決するにはソフトウェア工学的なアプローチが求められます。この原則を守ることで得られる利点は以下のとおりです。
(1) 同じ機能は論理的に単一のロケーションに集約されるので、大規模なシステムをあるまとまった機能単位に分割でき、さらにそれらを組み合わせることでより大きな単位の機能セットを単一ロケーション(コンポーネントなど)に集約することができる。結果として保守性が大幅に向上す。
(2) クラスやコンポーネント間の関連(依存関係)が整理される。
- 非環状性
非環状性といえば、非環状性グラフ(DAG)で表現されたり、"Hollywood Principle"と呼ばれることもありますが、あまり難しく考える必要はありません。
平たくいうとコンポーネント間の依存関係が環状にならないようにするという意味です。環状とはC1とC2が互いに依存し合う(相手の機能を呼ばないと動作できない)という状態のことです。
当然ながら環状だと片一方の変更が相手に影響を与えることになってしまいます。C1,C2という簡単な例だとまだわかりやすいのですが、この原則はいたるところで考慮しなければなりません。
またコンポーネントにも粒度(言語クラスからビジネスコンポーネント、サービスまで)という概念があり、それぞれの段階で考慮することが求められます。簡単に表現すると「コンポーネント間の依存関係は一方向にするべき」ということです。
これを実現するためにはコンポーネントを決定する際に依存関係が環状にならないように留意する必要があるということです。意外とこの原則が守られていないケースが多々あります。この原則を守ることで得られる利点は以下のとおりです。
(1) 自律性が確保される
コンポーネント化や SOA を実現する上で最重要な目的が自律性の確保です。自律性という言葉自体が多様な意味を持っていますが、ここで言いたいのは外界との境界(インタフェース)が明確に定義されていて、内部構造は自由度を確保しているという意味です。
オブジェクト指向でもすでに採用されている考えですが、境界がどの程度明確かという観点でのレベルがあります。オブジェクト指向ではオブジェクトを呼び出すときの関数名と変数をインタフェースとして公開しているわけですが、大きなコンポーネントや
SOA でいうサービスになると、もっと広範囲でのインタフェースが規定されます。
例えば SOA では非機能要件やビジネス上の利用条件(例として24時間以内に返答せよ)をコントラクト(契約)という概念で互いに合意した上で相手を呼び出すという次元に到達しているわけです。つまり以下に示す粒度に応じてインタフェースとして規定すべきものの範囲も広がっていくわけです。
.gif)
(2) 依存関係が整理される
当然ながら依存関係が一方向であれば、あるコンポーネントを変更しても他のコンポーネントへは影響が及びません。これが相互に依存し合っていれば影響範囲がどこまで及ぶのかが不透明になってしまいます。
コンポーネントが2つであればなんとかなりますが、大規模なシステムとなると 500 - 1000 というコンポーネントが存在し、それらの依存関係が整理されていないと大変なことになります。メンテナンス不可能な状態に陥るわけです。
(1) と (2) をまとめると、自律性とは外部境界が明確なのでそれさえ変えなければ内部構造の自由度が確保されるということです。整理された依存関係とはその自律性の高いコンポーネント間の依存度を低くすることで、互いに独立した関係を保てるということです。次の原則でより明確にご説明いたします。
- 高凝集度かつ低結合度 (疎結合)
簡単にいうと互いに密接に関連する機能セットはぎゅっとまとめてコンポーネント化(サービス化)しましょう。そしてコンポーネント(サービス)同士の依存関係はなるべく少なくしましょうということです。例えば
UML のコラボレーション図などでクラスの関連を明確にし、互いに強い関連を持つ集団をコンポーネントとして切り出します。そうすることで凝集度が高くなり、他のコンポーネントとの結合度は低くなる(疎結合)わけです。すでにお分かりのように、ここでいうコンポーネントとはある程度の大きさを持った粒度の大きいコンポーネント(SOA
でいうとサービス)を意味しています。この原則を守ることで以下の利点を得ることが可能です。
(1) ビジネス要件があるコンポーネントに1:1で対応しているので、要件変化時にどのコンポーネントが影響を受けるのかすぐに判別できる。結果として迅速な修正が可能となる。
(2) あるコンポーネントを改変しても、他のコンポーネントに与える影響は最小限ですむ。結合度が低い(依存関係が弱い)からこそ実現できるわけです。
以上アーキテクチャの原則についてご紹介しましたが、これらを適用するために存在するのが Layers や MVC といったアーキテクチャパターンです。もっと細かいものとしてデザインパターンもあれば、ビジネスモデリング、データモデリングにもパターンがあります。
ご参考までに弊社は下記サイト(英語)にて世界中の開発者の方とパターンに関する情報共有を行っております。お時間のあるときにぜひご覧ください。
http://patternshare.org/
実際のプロジェクトに適用する際に原則同士が相反するケースも発生します。例えば正規化を達成しようとした結果、依存関係が増えて自律性が下がるという現象もあります。そういう問題をプロジェクトごとに解決して最適なアーキテクチャを確立しなければなりません。それがアーキテクトの役割です。
アーキテクトにはこれら原則を(とくにその目的を)深く理解してプロジェクト個別のアーキテクチャを構築することが求められます。そしてそのアーキテクチャを基本的な枠組みとして設計者・開発者に提供することが役割です。言い換えるとアーキテクト以外の役割をもった開発者はこのような原則を深く理解しなくてもよいという状態に環境を整えることがアーキテクトの任務なのです。
|
■ 現実的なアーキテクチャの構築
今回のタイトルである「現実的なアーキテクチャ」とは開発現場で使えるアーキテクチャという意味です。現場で使えるものにするためには最低限以下のことに留意しなければなりません。
- システム規模に応じてアーキテクチャは変わる
- アプリケーション種類に応じてアーキテクチャは変わる (LOB, SI, パッケージ)
- 開発メンバーに適したアーキテクチャが存在する (スキル、慣習)
- アーキテクチャは制約条件によって制限される
唯一絶対のアーキテクチャがないと言いましたが、以上のように条件に応じて多種多様なアーキテクチャを構築していく必要があります。普遍的な原則は再利用できますが、そこから先の最適化が重要です。それを実現するためには現実と向き合って一つ一つ課題を解決する地道な努力が求められます。ここではキーポイントをご紹介いたします。
ウォーターフォールや UP、アジャイルなど開発プロセスにも種類がありますが、アーキテクチャはプロセスの初期段階でほぼ確立しておかなければなりません。UP を例に挙げると方向付け、推敲フェーズまでにはアーキテクチャのほとんどの部分は確立されているべきという指針があります。なぜならそれ以降のプロセスは確立されたアーキテクチャに従って作業されるからです。
ではどのようにアーキテクチャを構築していくべきかという部分に焦点をあててみます。私は以下のキーポイントを守ることが重要だと考えております。
「大量にある要件のうち最重要な部分から始める」
ある程度経験を積むと、要件リストのうちどの部分がクリティカルかの判断がつくようになります。クリティカルな部分は全体の10%程度だといわれております。この 10
% の部分がきちんと動作することを確かめつつアーキテクチャを詳細化していくことが大切です。
例えば非機能要件としてインフラストラクチャが古くて制約が大きい場合、ユースケースのうち性能要件の一番厳しい部分を取り上げてそれが動作するか確かめるということや、機能要件に関しては、ざっとモジュール分割をした上で依存される度合いが強いモジュールが動作することを確認するということです。一旦それらがクリアされれば他の要件に関しては問題が発生する可能性が低くなります。インクリメンタルなアプローチとはこういうことを意味します。
「全体を見て個別最適を図る。それを繰り返す」
抽象化・最適化の繰り返しが重要です。さらには問題が発生した時点で即座にアーキテクチャへとフィードバックしていく必要があります。これをイテレーティブ(繰り返し型)と呼んでいます。しかもアジャイルな姿勢が重要です。
昨年とある著名なアーキテクトとお話をする機会がありましたが、その方はアジリティとイテレーションこそがキーポイントだとおっしゃっていました。私もそれに関しては同感です。ただし定期的に全体を見渡すということも非常に重要です。特定ユースケースに依存し過ぎていないか、ビジネス要件が満たせる仕組みになっているか、設計や実装に無理がかかっていないかということを常に確認してください。
アーキテクトの方は例えれば鳥のようなもので高度1000メートルから見ることもあり、地上10メートルに降りて確認することもあるわけです。ただ高度1万メートルだと高すぎてすべてがぼやけてしか見えませんし、地上に降りてしまうとごく一部しか見えません。アーキテクチャには適度なバランスが必要といいましたが、それはそのままアーキテクトの持つべき観点・守備範囲にも当てはまるのです。
「変化する部分と変化しない部分を切り分ける」
機能、データ、要件どれをとっても変化の頻度がまちまちです。例えば SOA ではビジネスプロセスに着目しますが、プロセスは変化の度合いが大きいものです、しかしプロセスが呼び出す機能自体はそれほど変化しません。クレジット認証処理がしょっちゅう変わるとは思えません。
データに関しても同じことが言えます。顧客や商品といったマスターデータはそのスキーマ(構造)が大きく変化することはありませんが、マスタ以外のデータ例えば発注書のフォーマットは取引相手によって容易に変わるわけです。変化しやすい部分を予め特定しておいて、その変化にいかに迅速に対応できるかという仕組みをアーキテクチャとして取り込んでおくことが重要です。
「共通部分と個別部分を切り分ける」
例えば構成情報などをファイルとして管理し、各モジュールは実行時にそれを参照することでシステム全体の共通部分(構成情報)の同期をとるという事はみなさん実現されていると思います。それと同様にシステム全体にわたる共通部分を切り出して一元管理するということは運用時をはじめシステムの保守性・柔軟性を高めます。アスペクト指向などの試みも同じ目的です。
実現方法としては構成ファイル、アスペクト指向その他共有コンポーネントなどいろいろとありますが、肝心なのは、どの部分が共通なのかということを識別するということです。目的は動的な変更を可能にすることや保守性を大幅に高めることです。
「数値化された指針を示す」
前回アーキテクチャとして規定すべき項目をご紹介しましたが、それを正しく使ってもらうためには数値化された指針が大切です。数字は誰が見ても同じ意味として理解できるコミュニケーション言語です。
例えばコンポーネント指向で開発するとしましょう。その際コンポーネントの粒度が重要になってきます。これをコンポーネント数の適切な範囲として表せばすべての関係者間で共有できます。コンポーネント指向では
3 つの粒度を持っています。下図に示します。これが絶対的に正しいという保証はありませんがこのような形で数値化された指針を示すことが重要だと考えています。
|
コンポーネント粒度
|
意味
|
指針となる数値
|
|
システムレベル コンポーネント
|
ビジネス コンポーネントを組み合わせて一つのシステムを実現するもの
|
平均 10-20 個のビジネス コンポーネントにより構成される。大規模システムでは10-30個のシステムレベル コンポーネントが存在する
|
|
ビジネス コンポーネント
|
ビジネス概念を実装したもの
ビジネス エンティティやビジネス プロセス
|
ビジネス ドメインごとに平均 30-50 個のビジネス コンポーネントが存在する
|
|
分散コンポーネント
|
.NET クラスライブラリ (アセンブリ)
|
平均 10 個の言語クラスから構成される
|
■まとめ
第一話でアーキテクチャの役割とは「複雑さの管理と変化への対応」と述べましたが、今回の内容を読んでいただいて、アーキテクチャの役割とは依存関係の整理や機能の正規化により複雑なシステムを単純なものの組み合わせに変換すること、非環状性、適切な結合度により要件や実行環境などの変化にすばやく対応できることであると理解していただければ嬉しい限りです。
第一話、二話を通じてアーキテクチャのオーバービューをご紹介しました。次回以降はサービス指向やモデル駆動など旬を迎えつつあるトピックについてご紹介いたします。引き続きお付き合いいただければ幸いです。
Top
of Page