Ray.Di オブジェクトグラフの視覚化

# オブジェクトグラフの視覚化


## Guiceの"Grapher"

> 洗練されたアプリケーションを、Guiceの豊富なイントロスペクションAPIがオブジェクトグラフで詳細に記述することができます。 グラファー拡張機能はグラフを簡単に理解できるように視覚化します。 複雑なアプリケーションの複数のクラスのバインディングと依存関係を統合された図で表示できます。"

[Using grapher to visualize Guice applications](https://github.com/google/guice/wiki/Grapher)

以上はGoogle Guiceの**Grapher**というオブジェクトの依存関係を視覚化するツールの説明です。


これは`Module`で設定したオブジェクトの依存関係を以下のように[dotlang](https://www.graphviz.org/doc/info/lang.html)という、"グラフを表現するためのデータ記述言語"にに変換することで実現しています。

```injector.dot
digraph injector {
  graph [rankdir=TB];

  k_dd456f9 [shape=box, label=<<table cellspacing="0" cellpadding="5" cellborder="1" border="0"><tr><td 
      align="left" port="header" bgcolor="#000000"><font color="#ffffff">EnergySourceProvider<br 
      align="left"/></font></td></tr><tr><td align="left" port="m_f4b5f9f7">&lt;init&gt;</td></tr></table>>, 
      style=invis, margin=0.02,0]
  k_997fdab -> k_1c5031a6 [arrowtail=none, style=dashed, arrowhead=onormal]
  k_119e4fd8 -> k_17b87c3a [arrowtail=none, style=dashed, arrowhead=onormal]
  k_115c9d69 -> i_115c9d69 [arrowtail=none, style=dashed, arrowhead=onormalonormal]
  i_115c9d69:header:e -> k_9acc501 [arrowtail=none, style=solid, arrowhead=normal]
  k_9acc501 -> k_dd456f9 [arrowtail=none, style=dashed, arrowhead=onormalonormal]
}
```

# Ray.ObjectGrapher

Ray.Diでこの機能を実現する`Ray.ObjectGrapher`を開発中です。

例えば以下のモジュールがあるとします。

```FakeModule.php
class FakeModule extends AbstractModule
{
    protected function configure()
    {
        $this->bind(LoggerInterface::class)->to(DatabaseLogger::class);
        $this->bind(PdoInterface::class)->toProvider(PdoProvider::class);
        $this->bind()->annotatedWith('dsn')->toInstance('');
        $this->bind()->annotatedWith('id')->toInstance('');
        $this->bind()->annotatedWith('pass')->toInstance('');
        $this->bind(FooInterface::class)->to(Foo::class);
    }
}
```
`ObjectGrapher`クラスをモジュールを引数にして実行すると`dot`ファイルができます。

```make_dot.php
$dot = (new ObjectGrapher)(new FakeModule);
file_put_contents(__DIR__ . '/test.dot', $dot);
```

dotコマンドで`png`出力します。

```
dot -T png fake.dot -o fake.png
```

オブジェクトグラフが描画され、オブジェクトの構造やオブジェクトを繋ぐバインディングが理解しやすくなります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3940/60c52132-8b77-1946-8842-9146e78d7aa9.png)

線やノードの形や色には意味があります。
### 線

 1. 実線は型を表しています。
 2. 点線は型からクラスへのバインディングを表しています。
 3. ２つの矢印は依存が`プロバイダー`にバインドされてる事を表してます。

### ノード

 1. 黒はクラスです。
 2. 灰色はインスタンス（実値）です。

## BEAR.Resource

次はBEAR.Sundayのコンポーネントを描画してみましょう。まずはBEAR.Resourceのモジュールで宣言されたモジュールです。BEAR.SundayのコンポーネントはSRP原則を守り、複数のオブジェクトに分割されグラフを構成しています。

```php
$dot = (new ObjectGrapher)(new ResourceModule);
```
このスクリプトで出力された`dot`ファイルをPNGに変換しました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3940/4986289b-9eed-125c-02ee-73658147b0c9.png)

全体としては複雑ですが、部分を見るとオブジェクト同士の関係は単純です。
よく観察してみてください。`Annotations\Reader`が５つのクラスから依存されてる事がわかります。

この図なしにこの依存を理解するのは簡単ではありません。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3940/d125bfdf-65bd-7fe0-25b2-c8c4041df337.png)

## BEAR.Package

次にBEAR.Sundayで一番大きなオブジェクトグラフ`$app`を服もBEAR.Packageの`PackageModule`を描画しました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3940/06fec3d7-b769-5866-28d6-ecc5c68b5571.png)

複雑で大きなグラフですが、クリーンに分割できていると思います。

## 終わりに

このグラフ描画は、オートワイアリングが必須である GuiceやRay.Diだからこそできるものです。

グラフを描画する事でオブジェクトの構造を理解しやすいようにするだけではなく、誤ったバインディングや不要なバインディングを発見する事もできます。機能が適切な粒度を持って分割されてるか(SRP)のリファクターをする時にも役立つでしょう。

今までも [print_o](https://github.com/koriym/print_o)でオブジェクト間の繋がりを表す事ができましたが、このグラファーでは繋がりだけでなく、インターフェイスやプロバイダー、インスタンスなど、どのように繋がれたのかも確認する事ができます。

もうすぐ完成です。