くずきのblog

技術とか色々

Laravelでバリューオブジェクトのみを実装する

どうも、くずき(@kzkohashi)です。
今回はDDDにおける、バリューオブジェクトをLaravelでどう表現するかについて書きたいと思います。

なぜバリューオブジェクト?

DDDを学んでいる中で、実際にどうやったら実装に落とし込めるだろうか?って考えたときに、一番最小構成であるバリューオブジェクトから手をつけたほうがいいんじゃないかという話になった。
(もちろんユビキタス言語などを見つけてからの前提)

実装

Eloquentから返却される値をすべてバリューオブジェクトにしてたら、時間も手間もかかってしまうので、一部ずつ変えていく。
今回はUserの1カラムであるEmailを例に変更してみる。

バリューオブジェクトの生成

<?php
class Email implements \JsonSerializable
{
  /**
   * @var string
   */
  protected $value;

  /**
   * @param string $value
   */
  protected function __construct(string $value)
  {
      if (is_null($value)) {
        throw new \InvalidArgumentException("Argument must be set. Passed value is empty");
      }
      
      // 正しいメールアドレスかのチェック
      if (preg_match('/^([a-z0-9\+_\-]+)(\.[a-z0-9\+_\-]+)*@([a-z0-9\-]+\.)+[a-z]{2,6}$/iD', $value)) {
        throw new \App\Exceptions\EmailException();
      }
      
      $this->value = $value;
  }

  /**
   * @return string
   */
  public function value(): string
  {
      return $this->value;
  }

  /**
   * @param $string
   * @return bool
   */
  public function equals(self $string)
  {
      return $this->value === $string->value;
  }

  /**
   * @param string $value
   * @return static
   */
  public static function of(string $value)
  {
      return new static($value);
  }

  /**
   * @return string
   */
  public function __toString()
  {
      return (string)$this->value;
  }

  /**
   * @return mixed|string
   */
  public function jsonSerialize()
  {
      return $this->value;
  }

}

constructでは、バリューオブジェクトを作成する際のルールなどを書いておく。
JsonSerializableを継承しておく理由としては、response()->jsonなどを使用する際に、バリューオブジェクト->値 などになるため入れておいたほうが良さそう。

モデル

<?php

class User extends Models
{
  
  
  public function toValueObject()
  {
    // イミュータブルにするため
    $replication = $this->replicate();
    
    // すでに作成してあるバリューオブジェクトを入れる
    $replication->email = new Email($this->email);

    return $replication;
  }
}

実際の利用方法

<?php
  // カラムをバリューオブジェクトに変換
  $user = User::find(1)->toValueObject();
  
  // バリューオブジェクトの振る舞いを利用
  $user->email->xxxx();

こんな形で利用している。
ただ、ロジックが大きくなってしまう仕様や、判定ロジックは外部に切り出したりしている。
ここガッツリ書けなかったので、この方の記事が参考になると思います。

pospome.hatenablog.com

また、バリューオブジェクト=不変性だと思っていたのですが、かとじゅんさんが昔に書いた記事が参考なります。

d.hatena.ne.jp

感想

DDDが書いてある本などを読んでいると、設計方法はやんわりわかったけど実際動いてるコードはどこから手をつけたらいいかわらかなかった。
今回はバリューオブジェクトから始めることによって、ドメインロジックに集中するって大切さが少しわかってきた。