以下範例我大量省略了重點以外的內容,例如: PHPDoc、類別屬性定義、命名空間 ....
請記得 Repository 是切開領域層與持久層的關鍵,如果你把持久層物件(如:Eeloquent Model)當成回傳值,那 Rspository 的優勢就會慢慢地消失了。
DB Transaction 也不該放在 Repository 裡,因為只有調用 Repository 的地方(通常是 Application Service)才有上下文可以決定要如何 commit
、rollback
。
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); // 使用工廠做出領域物件
}
}
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 是執行業務的地方,所以相較其他類別來說東西比較多,如:
上個範例中有個服務叫做 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;
}
}