分散オブジェクト・アーキテクチャ/ドメイン指向アーキテクチャ
これから述べる分散オブジェクト・アーキテクチャに近いフレームワークやアプリケーションの実装がすでにあれば教えて頂けると助かります。
(凡例:追記と削除)
目的
ドメイン駆動設計(DDD)を原理的に突き詰めたアーキテクチャによって、オブジェクト指向プログラミングの作業から、インフラストラクチャ層に関する配慮がほとんど不要になる。SQLもデータスキーマ設計も不要になる。
といっても、Ruby on RailsのActiveRecord的な話ではないので、誤解なきよう。むしろ正反対。リレーショナル・データベースを使わない。
ドメインのオブジェクトをシンプルに書けばシステムが実現するようになる。一言で言えば、「GOOS/DDD原理主義者のためのフレームワークが欲しい」という話。
ウェブ・アプリケーションの三層アーキテクチャ再考
ウェブ・アプリケーションについて、
を対応付けると:
インフラストラクチャ | 三層アーキテクチャ | DDD四層 |
---|---|---|
ブラウザ(ユーザー・エージェント) | プレゼンテーション層 | ユーザー・インターフェイス層 |
ウェブサーバー | プレゼンテーション層/アプリケーション層 | ユーザー・インターフェイス層/アプリケーション層/ドメイン層 |
データベース・サーバー | データ層 | インフラストラクチャ層 |
例外もあるだろうが、私の知る範囲では、これが多い。
このアーキテクチャでシステムをスケール・アウトするには、ウェブサーバーを増やす。基本的な設計では、データベース・サーバーを増やせず、ボトルネックになる。特別な技術を用いれば、データベース・サーバーをスケール・アウトできるが、データベース設計を変える必要がある。(データベース設計を変えずにスケールアウトする手法があれば教えて頂けると助かります)
さて、違うアーキテクチャも考えられる。
RPCで連携する分散オブジェクト・アーキテクチャだ:
インフラストラクチャ | DDD四層 |
---|---|
ブラウザ(ユーザー・エージェント) | ユーザー・インターフェイス層 |
ウェブサーバー | ユーザー・インターフェイス層/インフラストラクチャ層 |
オブジェクト・サーバー | アプリケーション層/ドメイン層/インフラストラクチャ層 |
相違点
三層アーキテクチャでは多数のアプリケーション・サーバーが、(一つないし少数の)データベース・サーバーとTCPインターフェイスで疎結合されている。
データとアプリケーション・ロジックが、ネットワーク上に分散配置されている。
それに対して、分散オブジェクト・アーキテクチャは大きく異なる。
分散オブジェクト・アーキテクチャでは、データとアプリケーション・ロジックが物理的にカップリングされる。
データと処理が、オブジェクトとしてオブジェクト・サーバー上でカプセル化されるのだ。
オブジェクト指向のコンセプトを、そのまま物理的配置に対応させるアーキテクチャだと言える。
この観点からは、三層アーキテクチャでは、データと処理が物理的にカプセル化されていないことになる。
分散オブジェクト・アーキテクチャの動作原理
ウェブサーバーは、HTTPリクエストを適切なオブジェクトへ転送する。つまり、ルーティング処理を担当する。もちろん静的コンテンツは自らレスポンスする。それもルーティングの一種だから、ウェブサーバーはルーティングに特化する。
また、ユーザー・インターフェイス層の担い手として、HTMLやJSONを生成することも、ウェブ・サーバーの仕事だ。
ウェブサーバーがメッセージを送る先は、オブジェクト・サーバーだ。
ウェブサーバーからのメッセージを受け取ったオブジェクトがアプリケーション・ロジックを処理する。
スケールアウトするには、ウェブサーバーとオブジェクト・サーバーをそれぞれ増やす。ウェブサーバーとオブジェクト・サーバーは、一対一で対応しないので、それぞれ任意に拡張できる。
オブジェクト間の通信はTCPベースのRPCで行われる。HTTPベースのWeb APIかもしれない。
オブジェクトの物理的配置
どのオブジェクトが、どのオブジェクト・サーバーに配置されるか。これはパフォーマンスを大きく左右する。もちろん、サーバー間RPCを少なくする配置ほど、高いパフォーマンスになる。
その配置を人間が設定するかもしれないし、自動化されたログ分析と配置ポリシーによって動的に再配置されるかもしれない。
十分にインテリジェントなオブジェクト配置アルゴリズムは、「同じクラスのオブジェクトを集める」(RDBのテーブル的な配置)のではなく、「よく会話するオブジェクトを集める」だろう。オブジェクト間コミュニケーション指向の配置アルゴリズムだ。
分散オブジェクト・アーキテクチャのプログラミング・パラダイム
三層アーキテクチャなら、あるクラス(テーブル)のオブジェクト(レコード)全部に対する一括集計が用意にできる。リレーショナル・データベースでSQLを実行するだけだ。
例えば、
SELECT TOTAL(balance) FROM customers WHERE balance >= 1000000
とすれば、口座残高100万円以上の顧客の口座残高を合計することができる。
しかし、ここではオブジェクト指向パラダイムにSQLという異物が混じっている。
分散オブジェクト・アーキテクチャでは、次のようなコードになるだろう。
Balance rangeMinimum = new Balance(1000000);
Balace total = Customer.filterCustomers(function (customer){
customer.balance() >= rangeMinimum
}).map(function (customer){
customer.balance()
}).sum();
不変の値オブジェクト(Value Object)を用いて、副作用のない集計処理を書いている。このようなコードは、言語やフレームワークの機能によってスレッドセーフに並列処理できる。
オブジェクトが複数のオブジェクト・サーバーに物理的に分散しており、実際の処理にはRPCが必要だという事情も、フレームワークによって完璧に隠蔽される。コードには一切現れない。並列処理したければ、副作用のないコードを書けばいいだけだ。
むしろ、次のように言うことができる。
関数型オブジェクト指向パラダイムでコーディングしていれば、並列分散処理可能なコードになる。スケールアウトはインフラストラクチャ層に丸投げできる。そして、それは正しい。スケールアウトはインフラストラクチャ層の関心事でしかないからだ。
そのためにアプリケーション層やドメイン層のプログラミングで工夫すべき点は、それほど多くない。基本的には素直に書けばいい。
最大のポイントは、副作用のないコードを書くことだ。それは関数型言語でなくても可能だが、関数型言語こそ最適だろう。
このように、オブジェクト指向プログラミングのパラダイムからSQLを排し、関数型言語パラダイムを持ち込むことには、多大な利点がある。
SQLを排しているのだから、リレーショナル・データベースのスキーマを設計する必要もない。
オブジェクトの永続化については、透過的永続化フレームワークとキャッシュ・オン・メモリ(memcachedなど)を導入すればよいだろう。アプリケーション層とドメイン層に影響を与えずに、インフラストラクチャ層だけで、しかもなるべく自動的に、永続化という目的を達成するのだ。
RDB/SQL的な集合演算のパフォーマンス改善
もし集計処理を高速化したければ、リレーショナル・データベースのINDEX
に相当する機能を、分散オブジェクト・ブローカーに実装すればいいはずだ。インフラストラクチャ層の設定変更によって、アプリケーションが高速化するだろう。また、遅延評価も有効だろう。
RESTとSOA
このような分散オブジェクト・アーキテクチャは、ラディカルなサービス指向アーキテクチャ(SOA)と言えるだろう。
"Tell, Don't Ask"(命じよ、尋ねるな)という原則に照らすと、RESTのようなリソース指向アーキテクチャ(ROA)では、「尋ねる」ことがシステムの動作原理になっている。
プリミティブなデータ型を暴露するアーキテクチャは、オブジェクト指向の対極にある。
つまり、全レイヤーで一貫してオブジェクト指向であろうとするドメイン指向アーキテクチャと、RESTは、相性が悪い。
ドメイン駆動設計を原理的に突き詰めた結果がサービス指向アーキテクチャ(SOA)になるのは、偶然ではない。
ちなみに、RESTが役立つ場合もある。オブジェクトをリソースとみなして、GET
アクションに限定したREST APIを、ユーザー・インターフェイス層に対して提供する場合だ。そのようなAPIは自動生成してもよいだろう。
REST APIでオブジェクトの内部データ構造を暴露すると、ユーザー・インターフェイス層がそれに強く依存してしまうので望ましくない
先行事例
課題
関連情報
- Service-Oriented Architecture (SOA) と Resource-Oriented Architecture (ROA) について
実践テスト駆動開発 テストに導かれてオブジェクト指向ソフトウェアを育てる (Object Oriented SELECTION)
- 作者: Steve Freeman,Nat Pryce,和智右桂,高木正弘
- 出版社/メーカー: 翔泳社
- 発売日: 2012/09/14
- メディア: 大型本
- 購入: 4人 クリック: 261回
- この商品を含むブログ (24件) を見る
エリック・エヴァンスのドメイン駆動設計 (IT Architects’Archive ソフトウェア開発の実践)
- 作者: エリック・エヴァンス,今関剛,和智右桂,牧野祐子
- 出版社/メーカー: 翔泳社
- 発売日: 2011/04/09
- メディア: 大型本
- 購入: 19人 クリック: 1,353回
- この商品を含むブログ (119件) を見る
追記:執筆後に知った関連情報
- BLOG::broomie.net: Thriftが便利すぎる "何が楽になったかというと、サーバサイドも、クライアントサイドも基本的にはThriftがプログラムを生成してくれるんですね。"
- google-gson - A Java library to convert JSON to Java objects and vice-versa - Google Project Hosting
- JavaでノンブロッキングIOを使ったネットワークアプリを学ぶのに最適なNetty 3.5系のGetting Startedを日本語訳しました - しふーのブログ
- 協調型分散システムのための RPC 設計 - #9 Backyard "SOAP, Thrift, RMI など従来の RPC の多くはクライアント-サーバ型で構成されています。これは主 (Server) の提供するサービスを従 (Client) から利用するという一方通行の利用形態です。この構成はサーバ側からクライアントへ通知を行う必要があるようなシステム設計とはうまく適合しません。"
- #finagle_hack で ZooKeeperServerSetCluster を使ったデモをつくりました - case class HatenaBlog(id: Symbol = ’seratch2) "個々の Finagle サーバはそれぞれ単体のサーバとして立ち上がっているだけであり、サーバ側でお互いを意識している訳ではありません。/Finagle クライアント側に ZooKeeper への接続設定と、サーバの接続先(java.net.SocketAddress)情報のリストを持たせます。これらの情報を ClientBuilder で渡すと、リクエストのロードバランシングや障害検知、フェールオーバーなどをよしなにやってくれるようになります。"
- 管理が困難―分散処理の常識はZooKeeperで変わる (1/3) - @IT
「分散オブジェクト・サーバー」は「オブジェクト指向Hadoop」のようなものかもしれない。だとするとHBase、ZooKeeper、Finagleあたりが参考になりそうだ。