VitalRouter を VContainer と動かすサンプル

[VitalRouter](https://github.com/hadashiA/VitalRouter) を [VContainer](https://github.com/hadashiA/VContainer) と合わせて使うのに少し手間取ったのでサンプルコードを置いておきます。
とりあえず動かして[ドキュメント](https://vitalrouter.hadashikick.jp/)を見たら何となく分かるはず。

# サンプルコード
```SampleCommand.cs
using VitalRouter;

// メッセージとして送るデータ
// メッセージを誰が受信するかは型によって判別されるので型定義が必須
public readonly struct SampleCommand : ICommand // 必ず ICommand をつける
{
    public int Value { get; }
    public SampleCommand(int value) => Value = value;
}

// record struct を使えるようにしている (https://zenn.dev/chorome/articles/ce2d86608e55fc) なら
// 以下のように1行で書けて便利 
public readonly record struct SampleCommand(int Value) : ICommand;
```

```SubscriberSample.cs
using VitalRouter;

// メッセージを受け取るクラスには [Routes] と partial が必須
[Routes] 
public partial class SubscriberSample
{
    // メッセージを受け取るメソッドには [Route] をつける
    [Route]
    private void On(SampleCommand cmd)
    {
        // SampleCommand を受け取ってログ出力するだけ
        UnityEngine.Debug.Log("SampleCommand を受け取りました。 Value = " + cmd.Value);
    }

    // 他のメッセージも受け取りたいなら型を変えてメソッドを作るだけ
    // [Route] private void On(AnotherSampleCommand cmd) { }
}
```

```PublisherSample.cs
using System.Threading;
using System.Threading.Tasks;
using VitalRouter;

// メッセージを送るクラスでは ICommandPublisher を DI して、PublishAsync で送る
public class PublisherSample
{
    private ICommandPublisher _publisher;

    [VContainer.Inject]
    public PublisherSample(ICommandPublisher publisher)
    {
        _publisher = publisher;
    }

    public async ValueTask PublishAsync(CancellationToken token)
    {
        // ICommandPublisherに同期(Publish)は無いので必ず非同期(PublishAsync)
        await _publisher.PublishAsync(new SampleCommand(100), token);
    }
}
```

```SampleLifetimeScope.cs
using UnityEngine;
using VContainer;
using VContainer.Unity;
using VitalRouter.VContainer;

// LifetimeScope のサンプル
// これを適当な GameObject にアタッチすればサンプルが動く
public class SampleLifetimeScope : LifetimeScope
{
    protected override void Configure(IContainerBuilder builder)
    {
        // メッセージを送る側は普通に登録する
        builder.Register<PublisherSample>(Lifetime.Singleton);

        // VitalRouter を DI する場合は RegisterVitalRouter を使う
        builder.RegisterVitalRouter(routing =>
        {
            // メッセージを受け取る側は Map で登録する
            routing.Map<SubscriberSample>();

            // もし MonoBehaviour ならMap ではなく MapComponent を使う
            // routing.MapComponent<SubscriberSample>();
        });
    }

    // テストのために Aキーが押されるたびに PublishAsync を呼び出す
    private async void Update()
    {
        if (Input.GetKeyDown(KeyCode.A))
        {
            var publisherSample = Container.Resolve<PublisherSample>();
            await publisherSample.PublishAsync(destroyCancellationToken);
        }
    }
}
```

# VitalRouter とは
Unity と .NET で使えるメッセージングライブラリです。
Unity のメッセージングライブラリとしては [MessagePipe](https://github.com/Cysharp/MessagePipe) が有名ですが使い勝手が全然違います。

## VitalRouter の良いところ
### 受け取る側は属性をつけるだけ
MessagePipe では受け取るメッセージの種類の数だけ ISubscriber<> を DI して Subscribe します。
VitalRouter なら受け取る側は属性をつけるだけなのでコードが見やすくなります。

### 送る側は ICommandPublisher.PublishAsync で投げるだけ
MessagePipe では送るメッセージの種類ごとに IPublisher<> が必要です。さらに同期/非同期でも IPublisher<> と IAsyncPublisher<> を使い分けます。
VitalRouter なら ICommandPublisher だけであらゆるメッセージを投げられます。

## VitalRouter の注意すべきところ
### Publish が非同期のみ
メッセージを投げる側は全て非同期になります。開発途中で入れると書き換えていくのが大変かもしれません。
使うなら最初から入れておいた方が楽だと思います。

### LifetimeScope で Map が必要
VContainer に登録するのに Register を使ってしまうとメッセージが届きません。
[Routes] をつけるのと Map にするのはセットで忘れないようにやりましょう。

## VitalRouter を使うべきか
VContainer を使っているなら簡単に取り入れられておすすめです。
Subscribe が並んでいるのがどれだけ邪魔だったか、無くなってみてよく分かりました。

メッセージの送受信に ICommand の型定義を強制しているのが良いバランスになっていると思います。
使われている箇所を型から辿れる安心感があります。

MessagePipe は色々な機能がついているので完全互換ではありませんが、メッセージング機能としては VitalRouter で必要十分だと思います。
新規プロジェクトでは VitalRouter を最初の選択肢にしたいです。