PHP, システム設計

[DDD] Value Object 値オブジェクト

PHP

 

  • Value Object採用により仕様を豊かに表現する
  • stringやintといった以上のバリデーションができる

Value Objectのルール

  • 「不変」であること
    ・setter()をつけてはダメ
    ・別の値が欲しい時は別途でnewする

Clientクラス

呼び出し

UserConstroller

class UserController
{
      public function _construct()
    {
        $this->s_user = app()->make(App\Services\UserService::class);
    }
    
    public function create(CreateUserRequest $request)
    {
        $this->s_user->create(new FullName($request->first_name, $request->last_name), new Email($request->email, new Password($request->password)));
    }
    
    public function changePassword(AutoChangePasswordRequest $request)
    {
        return $this->s_user->changePaasword(new EntityId($request->user_id), new Password($request->password));
    }
}

 

Contextクラス(利用クラス)

UserService

class UserService
{
    public funcrtion _costruct(
    ) {
        $this->r_user = app()->make(App\Repository\UserRepository::class);
    }
    
    public function create(FullName $full_name, Email $email, Password $password)
    {
        return $this->r_user->createUser($full_name->getFullName(), $email->getEmailAddress, $password->getPassword());
    }
    
    public function changePassword(EntityId $user_id, Password $password): void
    {
        $user = $this->r_user->findWhere([
            'id' => $user_id->getEntityId(),
        ]);
        $user->password = $password->getPassword();
        $user->save();
    }
}

FullName型、Email型, Password型でバリデーションできる。

string以上に表現や仕様が豊かになる🐱

 

Value Objectの例

EntityId

class EntityId
{
    private int $entity_id;
    
    public function _construct(int $entity_id)
    {
        // マイナスが来たらおかしい
        if ($entity_id < 0) {
            throw new ValueObjectException();
        }
        $this->entity_id = $entity_id;
    }
    
    public function getEntityId(): int
    {
        return $this->entity_id;
    }
}

 

Email

class Email
{
    private string $email_address;
    
    public function _construct(string $email_address)
    {
                // RFCに沿ったメール型であること
        if (!filter_var($mailaddress, FILTER_VALIDATE_EMAIL)  || is_null($email_address)) {
           throw new ValueObjectException();
        }
        $this->email_address = $email_address;
    }
    
    public function getEmailAddress(): string
    {
        return $email_address;
    }
}

 

FullName

 

class FullName
{
    private string $first_name;
    private string $last_name;
    
    public function _construct(string $first_name, string $last_name)
    {
        $this->first_name = $first_name;
        $this->last_name = $last_name;
    }
    
    public function getFullName(): string
    {
        return sprintf("%s %s", $this->first_name, $this->last_name);
    }
    
    public function getFirstName(): string
    {
        return $this->first_name;
    }
    
    public function getLastName(): string
    {
        return $this->last_name;
    }
}

 

Password

class Password
{
    const MIN = 8;
    private string $password;
    
    public function _construct(string $password)
    {
        // 8文字以上
        if (mb_strlen($password) < $min) {
            throw new ValueObjectException();
        }
        $this->password = $password
    }
    
    public function getPassword(): string
    {
        return $this->password;
    }
}

 

 

不変であること

 

よくあるパターン

$fullname = new FullName($first_name, $last_name);
$fullname->changeFirstName($new_first_name);

Value Object ではこうする

$fullname = new FullName($first_name, $last_name);
$fullname = new FullName($new_first_name, $last_name);

 

 

Value Objectに振る舞いを持たせる

 

経験値を加算する例

 

class Experience
{
    private string $industry;
    private int $amount = 0;

    public function __construct(int $amount, string $industry)
    {
        $this->industry = $industry;
        $this->amount = $amount;
    }

    public function addExperience($amount = $this->amount + $amount, $industry)
    {
        if ($this->industry !== $industry) {
            throw new ValueObjectException();
        }
        return new Experience($amount, $industry);
    }

}

新しいインスタンスを生成して返却することで安定する

 

 

 

 

 

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

日本語が含まれない投稿は無視されますのでご注意ください。(スパム対策)