我在DDD實戰:一段訂單建立程序-Part 1進行了一次效果不佳的重構。
最後不盡理想的結果如下:
orderRepository = new OrderRepository(); } private function getNewOrderOrCreate() { if (Auth::check()){ $order = $this->orderRepository->getNewOrderByUser(Auth::user()); if ( !$order ){ $order = Order::createWithUser(Auth::user()); } } else { $order = $this->orderRepository->getNewOrderBySesssionId(Session::getId()); if ( !$order ){ $order = Order::createWithSession(Session::getId()); } } return $order; } public function getOrder() { $order = $this->orderRepository->getNewOrderOrCreate(); return View::make('client/order', ['order' => $order]); } }
我對controller中的getNewOrderOrCreate函式非常不滿意。
controller中的其他部份,都只是初始化某些類別、把某些參數傳來傳去,謹此而已,唯獨這個函式內含複雜邏輯。真醜。
但是那些判斷logic確實是controller該負責的事情,也不能丟進model,怎麼辦呢?
DDD觀點
DDD中區分了Application Layer、Domain Layer。乍看之下getNewOrderOrCreate不能進Domain Layer,只能硬放在Application Layer變成醜controller。
我在DDD實戰:一段金流程序提到過Service的概念。
我研究了一下發現…
其實Service又可分為Application Service、Domain Service以及Infrastructure Service。
要判斷一個Service隸屬於哪種,常常非常困難(譬如說,您覺得DDD實戰:一段金流程序中的ReceivePaymentNotification算是Application service還是Domain service?)
不過眼前的getNewOrderOrCreate卻非常清楚,應該是屬於Application Layer!
(但這都沒關係。目前我沒打算針對三種Service在程式中加上namespace,也沒打算在檔案結構上分別開資料夾之類的。現階段我就隨便開個Service資料夾,然後管他哪種Service通通丟進去就好了。)
那麼,就讓我來封裝一個Service,接著在Controller使用它吧:
getOrCreate(); return View::make('client/order', ['order' => $order]); } }
orderRepository = new OrderRepository(); } public function getOrCreate() { if (Auth::check()){ $order = $this->orderRepository->getNewOrderByUser(Auth::user()); if ( !$order ){ $order = Order::createWithUser(Auth::user()); } } else { $order = $this->orderRepository->getNewOrderBySesssionId(Session::getId()); if ( !$order ){ $order = Order::createWithSession(Session::getId()); } } return $order; } }
終於成功了!終於讓controller內的code看起來非常簡潔、expressive,卻又不影響到Domain Layer!
我還發現,getNewOrderOrCreate光看函式名稱就覺得概念模糊。這種模糊感本身就暗示了抽象化的不足、程式設計想得不夠清楚。
一模一樣的內容,封裝成類別、再重新命名,就成了FetchOrderForClient底下的getOrCreate,真是清楚多了。