SWIMMING IN HELL

E37b4344ef4bfd0fc4826c04971e54fb?s=47 nrs
March 30, 2019

SWIMMING IN HELL

レガシーあるあるに対しての簡単な処方箋のような内容です。
2019/03/30 PHPer茶会における LT のスライドです。

HomePage: https://nrslib.com
Twitter: https://twitter.com/nrslib

E37b4344ef4bfd0fc4826c04971e54fb?s=128

nrs

March 30, 2019
Tweet

Transcript

  1. Masanobu Naruse SWIMMING IN HELL

  2. <?php echo '-------------------------'; echo ' Introduction '; echo '-------------------------'; $name

    = 'Masanobu Naruse'; $job = ['Programmer', 'Developer Evangelist']; $company = 'GMO Internet, Inc. '; $team = ['UX UI', 'Developer Relations Team']; $twitter = '@nrslib'; $url = 'https://nrslib.com';
  3. Developer Relations Team Fans Making Opportunity Interest Comprehension

  4. なるせ

  5. PHPerKaigi スポンサー するんだけど LT やってもらえる? なるせ

  6. PHPerKaigi スポンサー するんだけど LT やってもらえる? はい喜んで! なるせ

  7. PHPer 茶会の LT だから なるせ

  8. !? PHPer 茶会の LT だから 面白くしてね なるせ

  9. 高くあげられたハードル 超えるためのネタは 面白くしてね

  10. 昨年 2018年9月

  11. クリーンアーキテクチャの実装例について

  12. その中のスライド

  13. 仕様書

  14. 無し

  15. 前任者

  16. 無し

  17. 頼れる仲間は

  18. ベトナムへ

  19. None
  20. 仕様書無し 前任者無し 頼れる仲間はベトナムへ

  21. というわけで今日のお話は

  22. というわけで今日のお話は アンチパターンマイグレガイド

  23. またの名を

  24. 本当にあった怖いコードの話

  25. 第壱話 独自プロトコル

  26. public function confirm(Data $data) { /* * なんかごっつい処理 */ }

  27. public function confirm(Data $data) { var_dump($data); /* * なんかごっつい処理 */

    }
  28. public function confirm(Data $data) { var_dump($data); /* * array(3) *

    { * [0]=> string(9) "111|201|1" * [1]=> string(9) "112|201|3" * [2]=> string(9) "100|011|1" * } */ /* * なんかごっつい処理 */ }
  29. public function confirm(Data $data) { var_dump($data); /* * array(3) *

    { * [0]=> string(9) "111|201|1" * [1]=> string(9) "112|201|3" * [2]=> string(9) "100|011|1" * } */ /* * なんかごっつい処理 */ } 111|201|1 112|201|3 100|011|1
  30. public function confirm(Data $data) { var_dump($data); /* * array(3) *

    { * [0]=> string(9) "111|201|1" * [1]=> string(9) "112|201|3" * [2]=> string(9) "100|011|1" * } */ /* * なんかごっつい処理 */ } 111|201|1 112|201|3 100|011|1 Q. どういう意味 でしょうか
  31. A.

  32. 111|201|1 112|201|3 100|011|1 商品コード|枝番|注文数 A.

  33. クラス作ろう class Data { /** * @var string */ public

    $productCode; /** * @var string */ public $branchNumber; /** * @var string */ public $orderNum; }
  34. 第弐話 利口な UI

  35. 金額計算はどうなってるか 確認してみよう

  36. class HumbleController { public function orderConfirm(string $id){ $order = DB::table('orders')->where('id',

    $id)->first(); $response = $order; return view('order', compact('response')); } }
  37. class HumbleController { public function orderConfirm(string $id){ $order = DB::table('orders')->where('id',

    $id)->first(); $response = $order; return view('order', compact('response')); } } 金額計算は いずこに?
  38. 注文確認画面 <?php $price = floor((($response->unit_price * $response->num) * 1.08) -

    $response->discount); ?> <h1>注文確認</h1> <p> 注文番号 {{$response->id}} の合計金額は {{$price}} 円です </p>
  39. 注文履歴確認画面 <?php $price = floor((($response->unit_price * $response->num) * 1.08) -

    $response->discount); ?> <h1>注文履歴</h1> <p> 注文番号 {{$response->id}} の合計金額は {{$price}} 円です </p>
  40. 注文履歴 確認画面 注文 確認画面

  41. 注文履歴 確認画面 注文 確認画面 同じコードがコピペ

  42. 注文 確認画面 注文金額計算 注文履歴 確認画面 注文金額計算 注文履歴 一覧画面 注文金額計算 注文情報

    サーバー
  43. 注文情報 注文金額計算 サーバー 注文 確認画面 注文金額計算 注文履歴 確認画面 注文金額計算 注文履歴

    一覧画面 注文金額計算
  44. 注文情報 注文金額計算 サーバー 注文 確認画面 注文履歴 確認画面 注文履歴 一覧画面

  45. 第参話 知りすぎたコントローラ

  46. ここの画面の 配色なんだけど フロントくん

  47. 稼働中はシアンが いいなー フロントくん

  48. 稼働中はシアンが いいなー はい喜んで! フロントくん

  49. フロントくん よし コードを見てみよう

  50. フロントくん <ul> @foreach ($response->data as $order) <li> Name: {{$order->productName}} Status:

    <span style="color: {{$order->status}}"> @switch($order->status) @case('yellow') 入金待ち @break @case('green') 設定中 @break @case('blue') 稼働中 @break @case('red') エラー @break @endswitch </span> </li> @endforeach </ul>
  51. <ul> @foreach ($response->data as $order) <li> Name: {{$order->productName}} Status: <span

    style="color: {{$order->status}}"> @switch($order->status) @case('yellow') 入金待ち @break @case('green') 設定中 @break @case('blue') 稼働中 @break @case('red') エラー @break @endswitch </span> </li> @endforeach </ul> フロントくん サーバーから 受け取ったデータが 色に・・・?
  52. class MeddleController extends BaseController { public function orderList(string $userId) {

    $orders = DB::table('orders')->where('owner_id', $userId)->get()->toArray(); $response = new ProductOrderResponse(); $response->data = array_map(function($order) { $data = new ProductOrderData(); $data->productName = $order->productName; switch ($order->status){ case '1' : $data->status = 'yellow'; break; case '2' : $data->status = 'green'; break; case '3' : $data->status = 'blue'; break; case '-1' : $data->status = 'red'; break; default: $data->status = 'gray'; break; } $data->status = $order->status; return $data; }, $orders); return view('meddle.orders', compact('response')); } }
  53. class MeddleController extends BaseController { public function orderList(string $userId) {

    $orders = DB::table('orders')->where('owner_id', $userId)->get()->toArray(); $response = new ProductOrderResponse(); $response->data = array_map(function($order) { $data = new ProductOrderData(); $data->productName = $order->productName; switch ($order->status){ case '1' : $data->status = 'yellow'; break; case '2' : $data->status = 'green'; break; case '3' : $data->status = 'blue'; break; case '-1' : $data->status = 'red'; break; default: $data->status = 'gray'; break; } $data->status = $order->status; return $data; }, $orders); return view('meddle.orders', compact('response')); } }
  54. None
  55. フロントエンドの変更のために

  56. フロントエンドの変更のために バックエンドの変更が必要

  57. どうする?

  58. フロントくん

  59. フロントくん バックエンドもやってもらって どうせならインフラもやってもらって フルスタックくん にレベルアップさせよう

  60. さもなければ

  61. class MeddleController extends BaseController { public function orderList(string $userId) {

    $orders = DB::table('orders')->where('owner_id', $userId)->get()->toArray(); $response = new ProductOrderResponse(); $response->data = array_map(function($order) { $data = new ProductOrderData(); $data->productName = $order->productName; switch ($order->status){ case '1' : $data->status = 'yellow'; break; case '2' : $data->status = 'green'; break; case '3' : $data->status = 'blue'; break; case '-1' : $data->status = 'red'; break; default: $data->status = 'gray'; break; } return $data; }, $orders); return view('meddle.orders', compact('response')); } }
  62. class MeddleController extends BaseController { public function orderList(string $userId) {

    $orders = DB::table('orders')->where('owner_id', $userId)->get()->toArray(); $response = new ProductOrderResponse(); $response->data = array_map(function($order) { $data = new ProductOrderData(); $data->productName = $order->productName; switch ($order->status){ case '1' : $data->status = 'wait'; break; case '2' : $data->status = 'setting'; break; case '3' : $data->status = 'running'; break; case '-1' : $data->status = 'error'; break; default: $data->status = 'undefined'; break; } return $data; }, $orders); return view('meddle.orders', compact('response')); } }
  63. class MeddleController extends BaseController { public function orderList(string $userId) {

    $orders = DB::table('orders')->where('owner_id', $userId)->get()->toArray(); $response = new ProductOrderResponse(); $response->data = array_map(function($order) { $data = new ProductOrderData(); $data->productName = $order->productName; switch ($order->status){ case '1' : $data->status = 'wait'; break; case '2' : $data->status = 'setting'; break; case '3' : $data->status = 'running'; break; case '-1' : $data->status = 'error'; break; default: $data->status = 'undefined'; break; } return $data; }, $orders); return view('meddle.orders', compact('response')); } } フロントエンドの都合は バックエンドに書かないように するのがおすすめ
  64. 第四話 地獄のエラーハンドリング

  65. public function create(Request $request) { $name = $request->input('name'); $user =

    new User(); $result = $user->loadByName($name); if ($user->error) { Logger::Log('error occurred. error:' . $user->dbError); $viewModel = new UserCreateResponse('', ''); return view('user.create', compact('viewModel')); } // ユーザ名重複 if ($result) { $viewModel = new UserCreateResponse('', ''); return view('user.create', compact('viewModel')); } $user->id = uniqid(); $user->name = $name; $result = $user->add(); if (!$result) { Logger::Log('error occurred. error:' . $user->dbError); $viewModel = new UserCreateResponse('', ''); return view('user.create', compact('viewModel')); } $viewModel = new UserCreateResponse($user->id, $user->name); return view('user.create', compact('viewModel')); }
  66. public function create(Request $request) { $name = $request->input('name'); $user =

    new User(); $result = $user->loadByName($name); if ($user->error) { Logger::Log('error occurred. error:' . $user->dbError); $viewModel = new UserCreateResponse('', ''); return view('user.create', compact('viewModel')); } // ユーザ名重複 if ($result) { $viewModel = new UserCreateResponse('', ''); return view('user.create', compact('viewModel')); } $user->id = uniqid(); $user->name = $name; $result = $user->add(); if (!$result) { Logger::Log('error occurred. error:' . $user->dbError); $viewModel = new UserCreateResponse('', ''); return view('user.create', compact('viewModel')); } $viewModel = new UserCreateResponse($user->id, $user->name); return view('user.create', compact('viewModel')); } class User { public $id; public $name; public $dbError; public $error; public function loadByName(string $name){ $this->error = false; try{ $user = DB::table('users')->where('name', $name)->first(); if($user !== null){ $this->id = $user->id; $this->name = $user->name; return true; }else{ return false; } }catch (¥Exception $e){ $this->dbError = $e; $this->error = true; return false; }
  67. public function create(Request $request) { $name = $request->input('name'); $user =

    new User(); $result = $user->loadByName($name); if ($user->error) { Logger::Log('error occurred. error:' . $user->dbError); $viewModel = new UserCreateResponse('', ''); return view('user.create', compact('viewModel')); } // ユーザ名重複 if ($result) { $viewModel = new UserCreateResponse('', ''); return view('user.create', compact('viewModel')); } $user->id = uniqid(); $user->name = $name; $result = $user->add(); if (!$result) { Logger::Log('error occurred. error:' . $user->dbError); $viewModel = new UserCreateResponse('', ''); return view('user.create', compact('viewModel')); } $viewModel = new UserCreateResponse($user->id, $user->name); return view('user.create', compact('viewModel')); }
  68. public function create(Request $request) { $name = $request->input('name'); $user =

    new User(); $result = $user->loadByName($name); if ($user->error) { Logger::Log('error occurred. error:' . $user->dbError); $viewModel = new UserCreateResponse('', ''); return view('user.create', compact('viewModel')); } // ユーザ名重複 if ($result) { $viewModel = new UserCreateResponse('', ''); return view('user.create', compact('viewModel')); } $user->id = uniqid(); $user->name = $name; $result = $user->add(); if (!$result) { Logger::Log('error occurred. error:' . $user->dbError); $viewModel = new UserCreateResponse('', ''); return view('user.create', compact('viewModel')); } $viewModel = new UserCreateResponse($user->id, $user->name); return view('user.create', compact('viewModel')); }
  69. public function create(Request $request) { $name = $request->input('name'); $user =

    new User(); $result = $user->loadByName($name); if ($user->error) { Logger::Log('error occurred. error:' . $user->dbError); $viewModel = new UserCreateResponse('', ''); return view('user.create', compact('viewModel')); } // ユーザ名重複 if ($result) { $viewModel = new UserCreateResponse('', ''); return view('user.create', compact('viewModel')); } $user->id = uniqid(); $user->name = $name; $result = $user->add(); if (!$result) { Logger::Log('error occurred. error:' . $user->dbError); $viewModel = new UserCreateResponse('', ''); return view('user.create', compact('viewModel')); } $viewModel = new UserCreateResponse($user->id, $user->name); return view('user.create', compact('viewModel')); }
  70. public function create(Request $request) { $name = $request->input('name'); $user =

    new User(); $result = $user->loadByName($name); if ($user->error) { Logger::Log('error occurred. error:' . $user->dbError); $viewModel = new UserCreateResponse('', ''); return view('user.create', compact('viewModel')); } // ユーザ名重複 if ($result) { $viewModel = new UserCreateResponse('', ''); return view('user.create', compact('viewModel')); } $user->id = uniqid(); $user->name = $name; $result = $user->add(); if (!$result) { Logger::Log('error occurred. error:' . $user->dbError); $viewModel = new UserCreateResponse('', ''); return view('user.create', compact('viewModel')); } $viewModel = new UserCreateResponse($user->id, $user->name); return view('user.create', compact('viewModel')); } class User { public $id; public $name; public $dbError; public $error; public function add() { $this->error = false; try { DB::table('users')->insert( [ 'id' => $this->id, 'name' => $this->name ] ); return true; } catch (¥Exception $e) { $this->error = true; $this->dbError = $e; return false; } } }
  71. public function create(Request $request) { $name = $request->input('name'); $user =

    new User(); $result = $user->loadByName($name); if ($user->error) { Logger::Log('error occurred. error:' . $user->dbError); $viewModel = new UserCreateResponse('', ''); return view('user.create', compact('viewModel')); } // ユーザ名重複 if ($result) { $viewModel = new UserCreateResponse('', ''); return view('user.create', compact('viewModel')); } $user->id = uniqid(); $user->name = $name; $result = $user->add(); if (!$result) { Logger::Log('error occurred. error:' . $user->dbError); $viewModel = new UserCreateResponse('', ''); return view('user.create', compact('viewModel')); } $viewModel = new UserCreateResponse($user->id, $user->name); return view('user.create', compact('viewModel')); }
  72. public function create(Request $request) { $name = $request->input('name'); $user =

    new User(); $result = $user->loadByName($name); if ($user->error) { Logger::Log('error occurred. error:' . $user->dbError); $viewModel = new UserCreateResponse('', ''); return view('user.create', compact('viewModel')); } // ユーザ名重複 if ($result) { $viewModel = new UserCreateResponse('', ''); return view('user.create', compact('viewModel')); } $user->id = uniqid(); $user->name = $name; $result = $user->add(); if (!$result) { Logger::Log('error occurred. error:' . $user->dbError); $viewModel = new UserCreateResponse('', ''); return view('user.create', compact('viewModel')); } $viewModel = new UserCreateResponse($user->id, $user->name); return view('user.create', compact('viewModel')); }
  73. public function create(Request $request) { $name = $request->input('name'); $user =

    new User(); $result = $user->loadByName($name); if ($user->error) { Logger::Log('error occurred. error:' . $user->dbError); $viewModel = new UserCreateResponse('', ''); return view('user.create', compact('viewModel')); } // ユーザ名重複 if ($result) { $viewModel = new UserCreateResponse('', ''); return view('user.create', compact('viewModel')); } $user->id = uniqid(); $user->name = $name; $result = $user->add(); if (!$result) { Logger::Log('error occurred. error:' . $user->dbError); $viewModel = new UserCreateResponse('', ''); return view('user.create', compact('viewModel')); } $viewModel = new UserCreateResponse($user->id, $user->name); return view('user.create', compact('viewModel')); }
  74. public function create(Request $request) { $name = $request->input('name'); $user =

    new User(); $result = $user->loadByName($name); if ($user->error) { Logger::Log('error occurred. error:' . $user->dbError); $viewModel = new UserCreateResponse('', ''); return view('user.create', compact('viewModel')); } // ユーザ名重複 if ($result) { $viewModel = new UserCreateResponse('', ''); return view('user.create', compact('viewModel')); } $user->id = uniqid(); $user->name = $name; $result = $user->add(); if (!$result) { Logger::Log('error occurred. error:' . $user->dbError); $viewModel = new UserCreateResponse('', ''); return view('user.create', compact('viewModel')); } $viewModel = new UserCreateResponse($user->id, $user->name); return view('user.create', compact('viewModel')); }
  75. public function create(Request $request) { $name = $request->input('name'); $user =

    new User(); $result = $user->loadByName($name); if ($user->error) { Logger::Log('error occurred. error:' . $user->dbError); $viewModel = new UserCreateResponse('', ''); return view('user.create', compact('viewModel')); } // ユーザ名重複 if ($result) { $viewModel = new UserCreateResponse('', ''); return view('user.create', compact('viewModel')); } $user->id = uniqid(); $user->name = $name; $result = $user->add(); if (!$result) { Logger::Log('error occurred. error:' . $user->dbError); $viewModel = new UserCreateResponse('', ''); return view('user.create', compact('viewModel')); } $viewModel = new UserCreateResponse($user->id, $user->name); return view('user.create', compact('viewModel')); }
  76. public function create(Request $request) { $name = $request->input('name'); $user =

    new User(); $result = $user->loadByName($name); if ($user->error) { Logger::Log('error occurred. error:' . $user->dbError); $viewModel = new UserCreateResponse('', ''); return view('user.create', compact('viewModel')); } // ユーザ名重複 if ($result) { $viewModel = new UserCreateResponse('', ''); return view('user.create', compact('viewModel')); } $user->id = uniqid(); $user->name = $name; $result = $user->add(); if (!$result) { Logger::Log('error occurred. error:' . $user->dbError); $viewModel = new UserCreateResponse('', ''); return view('user.create', compact('viewModel')); } $viewModel = new UserCreateResponse($user->id, $user->name); return view('user.create', compact('viewModel')); }
  77. public function create(Request $request) { $name = $request->input('name'); $user =

    new User(); $result = $user->loadByName($name); if ($user->error) { Logger::Log('error occurred. error:' . $user->dbError); $viewModel = new UserCreateResponse('', ''); return view('user.create', compact('viewModel')); } // ユーザ名重複 if ($result) { $viewModel = new UserCreateResponse('', ''); return view('user.create', compact('viewModel')); } $user->id = uniqid(); $user->name = $name; $result = $user->add(); if (!$result) { Logger::Log('error occurred. error:' . $user->dbError); $viewModel = new UserCreateResponse('', ''); return view('user.create', compact('viewModel')); } $viewModel = new UserCreateResponse($user->id, $user->name); return view('user.create', compact('viewModel')); }
  78. public function create( Request $request, UserRepositoryInterface $userRepository ) { $name

    = $request->input('name'); $duplicated = $userRepository->findByName($name); if($duplicated !== null) { $viewModel = UserCreateResponse::fail(); return view('user.create', compact('viewModel')); } $userId = uniqid(); $user = new User($userId, $name); $userRepository->save($user); $viewModel = new UserCreateResponse($user->id, $user->name); return view('user.create', compact('viewModel')); }
  79. public function create( Request $request, UserRepositoryInterface $userRepository ) { $name

    = $request->input('name'); $duplicated = $userRepository->findByName($name); if($duplicated !== null) { $viewModel = UserCreateResponse::fail(); return view('user.create', compact('viewModel')); } $userId = uniqid(); $user = new User($userId, $name); $userRepository->save($user); $viewModel = new UserCreateResponse($user->id, $user->name); return view('user.create', compact('viewModel')); } データの永続化を 担うオブジェクト を DI
  80. public function create( Request $request, UserRepositoryInterface $userRepository ) { $name

    = $request->input('name'); $duplicated = $userRepository->findByName($name); if($duplicated !== null) { $viewModel = UserCreateResponse::fail(); return view('user.create', compact('viewModel')); } $userId = uniqid(); $user = new User($userId, $name); $userRepository->save($user); $viewModel = new UserCreateResponse($user->id, $user->name); return view('user.create', compact('viewModel')); } リポジトリに 再構築・永続化を 依頼する
  81. DB のエラーは リポジトリで 例外投げて終わり (ここでは取り扱わない) public function create( Request $request,

    UserRepositoryInterface $userRepository ) { $name = $request->input('name'); $duplicated = $userRepository->findByName($name); if($duplicated !== null) { $viewModel = UserCreateResponse::fail(); return view('user.create', compact('viewModel')); } $userId = uniqid(); $user = new User($userId, $name); $userRepository->save($user); $viewModel = new UserCreateResponse($user->id, $user->name); return view('user.create', compact('viewModel')); }
  82. 最終話 ソースコードが仕様書だよ

  83. 人は言う

  84. コード読めばわかるじゃん

  85. public function create(Request $request) { $name = $request->input('name'); $user =

    new User(); $result = $user->loadByName($name); if ($user->error) { Logger::Log('error occurred. error:' . $user->dbError); $viewModel = new UserCreateResponse('', ''); return view('user.create', compact('viewModel')); } // ユーザ名重複 if ($result) { $viewModel = new UserCreateResponse('', ''); return view('user.create', compact('viewModel')); } $user->id = uniqid(); $user->name = $name; $result = $user->add(); if (!$result) { Logger::Log('error occurred. error:' . $user->dbError); $viewModel = new UserCreateResponse('', ''); return view('user.create', compact('viewModel')); } $viewModel = new UserCreateResponse($user->id, $user->name); return view('user.create', compact('viewModel')); }
  86. public function create(Request $request) { $name = $request->input('name'); $user =

    new User(); $result = $user->loadByName($name); if ($user->error) { Logger::Log('error occurred. error:' . $user->dbError); $viewModel = new UserCreateResponse('', ''); return view('user.create', compact('viewModel')); } // ユーザ名重複 if ($result) { $viewModel = new UserCreateResponse('', ''); return view('user.create', compact('viewModel')); } $user->id = uniqid(); $user->name = $name; $result = $user->add(); if (!$result) { Logger::Log('error occurred. error:' . $user->dbError); $viewModel = new UserCreateResponse('', ''); return view('user.create', compact('viewModel')); } $viewModel = new UserCreateResponse($user->id, $user->name); return view('user.create', compact('viewModel')); }
  87. public function create(Request $request) { $name = $request->input('name'); $user =

    new User(); $result = $user->loadByName($name); if ($user->error) { Logger::Log('error occurred. error:' . $user->dbError); $viewModel = new UserCreateResponse('', ''); return view('user.create', compact('viewModel')); } // ユーザ名重複 if ($result) { $viewModel = new UserCreateResponse('', ''); return view('user.create', compact('viewModel')); } $user->id = uniqid(); $user->name = $name; $result = $user->add(); if (!$result) { Logger::Log('error occurred. error:' . $user->dbError); $viewModel = new UserCreateResponse('', ''); return view('user.create', compact('viewModel')); } $viewModel = new UserCreateResponse($user->id, $user->name); return view('user.create', compact('viewModel')); }
  88. なにがあったらうれしかったか

  89. テスト

  90. None
  91. データを投入

  92. 表示 データ 変更 データを投入

  93. 表示 データ 変更 データを投入 ソースコードは 仕様書

  94. class UserService { private $userRepository; public function __construct(UserRepositoryInterface $userRepository) {

    $this->userRepository = $userRepository; } public function create(UserCreateRequest $request) { $userId = new UserId(uniqid()); $createdUser = new User($userId, $request->getName()); $this->userRepository->save($createdUser); return new UserCreateResponse($userId->getValue()); } }
  95. class UserService { private $userRepository; public function __construct(UserRepositoryInterface $userRepository) {

    $this->userRepository = $userRepository; } public function create(UserCreateRequest $request) { $userId = new UserId(uniqid()); $createdUser = new User($userId, $request->getName()); $this->userRepository->save($createdUser); return new UserCreateResponse($userId->getValue()); } } class UserController extends BaseController { public function create(UserService $service, Request $request) { $name = $request->input('name'); $request = new UserCreateRequest($name); $response = $service->create($request); $viewModel = new UserCreateViewModel($response->getCreatedUserId(), $name); return view('user.create', compact('viewModel')); } }
  96. class UserService { private $userRepository; public function __construct(UserRepositoryInterface $userRepository) {

    $this->userRepository = $userRepository; } public function create(UserCreateRequest $request) { $userId = new UserId(uniqid()); $createdUser = new User($userId, $request->getName()); $this->userRepository->save($createdUser); return new UserCreateResponse($userId->getValue()); } } class UserRepository implements UserRepositoryInterface { public function findByName(string $name) { $target = DB::table('users')->find('name', $name)->first(); return new User($target->id, $target->name); } public function save(User $user) { DB::table('users')->insert( [ 'id' => $user->id, 'name' => $user->name ] ); } } 本番用
  97. class UserService { private $userRepository; public function __construct(UserRepositoryInterface $userRepository) {

    $this->userRepository = $userRepository; } public function create(UserCreateRequest $request) { $userId = new UserId(uniqid()); $createdUser = new User($userId, $request->getName()); $this->userRepository->save($createdUser); return new UserCreateResponse($userId->getValue()); } } テスト用 class UserRepository implements UserRepositoryInterface { private $db = []; public function findByName(string $name) { $targets = array_filter($this->db, function ($e) use ($name) { return $e->name === $name; }); if (count($targets) > 0) { $target = array_shift($targets); return $this->cloneUser($target); } else { return null; } } public function save(User $user) { $cloned = $this->cloneUser($user); $this->db[$cloned->id] = $cloned; } private function cloneUser(User $user) { return new User($user->id, $user->name); } }
  98. class UserCreateTest extends TestCase { /** * テスト:正常にユーザを生成する * *

    @return void */ public function testValidCreate() { $repository = new InMemoryUserRepository(); $interactor = new UserService($repository); $request = new UserCreateRequest('test-name'); $response = $interactor->create($request); $this->assertNotNull($response); $this->assertNotNull($response->getCreatedUserId()); $userId = new UserId($response->getCreatedUserId()); $saved = $repository->find($userId); $this->assertEquals($saved->getName(), 'test-name'); } /** * テスト:重複エラー * * @return void */ public function testErrorXxx(){
  99. class UserCreateTest extends TestCase { /** * テスト:正常にユーザを生成する * *

    @return void */ public function testValidCreate() { $repository = new InMemoryUserRepository(); $interactor = new UserService($repository); $request = new UserCreateRequest('test-name'); $response = $interactor->create($request); $this->assertNotNull($response); $this->assertNotNull($response->getCreatedUserId()); $userId = new UserId($response->getCreatedUserId()); $saved = $repository->find($userId); $this->assertEquals($saved->getName(), 'test-name'); } /** * テスト:重複エラー * * @return void */ public function testErrorXxx(){ IN を表現
  100. class UserCreateTest extends TestCase { /** * テスト:正常にユーザを生成する * *

    @return void */ public function testValidCreate() { $repository = new InMemoryUserRepository(); $interactor = new UserService($repository); $request = new UserCreateRequest('test-name'); $response = $interactor->create($request); $this->assertNotNull($response); $this->assertNotNull($response->getCreatedUserId()); $userId = new UserId($response->getCreatedUserId()); $saved = $repository->find($userId); $this->assertEquals($saved->getName(), 'test-name'); } /** * テスト:重複エラー * * @return void */ public function testErrorXxx(){ IN を表現 OUT を表現
  101. class UserCreateTest extends TestCase { /** * テスト:正常にユーザを生成する * *

    @return void */ public function testValidCreate() { $repository = new InMemoryUserRepository(); $interactor = new UserService($repository); $request = new UserCreateRequest('test-name'); $response = $interactor->create($request); $this->assertNotNull($response); $this->assertNotNull($response->getCreatedUserId()); $userId = new UserId($response->getCreatedUserId()); $saved = $repository->find($userId); $this->assertEquals($saved->getName(), 'test-name'); } /** * テスト:重複エラー * * @return void */ public function testErrorXxx(){ IN を表現 OUT を表現 これこそ仕様書
  102. 最終話 ソースコードが仕様書だよ 第四話 地獄のエラーハンドリング 第参話 知りすぎたコントローラ 第壱話 独自プロトコル 第弐話 利口な

    UI
  103. エンドロール

  104. None
  105. None
  106. None
  107. None
  108. SWIMMING IN HELL

  109. Auther Masanobu Naruse Company GMO Internet, Inc. SWIMMING IN HELL