componentフレームワークは動作中に状態を保持するソフトウェアに関して
- 起動・停止のインターフェイスを定義する
- ソフトウェアをコンポーネントに分割し、コンポーネント間の依存関係を宣言的に記述する
- 動作中のソフトウェア全体の状態を一つのデータ構造(システム)にまとめる
といった機能を持つフレームワークです。componentフレームワークを利用することで、tools.namespaceを利用した開発が容易になります。
ring/compojureベースのWebアプリケーションに、componentフレームワークを適用する例を紹介します。
プロジェクト作成
compojure用テンプレートを利用してプロジェクトを作成します。
1
|
|
依存ライブラリ追加
project.clj
の:dependencies
に以下を追記します。
componentフレームワーク
1
|
|
tools.namespace
1
|
|
ring-jetty-adapter(ringアプリケーションの直接起動、停止)
1
|
|
HttpServerコンポーネント定義
example-component-ring.component
ネームスペースに、HttpServerコンポーネントを記述した例です。
component.clj
コンポーネントはLifecycle
プロトコルを実装したレコードとして定義します。
Lifecycle
プロトコルはメソッドとしてstart
とstop
を持っています。これをHTTPサーバの起動、停止を行うように実装しています。この時、サーバオブジェクト(このアプリケーションにおける状態)をレコードのフィールドに格納しています。
create-system
関数は、コンポーネントを作成し、1つのシステムにまとめる関数です。内部でsystem-map
関数を利用しています。上記では、HttpServerコンポーネントのみ作成していますが、複数のコンポーネントを組み合わせて1つのシステムにまとめることや、コンポーネント間の依存関係を記述することができます。
システムの起動・停止・リロード
システムの起動・停止・リロードを行うための関数を定義します。REPLから利用しやすいように、user
ネームスペースに定義する例を紹介します。
project.clj追記
起動・停止・リロード用の関数は、開発時のみ読み込まれるファイルに記述します。
project.clj
の:profiles :dev
に以下を追記します。
1
|
|
project.clj
全体は以下のようになります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
|
user.clj作成
dev/user.clj
ファイルを作成し、システム起動・停止・リロードを行う関数を定義します。
user.clj
プロジェクト内でREPLを起動すると自動的にこれらの関数が読み込まれた状態になります。
システムを起動する
1 2 3 |
|
システムを停止する
1 2 3 |
|
(ソースコード修正時)システムをリロードする
1 2 3 4 |
|
複数のコンポーネントの連携
componentフレームワークでは、複数のコンポーネントの依存関係を記述し、コンポーネント同士を連携させることができます。
ここでは、Databaseコンポーネントを導入し、HttpServerコンポーネントが利用する(HttpServerコンポーネントがDatabaseコンポーネントに依存する)例を紹介します。
MongoDB導入
Databaseコンポーネントの中身として、MongoDBを利用することにします。ClojureからMongoDBを利用するためのクライアントライブラリとしてMongerを利用します。project.clj
に以下を追記します。
1
|
|
Databaseコンポーネント定義
component.clj
にDatabaseコンポーネントを定義します。
component.clj
HttpServer
と同様に、Lifecycle
プロトコルを実装したレコードとして定義しています。
既存コードへの変更点として、HttpServer
レコードに、database
フィールドを追加しています。このフィールドにDatabase
コンポーネントのオブジェクトを、状態として保持します。
また、create-system
関数では、using
関数を利用して依存関係を定義しています。上記では、HttpServer
コンポーネントがDatabase
コンポーネントを利用する、という関係を記述しています。これによりHttpServer
インスタンスのdatabase
フィールドには、Database
インスタンスが代入されます(依存関係の定義に従って代入が自動的に行われる仕組みをDependency Injectionといいます)。
コンポーネントをringハンドラに渡す
上記までの例で、HttpServerコンポーネントにDatabaseコンポーネントを渡すことはできていますが、ringアプリケーションのエントリポイントとなるハンドラに、直接引数として渡すことはできないため、Webアプリケーション内からDatabaseコンポーネントを利用するためには、さらに一工夫が必要になります。
例として、requestマップ内にDatabaseコンポーネントを追加するringミドルウェアを利用する方法を紹介します。
component.clj
wrap-app-component
ミドルウェアで、requestにDatabase
コンポーネントを追加しています。これにより、ringハンドラ内の処理で、Databaseコンポーネントを利用できるようになります。
handler.clj