BT

最新技術を追い求めるデベロッパのための情報コミュニティ

寄稿

Topics

地域を選ぶ

InfoQ ホームページ アーティクル 実証済みのアイデアの融合: S#arp Architectureの裏側

実証済みのアイデアの融合: S#arp Architectureの裏側

ブックマーク

原文(投稿日:2009/3/16)へのリンク

Web用のアプリケーションを記述することは骨の折れる作業です。AJAXスクリプトの作成とテストから、ステートレス環境での状態のシミュレートまで、Webベースのアプリケーションをプログラミングすることは、計画と開発の全段階で慎重な注意を要する業務です。また、さらに問題を悪化させることとして、Web開発者は、オブジェクト/リレーショナルインピーダンスミスマッチに取り組むことや、生産性を向上させるために利用できる無数の選択肢の中で最も適切なツールセットを選択することや、コードの長期的な保守性を確保しつつ生産に迅速にソリューションをもたらすために適切なアーキテクチャをプロジェクトに注入することなど、典型的な開発の苦闘に直面しています。

最近の、そしていまだ発展中の技術と手法は、こうした開発の課題に進歩的に対処していますが、単独で銀の弾(特効薬)となるものは1つもありません。しかし、様々な、慎重に選択された技術と手法の強みを活用することによって、品質を犠牲にせずに生産性と保守性における多大な利益を実現できます。この記事では、Web開発における多数の成熟傾向と、クライアントに価値を提供することに対するそれらのメリット、およびS#arp Architecture(最善の手法と技術を活用しようとするASP.NET MVCをベースとしたフレームワーク)内でのそれらの使用について取り上げます。

本格化した動向

ソフトウェア開発業界を表現できる1つの単語があるとすれば、それは「変化」です。我々の業界は、土木工学などのより確立した専門分野と比べると、かなり若くて非常に未熟です。我々が通過しているこの成長期の最も明白な副作用は、業界が経験している変化量であり、しばらくの間経験し続けることになりそうです。

そのような不安定さの例として、プロジェクト方法論がスターの座にのし上がってから、失敗した痛みを伴う管理の実験として忘れ去られてしまうことの速さがあります。別の例は、新しい代替手段が前の手段のメリットに取って代わるときの手法と技術の盛衰です。たとえば、ASP.NETの世界におけるModel View Presenter (MVP)パターンを例に挙げます。このデザインパターンは、ASP.NETの世界に優れたテスタビリティ(テストしやすさ)を導入するために利用された手法でしたが、複雑さを犠牲にしていました。最近、Microsoftは、ASP.NETに代わる手段としてASP.NET MVCを導入しました。これは、ASP.NETでこれまで利用できなかった新しいレベルのテスタビリティを実現する代替手段です。その結果、ASP.NET MVCは、たいてい最終的にASP.NETページのコードビハインドになっていたコントローラロジックのテスタビリティに関して.NET Web開発内でMVPパターンを時代遅れなものにしました。これは、MVPの裏の原理が無効だと言っているわけではありません。単に、適切な関心の分離(separation of concerns)によりMVPのテスタビリティの目標をより良く実証する技術が登場したということだけです。

ソフトウェア業界は、劇的な変化を経験し続けていますが、高品質で保守しやすいプロジェクトの開発と提供の基盤となった特定の傾向と理想があります。これらのアイデアの実装方法は時間とともに変化する可能性があるのに対し、アイデア自体は、ソフトウェア開発に後々まで影響を与えると思われる成功したソフトウェアの強力な基盤に相当します。以下は、開発コミュニティ内での支持で本格化し、結果的にソフトウェア開発の将来に大きな影響を与えるであろうアイデアの簡単なレビューです。

インフラストラクチャの抽象化

私が、家を塗り替えなければならないのと同じくらい不吉な予測を胸に新しいオブジェクトのCRUD機能の記述を恐れていたのはそれほど大昔ではありません。それは、誤りに対する多くの修正で満たされた、余分の退屈な時間の長い運動でした。ストアドプロシージャおよびADO.NETアダプタの記述から、脆弱なJavaScript検証のテストまで、私は1日の大半がインフラストラクチャの詳細を結び付けて、一度記述したら二度とコードに触れる必要がないように望むことで満ちていると感じていました。

インフラストラクチャの詳細において、うんざりするほど退屈なタスクは専用ユーティリティに任せた方がよいと見なされるなど、パラダイムシフトはこの10年で十分に成長しました。課題は、ソフトウェアがインフラストラクチャの機能性の利用を促進する一方で実装の詳細を知らないようにする仕事に適したツールを見つけることです。NHibernateはそのようなツールの好例です。NHibernateは、永続的なプレーンな.NETオブジェクトから基調のリレーショナルデータベースへとその逆方向を扱います。NHibernateは、オブジェクト自体がオブジェクト/リレーショナルインピーダンスミスマッチに対処する一方でどのような永続性が実際に生じるかを知らないままにするような方法でそれを行います。また、ADO.NETやストアドプロシージャのコーディングを一行も必要としません。NHibernateは素晴らしいツールですが、何よりもこれは、開発活動の重要な部分を消費していたうんざりするほど退屈で脆弱なインフラストラクチャの機能性を隠すための強固なソリューションを提供する、非常に高い目標の実現を意味しています。

時が経つにつれて、ソフトウェア業界は、ますます増加する退屈なインフラストラクチャの関心を開発の結果論として追いやるように抽象化する、さらに多くの成熟ツールと手法を知るようになるでしょう。たとえば、NHibernateの成熟化により、自動マッピング機能の付いたFluent NHibernateなどの追加アドオンは、データアクセス管理の管理負担をさらに軽減することをすでに実現しています。『Godel, Escher, Bach(ゲーデル、エッシャー、バッハ)』でのDouglas Hofstadter氏の予言的な文章に描写されているように、適切な抽象化の導入はソフトウェア開発の自然な成り行きであり、適宜に奨励されるべきです。

疎結合

レガシーソフトウェアの一般的な破滅の元は密結合です。(ここで私は、あなたが別の開発者、あるいは若い頃の自分自身からしぶしぶ導入した可能性があるソフトウェアを説明するために、不公平に屈辱的な方法で「レガシー」という言葉を使用します。)密結合の例としては、2つのオブジェクト間の双方向の依存関係、データアクセスオブジェクトなどのサービスへの具体的な依存関係を持つオブジェクト、そして、すべてのサービスをオンラインで利用可能にせずにサービス依存のコードの動作をテストできない単体テストなどがあります。密結合は、脆弱なコード、テストが困難なコード、修正を行うタスクに直面するとすべての開発者が逃げ隠れしてしまうようなコードをもたらします。これを踏まえて、成功したソフトウェアの鍵は疎結合の特性であることはほぼ格言的になりました。

Wikipediaは、疎結合を「2台以上のシステム間の弾力性のある関係」と適切に表現しています。したがって、疎結合のプラス面の効果は、プログラムの関係の一側面を他側面に悪影響を与えずに変更できるということです。たとえば、Separated Interface(インターフェイスの分離)、別名Dependency Inversion(依存関係逆転)Dependency Injection(依存関係注入)と混同してはならない)よりも疎結合の理想をより良く例証するデザインパターンは、私の意見では、他にはありません。この手法は多くの場合、データアクセス層をドメイン層からきっちりと分離するために使用されます。

たとえば、MVCアプリケーション内のコントローラまたはアプリケーションサービスが、データベースから多数のアイテムを取り出すためにデータアクセスリポジトリオブジェクトと通信する必要があると仮定してください。(リポジトリはこの場合「サービス」です。)この要件を解決するために最もシンプルなソリューションは、コントローラにリポジトリ自体の新しいインスタンスを作成させることです。つまり、コントローラは、たとえば新しいキーワードを介して、リポジトリへの具体的な依存関係を作成します。残念ながら、このアプローチはそれと共に次のような密結合の多数の悪影響をもたらします。

  • リポジトリのクエリをサポートするライブデータベースを持たずにコントローラの単体テストを行うことは困難です。また、このライブデータベースの要件は、何らかのデータが以前に実行されたテストによって修正された状態のまま残されていると、脆弱な単体テストを引き起こします。コントローラロジックをテストする際、あなたは、そのコントローラが依存しているリポジトリがデータベースとうまく通信できるかどうかではなく、主としてコントローラの動作を検証することに関心を持ちます。また、「ライブサービス」、すなわちこの場合、ライブデータベースと通信するリポジトリを使ってテストすると、単体テストのパフォーマンスは遅くなります。その結果、開発者は単体テストの実行を中止し、品質が劣化します。
  • サービス(リポジトリ)の実装の詳細を、サービスのインスタンスを作成するコントローラを変更する必要なくスワップアウトすることは困難です。Webサービスを支持して、たとえばADO.NETを使用してリポジトリをスイッチアウトしたいと仮定してください。前のものに具体的な依存関係を持っていると、そのインスタンスを作成して使用するコントローラに多くの修正を行わずに後のものに切り替えることはより困難になります。多くの場合、これは変化をもたらしつつショットガン手術(shotgun surgery)を引き起こす可能性があります。新たな問題につながる兆候です。
  • コントローラが実際にいくつのサービス依存関係を持つかが不明です。言い換えると、コントローラが多数のサービス依存関係の作成を呼び出している場合、開発者にとってそのコントローラの論理的な境界、または責任範囲を突き止めることは困難です。あるいは、コントローラの依存関係がコンストラクタによって渡されたなら、開発者にとってコントローラの全体的な責任範囲を理解することはより簡単だったでしょう。

また、コントローラはそのサービス依存関係をパラメータとしてコンストラクタに与えることができます。そうしている間の主な改善点は、コントローラが、具体的な実装自体ではなく、サービス依存関係のインターフェイスだけを認識するということです。さらに詳しく例示するため、ASP.NET MVCアプリケーションの中におけるコントローラの次の2つのコード抜粋を比較してください。

以下のコントローラは、直接そのサービス依存関係、CustomerRepositoryを作成し、それに対ししかるべく具体的な依存関係を持ちます。

public class CustomerController {

   public CustomerController() {}

   public ActionResult Index() {

      CustomerRepository customerRepository = new CustomerRepository();

      return View(customerRepository.GetAll());

   }

}

反対に、以下のコントローラは、そのサービス依存関係をインタフェースパラメータとしてコンストラクタに与えます。これは、サービス依存関係に疎結合されると言われています。

public class CustomerController {

   public CustomerController(ICustomerRepository customerRepository) {

      this.customerRepository = customerRepository;

   }

   public ActionResult Index() {

      return View(customerRepository.GetAll());

   }

   private ICustomerRepository customerRepository;

}

密結合の欠点と比べると、このクリーンな分離はそれと共に次のような多くのメリットをもたらします。

  • " ドメイン層は、公表しているインターフェイスを除いて、リポジトリの作成方法およびリポジトリの実装の詳細に関して無知の喜びの中にとどまります。そのため、コントローラ自体を変更する必要なくデータアクセスの実装の詳細を(たとえば、ADO.NETからWebサービスへ)スイッチアウトすることが容易です。これは、交換したものが前のものと同じインターフェイスを実装することを前提とします。
  • " 具体的な実装ではなく、インターフェイスに依存関係を持つと、単体テストを行っている間にテストダブルリポジトリを注入することがより容易になります。これは、単体テストを超高速に保ち、データベース内のテストデータを維持する必要性を排除し、データベースと統合することではなくコントローラの動作をテストすることに焦点を置きます。

ここに詳述していないテーマは、インターフェイスの分離およびその他の疎結合の手法をサポートするのに必要な依存関係注入についてです。この件に関するさらなる詳細については、「Dependency Injection for Loose Coupling」という記事の中で論じています。(その記事に記載されている「モック」オブジェクトは、実際は「スタブ」であることに注意してください。これとその他の「テストダブル」の用語については、「Mocks Aren’t Stubs」でMartin Fowler氏によって記載されています。)最後に、インターフェイスの分離のデザインパターンへの移行を詳述している役立つ記事はというと、「Refactoring Service Dependencies to Separated Interface」です。

テスト駆動開発

簡単に言うと、テスト駆動開発(TDD)は、それなしで開発するよりも、高品質で保守しやすいソフトウェアを実現して、結果的に設計全体をよりシンプルなものにする取り組みです。テスト駆動開発は決して、単に開発手法の一時的な流行なのではありません。これは、成功したソフトウェア開発の極めて重要な面としてますます広く受け入れられるようになっている手法であり、我々の業界の成熟化を通して長期にわたり続くと思われる手法です。

TDDの裏にある基本的な概念は、開発中のシステムに質問を投げかけることから開発努力を開始することです。たとえば、バンキングアプリケーションを開発している場合は、顧客からの預金を問題なく処理できるかどうかシステムに質問したいかもしれません。鍵となるのは、行動の実装の詳細が実装される前に質問を投げかけるということです。このメリットは、システムが実際に記述される前に、システムの望ましい行動を検討することに焦点を置くことです。

たとえば、テスト駆動開発の指針に従った一般的なコーディング手順は次のとおりです。

  1. ターゲットのオブジェクトと望ましい動作がすでに存在するかのようにテストを記述する。
  2. ソリューションをコンパイルしてコンパイルの失敗を確認する。
  3. コンパイルに必要な最小限のコードを記述する。
  4. 単体テストを実行して失敗するかどうか確認する。
  5. 単体テストがパスするような最小限のコードを記述する。
  6. 5. 必要に応じてリファクタリングする!

テスト駆動開発が浸透する一方で、日々の開発努力におけるその使用法は今もなお発展しています。たとえば、テスト駆動開発の最近の傾向は、ビヘイビア駆動開発として知られています。このアプローチは、「コードを記述する技術言語とビジネスで話されるドメイン言語との間」のギャップを埋めようと試みます。言い換えれば、ビヘイビア駆動開発は、TDDに、次に論じるドメイン駆動設計(またはお望みの場合はその反対)を注入します。

ドメイン駆動設計

本格化している最近の傾向に関して、私は、ドメイン駆動設計(DDD)を含めなくてはならないように感じます。ソフトウェア開発へのこのアプローチは、技術的ソリューションのサポートに必要なテクノロジーとリレーショナルデータベースモデルではなく、ドメインとドメインロジックに真っ向から焦点を当てています。ビヘイビア駆動設計と同様に、DDDは、開発チームの言語と取り組みをクライアントの言語と取り組みによりよく同調させるための多くの手法とパターンを提案します。理想としては、クライアントは、DDDアプリケーションのドメイン層を読み取ることができ、コーディングロジック自体の中に示されている自分たちのビジネスを強力に反映したものを確認できるべきです。

様々なアプローチの私自身の経験では、私はドメイン駆動設計を、モデル駆動開発などの初期のプログラム的アプローチの自然進化と見なしています。モデル駆動開発では、データベース内のデータ、およびそれに対応するモデルはアプリケーションの中核として見なされ、その他すべてのことは単にそのデータを操作するために行われます。(Castle ActiveRecordADO.NET Entity Frameworkのどちらも、強固なモデル駆動設計ユーティリティの好例です。)反対に、DDDでは、データベースはドメインおよび関連ロジックのサポートに必要なインフラストラクチャの詳細として見なされます。事実、ちょうど映画マトリックスの「スプーンはない(there is no spoon)」というのと同じ様に、DDDにデータベースはないのです。明らかにデータベースはあるのですが、ポイントは、ドメインが、データの蓄積と検索の基調メカニズムがどのように実装されているかに関して、幸せな「永続化無知(persistence ignorance)」の状態であり続けようとすることです。

しかし、ドメイン駆動設計は、単にドメインからデータ永続化の関心を分離するだけのものではありません。DDDの主要テナントは、ドメインオブジェクト自体の中にドメインの動作を置いています。たとえば、CustomerAccountが予定どおりに決済されているかどうかを判断するために個別のCustomerAccountLogicクラスを持つのではなく、単にこの情報についてCustomerAccount自体に尋ねます。このように、ドメインの動作はモデル自体に埋め込まれています。

上記は、ソフトウェア開発への急速に成熟しているDDDアプローチからのアイデアのごく小さな見本です。ドメイン駆動設計に関する詳細については、Eric Evans氏の有名な本『Domain-Driven Design』の要約である「Domain-Driven Design Quickly」をお読みください。

これらのアイデアとS#arp Architectureとの融合

どんなプロジェクトにも固有のニーズがあり、そして、フリーサイズを提供するフレームワークは存在しませんが、ほぼすべてのWebベースアプリケーションの開発時に現れる課題と機会があります。しかし、非常に多くのオプションが利用可能なため、どのツールセットや手法が特定のプロジェクトに適していて、非常に一般的に直面する開発課題にきちんと対処するのに適しているかを決定することは、開発者にとって困難です。たとえば、.NET依存関係注入ツール - Inversion of Control(IoC; 制御の反転)コンテナ - を探している場合、Spring.NET(単なるIoCユーティリティをはるかに超えた存在)、UnityCastle WindsorNinjectStructureMap、その他多数から選ぶことができます。そして、それは単にIoCの選択なのです! さらに困難なことに、柔軟性を抑制することなく選択したツールと手法のメリットを利用するアーキテクチャを実現するための、最も適した賢明な計画量を決めることは骨の折れる試みです。

現在少なくとも.NET Web開発の世界で欠けていることは、オープンソースコミュニティによって開発された高品質ツールの有用性を考慮に入れながら、実証済みのプラクティスに基づく最新の技術を使用するための、最善の技術と手法を組み合わせたアプリケーション開発の共通のアーキテクチャと基盤です。S#arp Architectureはこの必要性に対応するものです。オープンソースのS#arp Architectureは、高レベルの品質と保守性を可能にしつつ、開発の生産性を向上させるために慎重に選択されたツールによって、本記事で説明している実証済みのプラクティスを利用しようと試みます。

S#arp Architectureによって利用されるツールと手法の見本は次のとおりです。

この見本は、銀の弾(特効薬)は存在しないが、適切なツールとのペアで強固な開発プラクティスを選択することから大きな価値を見つけることができるということを証明する役目を果たしています。

一つにまとめ上げるドメイン駆動アーキテクチャ

私は、S#arp Architecture内にカプセル化された主要なアイデアは、ドメインとデータアクセス層との関係を反転させる手法であると感じています。一般的なアプリケーションアーキテクチャ、特にMicrosoftの勧告に従うものでは、依存関係のフローはプレゼンテーション層から始まります。次にプレゼンテーション層はビジネス層に依存し、ビジネス層は最終的にデータ層に依存します。これは実装の詳細を単純化し過ぎですが、一般に、データ層は他のすべての層が依存する最下層であることが示唆されます。これはまさに設計によるモデル駆動です。

モデル駆動のアプローチには確かにメリットがあり、多くの状況で非常に適していますが、本記事が提示したドメイン駆動目標と一致していません。ドメインをデータ層に直接依存させると、ドメインオブジェクトとデータアクセスコード間の双方向の依存関係が生じるという別の欠点が頻繁に生じます。私の恩師の教授であるLang氏は、我々は予想されるすべての誤りを犯さなければその分野のエキスパートとはいえないと言っていました。私は現在もエキスパートになる努力をしていますが、ドメインオブジェクトとデータアクセスコード間の双方向の依存関係はトラブルによって成熟していることを学びました。(これは、私をエキスパートになることに一歩近づけた、苦労して学んだ教訓でした。)

では、どのようにこの難問を乗り越えて、これらのアイデアを反映するクリーンな設計を見つけ出すのでしょうか? その解決法は、様々なアプリケーションの関心をきちんと分離することと、ドメインとデータアクセス層の関係を反転させることです。実装するデータアクセス層の実装のために、ドメイン層で定義された、インターフェイスの分離を使用します。このように、アプリケーションのすべての層が実装の詳細について無知なまま、データアクセス(またはその他の外部サービス)層のインターフェイスのみに依存できます。これは、プロジェクト開発の保守段階時に、より疎結合して、単体テストがしやすく、より安定した設計につながります。

以下の図は、S#arp Architectureで支持されるアーキテクチャを説明し、インターフェイスの分離パターンを使用してドメインとデータアクセス層の間の従来の依存関係を反転させる手法を例示しています。各ボックスは、個別の物理的アセンブリを表しており、しかるべく依存関係の方向が示されています。

ddd

この図で、リポジトリの具体的な実装の詳細を定義するデータ層が、コアドメイン層に依存していることに注意してください。コア層は、ドメインモデルとロジックを定義するほか、リポジトリインターフェイスを定義します。このリポジトリインターフェイスは、実装の詳細から分離されたままリポジトリと通信するアプリケーションサービス層などの、他の様々な層によって利用される可能性があります。プレゼンテーション層 - 上記の図のYourProject.Web - はドメイン層に直接の依存関係を持つべきでないと示唆する人もいるでしょう。あるいは、データ転送オブジェクト(DTO)層を、データがプレゼンテーションのビューに渡されるときにプレゼンテーション層からドメイン層をよりよく隔離するために導入できます。

結論

結論は、ソフトウェア提供スペシャリストとしての我々の仕事は、高レベルの品質と保守性に固執しつつタイムリーにクライアントのニーズを満たすソリューションを提供することであるということです。我々の前に習得された何十年もの実証済みのプラクティスがあるのですから、データ永続化や単体テストなどの非常に頻繁な開発課題に対処する基本ツールの実装と実証済みの設計パターンに関して、車輪の再発明をする必要はまったくありません。真の課題は、予期していないニーズに創造的な方法で柔軟に対応できる能力を抑制してしまうことなく生産性を向上させるためのプレファクタリングと賢明な計画の適切量を提示することにあります。1000通りの解決法があるが、たいていは、我々より前にソフトウェア巨大企業が習得した知恵と教訓に基づく強固なスターティングポイントに立つことが理にかなっているということを明らかにするために、この記事で紹介した手法とツール、ならびにS#arp Architecture内でのそれらの集合がお役に立てばと思います。

さらに詳しく学ぶには

S#arp Architectureは、1年近く開発中であり、強固なドメイン駆動アプリケーションの迅速な開発のためのますますシンプルでより強力なアーキテクチャ基盤になるよう発展し続けています。S#arp Architectureのリリース候補をhttp://code.google.com/p/sharp-architectureからダウンロードすることができます。1.0 GAリリースは、ASP.NET MVC 1.0のリリースにすぐに続くと予想されます。S#arp Architectureディスカッションフォーラムにて、皆様の情報と経験を大歓迎いたします。

著者について

http://devlicio.us

Billy McCafferty氏は、長年開発者であり、美しいソフトウェアを記述することに関しては満足することのない情熱家です。Billy氏は現在、Codaiとして知られる小規模のトレーニングおよびコンサルティング会社(近々新しいWebサイトを公開予定)の運営を手助けすることと、Parsons Brinckerhoffで主任開発者兼アーキテクトの役割を果たすことの二重生活を送っています。Billy氏が自分の生活を取り戻した後(S#arp Architecture 1.0のリリース後になるはずである)、すぐにALT.NETおよびその他の開発カンファレンスで彼に会えることを期待してください。

この記事に星をつける

おすすめ度
スタイル

BT