アプリケーションコンテキスト

# アプリケーションコンテキスト

アプリケーションがどのように振る舞うかを決定するのがアプリケーションコンテキストです。

例えば、**環境**(`prod` / `stage` / `develop`)によって、DBの接続先が変わったり、キャッシュの使用の有無があったりします。これはどのフレームワークにも用意されている機能だと思います。

１つのサイトなのにHTMLサイトとAPIサイトを別に開発することがあります。そうではなくて**１つのHTTPアプリケーション**をつくってHTMLとAPIという風に振舞いを変える...というプレゼンテーションをREST API界の大御所中の大御所、Mike Amundsenさんの前で以前行ったことがあります。（緊張しました！）
http://koriym.github.io/http-is-one-thing/#/

**言語**もアプリケーションコンテキストの１つです。**HTTPアプリケーション**なのか、**コンソールアプリケーション**なのか、他にも通常バージョン、特別プロモーションバージョンなど１つのアプリケーションの振舞いを変えたい場合があるかもしれません。

# 0.xでは

それを現在のBEAR.Package 0.xでは**アプリケーションコンテキスト変数**`$context`とアプリケーションスクリプトの実行シークエンスを変えることで実現していました。

`$context`は依存解決を決定します。たとえば`develop`ならキャッシュは効きません。それに`prod.php`というアプリケーションの実行を決めるアプリケーションスクリプトで実行することができました。構成は`develop`だけど実行は`prod`ということはできました。

アプリケーションオブジェクトはDIで決定、実行はスクリプトです。

 * prod https://github.com/koriym/BEAR.DemoApps/blob/develop/Demo.Sandbox/bootstrap/contexts/prod.php
 * dev https://github.com/koriym/BEAR.DemoApps/blob/develop/Demo.Sandbox/bootstrap/contexts/dev.php

```php
new ProdModule();
new ApiModule();  // これらのモジュールがアプリケーションオブジェクトを決定
```

アプリケーションコンテキストは単純な文字列として表されるので、キャッシュキーにも使えます。`prod`, `develop`, `api`という３つのコンテキストキーを決めましたがアプリケーション要求で追加できます。

## 問題点

複数のコンテキストにどう対応するかという問題がでてきました。@mackstarさんからはAPIのdevelopはどうしたらいいのか？という質問をうけ、配列にして対応することで一時的な対処もしました。複数の実行スクリプトがメリットでもあるのですがさらに簡潔するため１つにならないかということも検討しました。

コンフィギュレーションとコンテキストの境界もあいまいでした。モジュールの中で他のモジュールをinstallしたり書き換えたりすることもできますが、一方でdbの接続設定などは変数を合成して`dev`, `prod`という環境値の優先順位を決定していました。他のFWと比べても特別複雑なものではなかったのですが、（**設計上の**）十分な集約が行われていないという認識をもつようになりました。

またデフォルトのアプリケーションの振舞いは`develop` ? `prod` ?　どちらがいいのでしょうか？どうやって決めたらいいのでしょうか？

## デコレーション可能なアプリケーション

アプリケーションをどのように振る舞うか選択するのではなく、素のアプリケーションを必要なコンテキストで装飾（デコレーション）していくことで解決できないか考えてみました。

図でかくと、...いやPHPで書いてみましょう！
こんな感じです。

`0.x`の単一コンテキストのイメージはswitch文で表せます。モードによって振舞いが**swtich**します。

```php
switch ($context) {
    case 'api':
        // API出力
        break;
    case 'develop':
        // developとしての構成;
        break;
    case 'prod':
        // prodとしての構成;
        break;
}
```
そうではなく、[GoFのデコレーション](http://ja.wikipedia.org/wiki/Decorator_%E3%83%91%E3%82%BF%E3%83%BC%E3%83%B3)のイメージです。

```php
$app = new Prod(new Hal(new Api(new App))));
```

素のアプリケーション(**App**)を**Api**アプリとして扱い、フォーマットは素JSONではなく**Hal**、それをプロダクション**Prod**とアプリケーションを装飾しています。

素のアプリケ−ションは開発用(develop)でもなくプロダクション用(prod)でもなく**素**(bare)です。開発用の出力もないし、Prod用のキャッシュもありません。ただ最も単純に動作するだけのプログラムです。

## DIとAOPで構成

これのアプリケーションオブジェクトをDIで構成するとこうです。

```php
$injector = new Injector(new ProdModule(new HalModule(new ApiModule(new YourApp)))));
$app = $injector->getInstance(AppInterface::class);
```

パフォーマンスを考えるとこのnewの集合もキャッシュしたいので`Bootstrap::newApp`というメソッドを用意してこのようになりました。

```php
$appName = 'Koriym\MyFirstApp';
$context = 'prod-hal-api-app';
$app = (new Bootstrap)->newApp(new AppMeta($appName), $context, new ApcCache);
```
アプリケーションの名前(vendor/app)とハイフン区切りでコンテキストに応じたアプリケーションオブジェクトがつくられます。

アプリケーション内ではオブジェクトが構成されているので振舞いを状態に応じて変える必要はありません。

```php
if ($context === 'api') {
 $this->showAsJson($page);
} // などではなく

echo $page; // APIのときはjson
echo $page; // HTMLのときは
```

## まとめ

素のアプリケーションは値だけを持ち、なんとかJSONに変換できるだけのアプリです。これは値だけなのでテストが簡単です。つぎに(HTMLなどの）表現が与えられるのですが値のテストが出来ていれば、表現だけのテストを行えばOKです。ソフトウエアをブラックボックスにして一番外側のIFから振舞いだけをみるようなテストを最小限にすることができます。

一度つくったものに外側からデコレーション可能であれば、リソースを情報コンポーネントとして再利用することができます。レガシーを本当の意味でのレガシー（資産）として扱うことが容易になるかもしれません。もしそれが可能になるとしたら、素晴らしいと思いませんか。