LoginSignup
5
3

Livewire v3を使って多層レイヤーアーキテクチャを考える

Last updated at Posted at 2023-12-17

この記事は、AEON Advent Calendar 2023の18日目です🎉

こんにちは。イオンスマートテクノロジー株式会社のCTO室でテックリードをはじめました。うしおです。

ここまで順調にカレンダー公開されていますが、インフラやNewRelicネタが多いですね:innocent:
箸休めにLaravel Livewireの話でもいかがでしょうか。

Livewireのv3がついに来ました。ググると8月だったようです。ようやくガッツリ触れましたのでコード付きでレポートしたいと思います。
ソースコード全体はこちらです。

作ったもの

こんな感じの、ECサイトの商品一覧をイメージしています。
サンプルなので機能は限定的ですが、設計はちゃんとやっています。
Animation.gif

使い勝手とか

v2の頃からそうでしたが、やはりイイです、Livewire:sparkles:
通常のWebアプリケーションだと、上のようなことをやるだけでajaxのエンドポイントがモリモリ増えていきますが、単なるアクションメソッドのように処理できるのが最高です。

Livewire v3の良い点

わたし的に一番嬉しかったのがシンセサイザーですね。
こいつができてようやくLivewire ComponentとDomain Entityをつなげることができました。
商品一覧のページでやっている、ここのところ。

final class Index extends Component
{
    /** @var IndexResult[] $products */
    public array $products;
    public FavoriteProduct $favoriteProduct;
    public Cart $cart;
    ...

$productsがPresentation用のDTO、$favoriteProduct$cartがDomain Entityなんですが、
Livewire v2のルールではプリミティブ型かWireableなオブジェクト1しかプロパティにできませんでした。
かといってDomain EntityをWireableにするのは関心分離の点でやりたくない。
これがv3のシンセサイザーにより綺麗に解決できました。
たったこれだけ書けばFavoriteProductクラスのコードはそのままでWireableになります。
素晴らしすぎるのにv3の機能紹介に入っていない。なぜだ。

final class FavoriteProductSynth extends Synth
{
    public static $key = 'favorite_product';

    public static function match($target): bool
    {
        return $target instanceof FavoriteProduct;
    }

    public function dehydrate(FavoriteProduct $target): array
    {
        return [$target->toArray(), []];
    }

    public function hydrate($value): FavoriteProduct
    {
        return FavoriteProduct::fromArray($value);
    }
}

レイヤー構成

下記のような構成にしました。
LivewireのFull Page ComponentがControllerとViewを兼ねている形です。
ユースケースも単純なものしかないのでLivewire Componentに直接書いています。

  • Domain
    • Entity
    • Command
    • Query
    • Value (まだ無い)
    • Service (まだ無い)
    • Event (まだ無い)
  • Application (まだ無い)
  • Infrastructure
    • Command
    • Query
  • Presentation
    • Query
    • Livewire Component + Blade View

コンポーネント構成

赤枠で囲った部分がLivewireコンポーネントです。
①商品一覧
②お気に入り数アイコン
③カート商品数アイコン

コンポーネント.png

①の操作でお気に入りやカート商品が変更される度、②や③へイベント通知しています。
イベント受信側がこうで、

#[On('favorite-product-changed')]
public function updateCount(int $count): void
{
    $this->count = $count;
}

イベント送信側がこれだけ。便利すぎる。

$this->dispatch('favorite-product-changed', count: $this->favoriteProduct->count());

テストコード

テストもイイ感じですね。
データのアサーションが強力なのでロジックのテストがシンプルに行えます。

/**
 * お気に入り追加ができること
 */
public function test_user_can_add_favorites(): void
{
    // 操作対象ユーザー準備
    $user = Models\User::factory()->create();
    // 対象外のユーザー準備
    $otherUser = Models\User::factory()->create();
    // 商品マスター準備
    $products = Models\MasterProduct::factory()->count(10)->create();

    // ユーザーがお気に入り追加のアクションを実行
    // お気に入りに追加されたことを検証
    Livewire::actingAs($user)
        ->test(Index::class)
        // これでアクション実行
        ->call('toggleFavoriteProduct', $products[0]['id'])
        // $favoriteProductを検証
        ->assertViewHas('favoriteProduct', function (FavoriteProduct $favoriteProduct) use ($products) {
            return $favoriteProduct->contains($products[0]['id'])
                && $favoriteProduct->count() === 1;
        });

    // 対象外ユーザーのお気に入りは変化が無いことを検証
    Livewire::actingAs($otherUser)
        ->test(Index::class)
        ->assertViewHas('favoriteProduct', function (FavoriteProduct $favoriteProduct) use ($products) {
            return $favoriteProduct->count() === 0;
        });
}

おわりに

いやー、すごいです。非常に良質な開発体験を得られます。
Laravel開発にReactやVueが不要になる日も近いですね(もうすでに?)。
無条件でオススメできるのですが、採用する場合は必ずここを熟読しましょう。

絶賛採用中です!

イオンスマートテクノロジーではエンジニアをはじめとした様々な職種を積極的に採用中です!
これからとてもおもしろいフェーズへ突入していくと思いますので興味のある方は是非カジュアル面談などで話を聞いてください!

  1. Eloquent Model含む

5
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
3