iT邦幫忙

第 12 屆 iT 邦幫忙鐵人賽

DAY 23
0
Modern Web

全端成長之旅系列 第 23

Day.23 Laravel with DDD Part.4

以下範例我大量省略了重點以外的內容,例如: PHPDoc、類別屬性定義、命名空間 ....

Repository

請記得 Repository 是切開領域層與持久層的關鍵,如果你把持久層物件(如:Eeloquent Model)當成回傳值,那 Rspository 的優勢就會慢慢地消失了。

DB Transaction 也不該放在 Repository 裡,因為只有調用 Repository 的地方(通常是 Application Service)才有上下文可以決定要如何 commitrollback

使用範例

介面


interface GetUserRepository
{
    public function byId();
}

實作


class SqlGetUserRepository implements GetUserRepository
{
    public function byId($id)
    {
        $userModel = UserModel::find($id);
        
        return is_null($userModel)
            ? null
            : app(UserFactory::class)->makeFromModel($userModel); // 使用工廠做出領域物件
    }
}

Application Service

use Log;
use DB;
use Event;
use Exception;
use Modules/ModuleA/DomainEvents/MoneyTransfered;
use Modules/ModuleA/Repositories/SqlAccountRepository;
use Modules/ModuleA/DomainServices/CheckAccountAmountService;

class TransferService extends ApplicationService
{
    public function __construct(
        CheckAccountAmountService $checkAmountService,
        SqlAccountRepository $accounts
    ) {
        $this->accounts = $accounts;
        $this->checkAmountService = $checkAmountService;
    }

    public function setToAccount($accountId)
    {
        $this->toAmmountId = $accountId;

        return $this;
    }

    public function setFromAccount($accountId)
    {
        $this->fromAccountId = $accountId;
        
        return $this;
    }

    public function setAmount($amount)
    {
        $this->amount = $amount;
        return $this;
    }

    public function exec()
    {
        try {
            DB::beginTransaction();

            $fromAcount = $this->accounts->getById($this->fromAccountId);
            $toAcount = $this->accounts->getById($this->toAccountId);

            $canTransfer = $this->checkAmountService
                ->setAccountt($fromAcount)
                ->setAmmount($this->amount)
                ->check();
            
            if ($canTransfer) {
                $fromAcount->minusAmount($this->amount);
                $toAcount->plusAmount($this->amount);
                $this->accounts->saveAmount($fromAcount);
                $this->accounts->saveAmount($toAcount);
            }

            DB::commit();
            $this->log("Transfer Amount: {$this->amount}, from {$fromAcount} to {$toAcount} success.");
            event(new MoneyTransfered([
                'timestamp' => now(),
                'from' => $fromAcount,
                'to' => $toAcount,
                'amount' => $this->amount
            ]));
            return true;
        } catch (\Exception $e) {
            DB::rollBack();
            $this->log($e);
            return false;;
        }
    }
}

ApplicationService 是執行業務的地方,所以相較其他類別來說東西比較多,如:

  • DB Transaction
  • Log
  • 調用 DomainService
  • 調用 DomainEvent
  • 指派 Domain Model 做事

DomainService

上個範例中有個服務叫做 CheckAccountAmountService,我們用它來檢查帳戶金額。
CheckAccountAmountService 就是一個 Domain Service, Domain Service 維護的是領域規則,而不是執行某一項任務。

抽象類

abstract class DomainService
{
    abstract function check()
}

實作類

class CheckAccountAmountService extends DomainService
{
    public function setAccount(Account $account)
    {
        $this->account = $account;

        return $this;
    }

    public function setAmount($amount)
    {
        $this->amount = $amount;

        return $this;
    }

    public function check()
    {
        return $this->account->getAmount() >= $this->amount;
    }
}

上一篇
Day.22 Laravel with DDD Part.3
下一篇
Day.24 Laravel with DDD Part.5
系列文
全端成長之旅30

尚未有邦友留言

立即登入留言