Upgrade to Pro — share decks privately, control downloads, hide ads and more …

WEB アプリケーション設計入門 / Introduction to web application design

nrs
December 12, 2020

WEB アプリケーション設計入門 / Introduction to web application design

PHP Conference Japan 2020
トーク前提の資料です。そのため、トークがないと理解が難しいかもしれません。
https://youtu.be/UTKJ-Lgn3aI?t=36
※冒頭音声が小さいです。マイクを手に持ってから聞こえやすくなると思います。

資料中の ADOP については下記を参照ください。
https://nrslib.com/adop/

# Abstract
https://fortee.jp/phpcon-2020/proposal/da5b9d99-e5a6-4f51-adea-1f1c10d99020

# Ref
https://github.com/nrslib/scrum-app-sample-php
https://github.com/nrslib/repository-support-php

# URL
Togetter: https://togetter.com/li/1635360
YouTube: https://www.youtube.com/c/narusemi
HomePage: https://nrslib.com
Twitter: https://twitter.com/nrslib

nrs

December 12, 2020
Tweet

More Decks by nrs

Other Decks in Programming

Transcript

  1. 10年
    先を見据えて作る
    PHP WEBアプリケーション設計入門

    View Slide

  2. 2
    10年

    View Slide

  3. 3
    10年続くことなんて
    そうそうなくない?

    View Slide

  4. 4
    2000
    2005
    2010
    2015
    2020

    View Slide

  5. 5
    2000
    2005
    2010
    2015
    2020

    View Slide

  6. 6
    2000
    2005
    2010
    2015
    2020

    View Slide

  7. 7
    2000
    2005
    2010
    2015
    2020

    View Slide

  8. 8
    2000
    2005
    2010
    2015
    2020

    View Slide

  9. 9
    2000
    2005
    2010
    2015
    2020

    View Slide

  10. 10
    2000
    2005
    2010
    2015
    2020

    View Slide

  11. 11
    2000
    2005
    2010
    2015
    2020

    View Slide

  12. 12
    2000
    2005
    2010
    2015
    2020

    View Slide

  13. 13
    2000
    2005
    2010
    2015
    2020

    View Slide

  14. 14
    2000
    2005
    2010
    2015
    2020
    SDプラン

    View Slide

  15. 15
    2000
    2005
    2010
    2015
    2020
    SDプラン RSプラン

    View Slide

  16. 16
    10年の月日が
    もたらすもの

    View Slide

  17. 17
    10年
    ×

    View Slide

  18. 18
    2000
    2005
    2010
    2015
    2020

    View Slide

  19. 19
    2000
    2005
    2010
    2015
    2020
    PHP Tools
    (1995)

    View Slide

  20. 20
    2000
    2005
    2010
    2015
    2020
    PHP Tools
    (1995)
    PHP2
    (1996)
    PHP3
    (1998)
    PHP4
    (2000) PHP5
    (2004)
    PHP6 PHP7
    (2015)
    PHP8
    (2020)

    View Slide

  21. 21
    2000
    2005
    2010
    2015
    2020
    PHP Tools
    (1995)
    PHP2
    (1996)
    PHP3
    (1998)
    PHP4
    (2000) PHP5
    (2004)
    PHP6 PHP7
    (2015)
    PHP8
    (2020)
    phrame
    (2003)
    mojavi
    (2004)
    Agavi
    (2005)
    Ethna
    (2005)
    Maple
    (2005)
    CakePHP
    (2006)
    Symfony
    (2007)
    Zend Framework
    (2007)
    Laravel
    (2011)
    CodeIgniter
    (2006)
    FuelPHP
    (2011)
    Phalcon
    (2012)
    Yii
    (2008)

    View Slide

  22. 22
    2000
    2005
    2010
    2015
    2020
    PHP Tools
    (1995)
    PHP2
    (1996)
    PHP3
    (1998)
    PHP4
    (2000) PHP5
    (2004)
    PHP6 PHP7
    (2015)
    PHP8
    (2020)
    phrame
    (2003)
    mojavi
    (2004)
    Agavi
    (2005)
    Ethna
    (2005)
    Maple
    (2005)
    CakePHP
    (2006)
    Symfony
    (2007)
    Zend Framework
    (2007)
    Laravel
    (2011)
    CodeIgniter
    (2006)
    FuelPHP
    (2011)
    Phalcon
    (2012)
    Yii
    (2008)

    View Slide

  23. 23
    2000
    2005
    2010
    2015
    2020
    PHP Tools
    (1995)
    PHP2
    (1996)
    PHP3
    (1998)
    PHP4
    (2000) PHP5
    (2004)
    PHP6 PHP7
    (2015)
    PHP8
    (2020)
    phrame
    (2003)
    mojavi
    (2004)
    Agavi
    (2005)
    Ethna
    (2005)
    Maple
    (2005)
    CakePHP
    (2006)
    Symfony
    (2007)
    Zend Framework
    (2007)
    Laravel
    (2011)
    CodeIgniter
    (2006)
    FuelPHP
    (2011)
    Phalcon
    (2012)
    Yii
    (2008)

    View Slide

  24. 24
    2000
    2005
    2010
    2015
    2020
    PHP Tools
    (1995)
    PHP2
    (1996)
    PHP3
    (1998)
    PHP4
    (2000) PHP5
    (2004)
    PHP6 PHP7
    (2015)
    PHP8
    (2020)
    phrame
    (2003)
    mojavi
    (2004)
    Agavi
    (2005)
    Ethna
    (2005)
    Maple
    (2005)
    CakePHP
    (2006)
    Symfony
    (2007)
    Zend Framework
    (2007)
    Laravel
    (2011)
    CodeIgniter
    (2006)
    FuelPHP
    (2011)
    Phalcon
    (2012)
    Yii
    (2008)

    View Slide

  25. 25
    2000
    2005
    2010
    2015
    2020
    PHP Tools
    (1995)
    PHP2
    (1996)
    PHP3
    (1998)
    PHP4
    (2000) PHP5
    (2004)
    PHP6 PHP7
    (2015)
    PHP8
    (2020)
    phrame
    (2003)
    mojavi
    (2004)
    Agavi
    (2005)
    Ethna
    (2005)
    Maple
    (2005)
    CakePHP
    (2006)
    Symfony
    (2007)
    Zend Framework
    (2007)
    Laravel
    (2011)
    CodeIgniter
    (2006)
    FuelPHP
    (2011)
    Phalcon
    (2012)
    Yii
    (2008)
    1
    (2011)

    View Slide

  26. 26
    2000
    2005
    2010
    2015
    2020
    PHP Tools
    (1995)
    PHP2
    (1996)
    PHP3
    (1998)
    PHP4
    (2000) PHP5
    (2004)
    PHP6 PHP7
    (2015)
    PHP8
    (2020)
    phrame
    (2003)
    mojavi
    (2004)
    Agavi
    (2005)
    Ethna
    (2005)
    Maple
    (2005)
    CakePHP
    (2006)
    Symfony
    (2007)
    Zend Framework
    (2007)
    Laravel
    (2011)
    CodeIgniter
    (2006)
    FuelPHP
    (2011)
    Phalcon
    (2012)
    Yii
    (2008)
    2
    (2011)
    1
    (2011)

    View Slide

  27. 27
    2000
    2005
    2010
    2015
    2020
    PHP Tools
    (1995)
    PHP2
    (1996)
    PHP3
    (1998)
    PHP4
    (2000) PHP5
    (2004)
    PHP6 PHP7
    (2015)
    PHP8
    (2020)
    phrame
    (2003)
    mojavi
    (2004)
    Agavi
    (2005)
    Ethna
    (2005)
    Maple
    (2005)
    CakePHP
    (2006)
    Symfony
    (2007)
    Zend Framework
    (2007)
    Laravel
    (2011)
    CodeIgniter
    (2006)
    FuelPHP
    (2011)
    Phalcon
    (2012)
    Yii
    (2008)
    2
    (2011)
    3
    (2012)
    1
    (2011)

    View Slide

  28. 28
    2000
    2005
    2010
    2015
    2020
    PHP Tools
    (1995)
    PHP2
    (1996)
    PHP3
    (1998)
    PHP4
    (2000) PHP5
    (2004)
    PHP6 PHP7
    (2015)
    PHP8
    (2020)
    phrame
    (2003)
    mojavi
    (2004)
    Agavi
    (2005)
    Ethna
    (2005)
    Maple
    (2005)
    CakePHP
    (2006)
    Symfony
    (2007)
    Zend Framework
    (2007)
    Laravel
    (2011)
    CodeIgniter
    (2006)
    FuelPHP
    (2011)
    Phalcon
    (2012)
    Yii
    (2008)
    2
    (2011)
    3
    (2012)
    4.0
    (2013)
    4.1
    (2013)
    4.2
    (2014)
    1
    (2011)

    View Slide

  29. 29
    2000
    2005
    2010
    2015
    2020
    PHP Tools
    (1995)
    PHP2
    (1996)
    PHP3
    (1998)
    PHP4
    (2000) PHP5
    (2004)
    PHP6 PHP7
    (2015)
    PHP8
    (2020)
    phrame
    (2003)
    mojavi
    (2004)
    Agavi
    (2005)
    Ethna
    (2005)
    Maple
    (2005)
    CakePHP
    (2006)
    Symfony
    (2007)
    Zend Framework
    (2007)
    Laravel
    (2011)
    CodeIgniter
    (2006)
    FuelPHP
    (2011)
    Phalcon
    (2012)
    Yii
    (2008)
    2
    (2011)
    3
    (2012)
    4.0
    (2013)
    5.0
    (2015)
    5.1
    (2015)
    4.1
    (2013)
    4.2
    (2014)
    5.2
    (2015)
    5.3
    (2016)
    5.4
    (2017)
    5.5
    (2017)
    5.6
    (2018)
    5.7
    (2018)
    5.8
    (2019)
    1
    (2011)

    View Slide

  30. 30
    2000
    2005
    2010
    2015
    2020
    PHP Tools
    (1995)
    PHP2
    (1996)
    PHP3
    (1998)
    PHP4
    (2000) PHP5
    (2004)
    PHP6 PHP7
    (2015)
    PHP8
    (2020)
    phrame
    (2003)
    mojavi
    (2004)
    Agavi
    (2005)
    Ethna
    (2005)
    Maple
    (2005)
    CakePHP
    (2006)
    Symfony
    (2007)
    Zend Framework
    (2007)
    Laravel
    (2011)
    CodeIgniter
    (2006)
    FuelPHP
    (2011)
    Phalcon
    (2012)
    Yii
    (2008)
    2
    (2011)
    3
    (2012)
    4.0
    (2013)
    5.0
    (2015)
    5.1
    (2015)
    4.1
    (2013)
    4.2
    (2014)
    5.2
    (2015)
    5.3
    (2016)
    5.4
    (2017)
    5.5
    (2017)
    5.6
    (2018)
    5.7
    (2018)
    5.8
    (2019)
    6
    (2019)
    7
    (2020)
    8
    (2020)
    1
    (2011)

    View Slide

  31. 31
    2000
    2005
    2010
    2015
    2020
    PHP Tools
    (1995)
    PHP2
    (1996)
    PHP3
    (1998)
    PHP4
    (2000) PHP5
    (2004)
    PHP6 PHP7
    (2015)
    PHP8
    (2020)
    phrame
    (2003)
    mojavi
    (2004)
    Agavi
    (2005)
    Ethna
    (2005)
    Maple
    (2005)
    CakePHP
    (2006)
    Symfony
    (2007)
    Zend Framework
    (2007)
    Laravel
    (2011)
    CodeIgniter
    (2006)
    FuelPHP
    (2011)
    Phalcon
    (2012)
    Yii
    (2008)
    2
    (2011)
    3
    (2012)
    4.0
    (2013)
    5.0
    (2015)
    5.1
    (2015)
    4.1
    (2013)
    4.2
    (2014)
    5.2
    (2015)
    5.3
    (2016)
    5.4
    (2017)
    5.5
    (2017)
    5.6
    (2018)
    5.7
    (2018)
    5.8
    (2019)
    6
    (2019)
    7
    (2020)
    8
    (2020)
    1
    (2011)

    View Slide

  32. 32
    どんな手立てを取れるか

    View Slide

  33. 33

    View Slide

  34. 34

    View Slide

  35. 35

    View Slide

  36. 36

    View Slide

  37. 37

    View Slide

  38. 10年
    先を見据えて作る
    PHP WEBアプリケーション設計入門

    View Slide

  39. View Slide

  40. View Slide

  41. View Slide

  42. View Slide

  43. View Slide

  44. 44
    2000
    2005
    2010
    2015
    2020
    PHP Tools
    (1995)
    PHP2
    (1996)
    PHP3
    (1998)
    PHP4
    (2000) PHP5
    (2004)
    PHP6 PHP7
    (2015)
    PHP8
    (2020)
    phrame
    (2003)
    mojavi
    (2004)
    Agavi
    (2005)
    Ethna
    (2005)
    Maple
    (2005)
    CakePHP
    (2006)
    Symfony
    (2007)
    Zend Framework
    (2007)
    Laravel
    (2011)
    CodeIgniter
    (2006)
    FuelPHP
    (2011)
    Phalcon
    (2012)
    Yii
    (2008)

    View Slide

  45. 45

    View Slide

  46. 46
    2000
    2005
    2010
    2015
    2020
    PHP Tools
    (1995)
    PHP2
    (1996)
    PHP3
    (1998)
    PHP4
    (2000) PHP5
    (2004)
    PHP6 PHP7
    (2015)
    PHP8
    (2020)
    phrame
    (2003)
    mojavi
    (2004)
    Agavi
    (2005)
    Ethna
    (2005)
    Maple
    (2005)
    CakePHP
    (2006)
    Symfony
    (2007)
    Zend Framework
    (2007)
    Laravel
    (2011)
    CodeIgniter
    (2006)
    FuelPHP
    (2011)
    Phalcon
    (2012)
    Yii
    (2008)
    候補があがったら
    ・チームの士気が上がるか
    ・10年後に残ってそうか
    の観点で選ぶ

    View Slide

  47. 47
    2000
    2005
    2010
    2015
    2020
    PHP Tools
    (1995)
    PHP2
    (1996)
    PHP3
    (1998)
    PHP4
    (2000) PHP5
    (2004)
    PHP6 PHP7
    (2015)
    PHP8
    (2020)
    phrame
    (2003)
    mojavi
    (2004)
    Agavi
    (2005)
    Ethna
    (2005)
    Maple
    (2005)
    CakePHP
    (2006)
    Symfony
    (2007)
    Zend Framework
    (2007)
    Laravel
    (2011)
    CodeIgniter
    (2006)
    FuelPHP
    (2011)
    Phalcon
    (2012)
    Yii
    (2008)
    候補があがったら
    ・チームの士気が上がるか
    ・10年後に残ってそうか
    の観点で選ぶ
    10年残るかは運次第

    View Slide

  48. 48

    View Slide

  49. 49

    View Slide

  50. 50

    View Slide

  51. View Slide

  52. 52

    View Slide

  53. 53

    View Slide

  54. 54
    https://alistair.cockburn.us/hexagonal-architecture/
    https://blog.cleancoder.com/uncle-bob/2012/08/13/the-clean-architecture.html
    User Interface
    Application
    Domain
    Infrastructure

    View Slide

  55. 55
    https://alistair.cockburn.us/hexagonal-architecture/
    https://blog.cleancoder.com/uncle-bob/2012/08/13/the-clean-architecture.html
    User Interface
    Application
    Domain
    Infrastructure

    View Slide

  56. 56
    https://alistair.cockburn.us/hexagonal-architecture/
    https://blog.cleancoder.com/uncle-bob/2012/08/13/the-clean-architecture.html
    User Interface
    Application
    Domain
    Infrastructure

    View Slide

  57. 57
    User Interface
    Application
    Domain
    Infrastructure

    View Slide

  58. 58
    User Interface
    Application
    Domain
    Infrastructure
    OK

    View Slide

  59. 59
    User Interface
    Application
    Domain
    Infrastructure
    OK
    NG

    View Slide

  60. 60
    User Interface
    Application
    Domain
    Infrastructure
    OK
    NG
    MVC
    Framework

    View Slide

  61. View Slide

  62. 62
    https://github.com/nrslib/scrum-app-sample-php

    View Slide

  63. 63

    View Slide

  64. 64

    View Slide

  65. 65

    View Slide

  66. 66

    View Slide

  67. 67
    class BackLogController extends Controller
    {
    ...
    public function postAddUserStory(
    Request $request,
    BackLogApplicationService $applicationService
    ) {
    $request->validate([
    "story" => "required"
    ]);
    $story = $request->input("story");
    $command = new AddUserStoryCommand($story);
    $applicationService->addUserStory($command);
    return redirect()->action([BackLogController::class, "index"]);
    }
    }

    View Slide

  68. 68
    class BackLogController extends Controller
    {
    ...
    public function postAddUserStory(
    Request $request,
    BackLogApplicationService $applicationService
    ) {
    $request->validate([
    "story" => "required"
    ]);
    $story = $request->input("story");
    $command = new AddUserStoryCommand($story);
    $applicationService->addUserStory($command);
    return redirect()->action([BackLogController::class, "index"]);
    }
    }

    View Slide

  69. 69
    class BackLogController extends Controller
    {
    ...
    public function postAddUserStory(
    Request $request,
    BackLogApplicationService $applicationService
    ) {
    $request->validate([
    "story" => "required"
    ]);
    $story = $request->input("story");
    $command = new AddUserStoryCommand($story);
    $applicationService->addUserStory($command);
    return redirect()->action([BackLogController::class, "index"]);
    }
    }

    View Slide

  70. 70
    class BackLogController extends Controller
    {
    ...
    public function postAddUserStory(
    Request $request,
    BackLogApplicationService $applicationService
    ) {
    $request->validate([
    "story" => "required"
    ]);
    $story = $request->input("story");
    $command = new AddUserStoryCommand($story);
    $applicationService->addUserStory($command);
    return redirect()->action([BackLogController::class, "index"]);
    }
    }

    View Slide

  71. 71
    class BackLogController extends Controller
    {
    ...
    public function postAddUserStory(
    Request $request,
    BackLogApplicationService $applicationService
    ) {
    $request->validate([
    "story" => "required"
    ]);
    $story = $request->input("story");
    $command = new AddUserStoryCommand($story);
    $applicationService->addUserStory($command);
    return redirect()->action([BackLogController::class, "index"]);
    }
    }

    View Slide

  72. 72
    class BackLogController extends Controller
    {
    ...
    public function postAddUserStory(
    Request $request,
    BackLogApplicationService $applicationService
    ) {
    $request->validate([
    "story" => "required"
    ]);
    $story = $request->input("story");
    $command = new AddUserStoryCommand($story);
    $applicationService->addUserStory($command);
    return redirect()->action([BackLogController::class, "index"]);
    }
    }

    View Slide

  73. 73
    class BackLogController extends Controller
    {
    ...
    public function postAddUserStory(
    Request $request,
    BackLogApplicationService $applicationService
    ) {
    $request->validate([
    "story" => "required"
    ]);
    $story = $request->input("story");
    $command = new AddUserStoryCommand($story);
    $applicationService->addUserStory($command);
    return redirect()->action([BackLogController::class, "index"]);
    }
    }

    View Slide

  74. 74
    class BackLogController extends Controller
    {
    ...
    public function postAddUserStory(
    Request $request,
    BackLogApplicationService $applicationService
    ) {
    $request->validate([
    "story" => "required"
    ]);
    $story = $request->input("story");
    $command = new AddUserStoryCommand($story);
    $applicationService->addUserStory($command);
    return redirect()->action([BackLogController::class, "index"]);
    }
    }

    View Slide

  75. 75
    class BackLogController extends Controller
    {
    ...
    public function postAddUserStory(
    Request $request,
    BackLogApplicationService $applicationService
    ) {
    $request->validate([
    "story" => "required"
    ]);
    $story = $request->input("story");
    $command = new AddUserStoryCommand($story);
    $applicationService->addUserStory($command);
    return redirect()->action([BackLogController::class, "index"]);
    }
    }

    View Slide

  76. 76
    class BackLogController extends Controller
    {
    ...
    public function postAddUserStory(
    Request $request,
    BackLogApplicationService $applicationService
    ) {
    $request->validate([
    "story" => "required"
    ]);
    $story = $request->input("story");
    $command = new AddUserStoryCommand($story);
    $applicationService->addUserStory($command);
    return redirect()->action([BackLogController::class, "index"]);
    }
    }

    View Slide

  77. 77
    class BackLogController extends Controller
    {
    ...
    public function postAddUserStory(
    Request $request,
    BackLogApplicationService $applicationService
    ) {
    $request->validate([
    "story" => "required"
    ]);
    $story = $request->input("story");
    $command = new AddUserStoryCommand($story);
    $applicationService->addUserStory($command);
    return redirect()->action([BackLogController::class, "index"]);
    }
    }

    View Slide

  78. 78
    class BackLogController extends Controller
    {
    ...
    public function postAddUserStory(
    Request $request,
    BackLogApplicationService $applicationService
    ) {
    $request->validate([
    "story" => "required"
    ]);
    $story = $request->input("story");
    $command = new AddUserStoryCommand($story);
    $applicationService->addUserStory($command);
    return redirect()->action([BackLogController::class, "index"]);
    }
    }

    View Slide

  79. 79
    class BackLogController extends Controller
    {
    ...
    public function postAddUserStory(
    Request $request,
    BackLogApplicationService $applicationService
    ) {
    $request->validate([
    "story" => "required"
    ]);
    $story = $request->input("story");
    $command = new AddUserStoryCommand($story);
    $applicationService->addUserStory($command);
    return redirect()->action([BackLogController::class, "index"]);
    }
    }

    View Slide

  80. 80
    class BackLogController extends Controller
    {
    ...
    public function postAddUserStory(
    Request $request,
    BackLogApplicationService $applicationService
    ) {
    $request->validate([
    "story" => "required"
    ]);
    $story = $request->input("story");
    $command = new AddUserStoryCommand($story);
    $applicationService->addUserStory($command);
    return redirect()->action([BackLogController::class, "index"]);
    }
    }

    View Slide

  81. 81
    class BackLogController extends Controller
    {
    ...
    public function postAddUserStory(
    Request $request,
    BackLogApplicationService $applicationService
    ) {
    $request->validate([
    "story" => "required"
    ]);
    $story = $request->input("story");
    $command = new AddUserStoryCommand($story);
    $applicationService->addUserStory($command);
    return redirect()->action([BackLogController::class, "index"]);
    }
    }

    View Slide

  82. 82
    class BackLogApplicationService
    {
    /** @var Transaction */
    private $transaction;
    /** @var UserContextInterface */
    private $userContext;
    /** @var UserStoryRepositoryInterface */
    private $userStoryRepository;
    public function __construct(
    Transaction $transaction,
    UserContextInterface $userContext,
    UserStoryRepositoryInterface $userStoryRepository
    ) {
    $this->transaction = $transaction;
    $this->userContext = $userContext;
    $this->userStoryRepository = $userStoryRepository;
    }
    public function addUserStory(AddUserStoryCommand $command)
    {
    $this->transaction->scope(function () use ($command) {
    $id = new UserStoryId(uniqid());
    $story = new UserStory(
    $id,
    $command->getStory(),
    $this->userContext->getId(),
    $command->getDemo()
    );
    $this->userStoryRepository->save($story);
    });
    }
    }

    View Slide

  83. 83
    class BackLogApplicationService
    {
    /** @var Transaction */
    private $transaction;
    /** @var UserContextInterface */
    private $userContext;
    /** @var UserStoryRepositoryInterface */
    private $userStoryRepository;
    public function __construct(
    Transaction $transaction,
    UserContextInterface $userContext,
    UserStoryRepositoryInterface $userStoryRepository
    ) {
    $this->transaction = $transaction;
    $this->userContext = $userContext;
    $this->userStoryRepository = $userStoryRepository;
    }
    public function addUserStory(AddUserStoryCommand $command)
    {
    $this->transaction->scope(function () use ($command) {
    $id = new UserStoryId(uniqid());
    $story = new UserStory(
    $id,
    $command->getStory(),
    $this->userContext->getId(),
    $command->getDemo()
    );
    $this->userStoryRepository->save($story);
    });
    }
    }

    View Slide

  84. 84
    class BackLogApplicationService
    {
    /** @var Transaction */
    private $transaction;
    /** @var UserContextInterface */
    private $userContext;
    /** @var UserStoryRepositoryInterface */
    private $userStoryRepository;
    ...
    public function addUserStory(AddUserStoryCommand $command)
    {
    $this->transaction->scope(function () use ($command) {
    $id = new UserStoryId(uniqid());
    $story = new UserStory(
    $id,
    $command->getStory(),
    $this->userContext->getId(),
    $command->getDemo()
    );
    $this->userStoryRepository->save($story);
    });
    }
    }

    View Slide

  85. 85
    class BackLogApplicationService
    {
    /** @var Transaction */
    private $transaction;
    /** @var UserContextInterface */
    private $userContext;
    /** @var UserStoryRepositoryInterface */
    private $userStoryRepository;
    ...
    public function addUserStory(AddUserStoryCommand $command)
    {
    $this->transaction->scope(function () use ($command) {
    $id = new UserStoryId(uniqid());
    $story = new UserStory(
    $id,
    $command->getStory(),
    $this->userContext->getId(),
    $command->getDemo()
    );
    $this->userStoryRepository->save($story);
    });
    }
    }

    View Slide

  86. 86
    class BackLogApplicationService
    {
    /** @var Transaction */
    private $transaction;
    /** @var UserContextInterface */
    private $userContext;
    /** @var UserStoryRepositoryInterface */
    private $userStoryRepository;
    ...
    public function addUserStory(AddUserStoryCommand $command)
    {
    $this->transaction->scope(function () use ($command) {
    $id = new UserStoryId(uniqid());
    $story = new UserStory(
    $id,
    $command->getStory(),
    $this->userContext->getId(),
    $command->getDemo()
    );
    $this->userStoryRepository->save($story);
    });
    }
    }

    View Slide

  87. 87
    class BackLogApplicationService
    {
    /** @var Transaction */
    private $transaction;
    /** @var UserContextInterface */
    private $userContext;
    /** @var UserStoryRepositoryInterface */
    private $userStoryRepository;
    ...
    public function addUserStory(AddUserStoryCommand $command)
    {
    $this->transaction->scope(function () use ($command) {
    $id = new UserStoryId(uniqid());
    $story = new UserStory(
    $id,
    $command->getStory(),
    $this->userContext->getId(),
    $command->getDemo()
    );
    $this->userStoryRepository->save($story);
    });
    }
    }

    View Slide

  88. 88
    class BackLogApplicationService
    {
    /** @var Transaction */
    private $transaction;
    /** @var UserContextInterface */
    private $userContext;
    /** @var UserStoryRepositoryInterface */
    private $userStoryRepository;
    ...
    public function addUserStory(AddUserStoryCommand $command)
    {
    $this->transaction->scope(function () use ($command) {
    $id = new UserStoryId(uniqid());
    $story = new UserStory(
    $id,
    $command->getStory(),
    $this->userContext->getId(),
    $command->getDemo()
    );
    $this->userStoryRepository->save($story);
    });
    }
    }

    View Slide

  89. 89
    class BackLogApplicationService
    {
    /** @var Transaction */
    private $transaction;
    /** @var UserContextInterface */
    private $userContext;
    /** @var UserStoryRepositoryInterface */
    private $userStoryRepository;
    ...
    public function addUserStory(AddUserStoryCommand $command)
    {
    $this->transaction->scope(function () use ($command) {
    $id = new UserStoryId(uniqid());
    $story = new UserStory(
    $id,
    $command->getStory(),
    $this->userContext->getId(),
    $command->getDemo()
    );
    $this->userStoryRepository->save($story);
    });
    }
    }

    View Slide

  90. 90
    class BackLogApplicationService
    {
    /** @var Transaction */
    private $transaction;
    /** @var UserContextInterface */
    private $userContext;
    /** @var UserStoryRepositoryInterface */
    private $userStoryRepository;
    ...
    public function addUserStory(AddUserStoryCommand $command)
    {
    $this->transaction->scope(function () use ($command) {
    $id = new UserStoryId(uniqid());
    $story = new UserStory(
    $id,
    $command->getStory(),
    $this->userContext->getId(),
    $command->getDemo()
    );
    $this->userStoryRepository->save($story);
    });
    }
    }

    View Slide

  91. 91
    class BackLogApplicationService
    {
    /** @var Transaction */
    private $transaction;
    /** @var UserContextInterface */
    private $userContext;
    /** @var UserStoryRepositoryInterface */
    private $userStoryRepository;
    ...
    public function addUserStory(AddUserStoryCommand $command)
    {
    $this->transaction->scope(function () use ($command) {
    $id = new UserStoryId(uniqid());
    $story = new UserStory(
    $id,
    $command->getStory(),
    $this->userContext->getId(),
    $command->getDemo()
    );
    $this->userStoryRepository->save($story);
    });
    }
    }

    View Slide

  92. 92
    class BackLogApplicationService
    {
    /** @var Transaction */
    private $transaction;
    /** @var UserContextInterface */
    private $userContext;
    /** @var UserStoryRepositoryInterface */
    private $userStoryRepository;
    ...
    public function addUserStory(AddUserStoryCommand $command)
    {
    $this->transaction->scope(function () use ($command) {
    $id = new UserStoryId(uniqid());
    $story = new UserStory(
    $id,
    $command->getStory(),
    $this->userContext->getId(),
    $command->getDemo()
    );
    $this->userStoryRepository->save($story);
    });
    }
    }

    View Slide

  93. 93
    class BackLogApplicationService
    {
    /** @var Transaction */
    private $transaction;
    /** @var UserContextInterface */
    private $userContext;
    /** @var UserStoryRepositoryInterface */
    private $userStoryRepository;
    ...
    public function addUserStory(AddUserStoryCommand $command)
    {
    $this->transaction->scope(function () use ($command) {
    $id = new UserStoryId(uniqid());
    $story = new UserStory(
    $id,
    $command->getStory(),
    $this->userContext->getId(),
    $command->getDemo()
    );
    $this->userStoryRepository->save($story);
    });
    }
    }

    View Slide

  94. 94
    class BackLogApplicationService
    {
    /** @var Transaction */
    private $transaction;
    /** @var UserContextInterface */
    private $userContext;
    /** @var UserStoryRepositoryInterface */
    private $userStoryRepository;
    ...
    public function addUserStory(AddUserStoryCommand $command)
    {
    $this->transaction->scope(function () use ($command) {
    $id = new UserStoryId(uniqid());
    $story = new UserStory(
    $id,
    $command->getStory(),
    $this->userContext->getId(),
    $command->getDemo()
    );
    $this->userStoryRepository->save($story);
    });
    }
    }

    View Slide

  95. 95
    interface UserStoryRepositoryInterface
    {
    public function find(UserStoryId $id): ?UserStory;
    public function save(UserStory $userStory): void;
    }

    View Slide

  96. 96
    class EloquentUserStoryRepository implements UserStoryRepositoryInterface
    {
    ...
    public function save(UserStory $userStory): void
    {
    UserStoryDataModel::updateOrCreate(
    ["id" => $userStory->getId()->getValue()],
    [
    "story" => $userStory->getStory(),
    "author" => $userStory->getAuthor()->getValue(),
    "demo" => $userStory->getDemo(),
    "estimate" => $userStory->getEstimate(),
    "seq" => $userStory->getSeq()
    ]
    );
    }
    }

    View Slide

  97. 97
    class EloquentUserStoryRepository implements UserStoryRepositoryInterface
    {
    ...
    public function save(UserStory $userStory): void
    {
    UserStoryDataModel::updateOrCreate(
    ["id" => $userStory->getId()->getValue()],
    [
    "story" => $userStory->getStory(),
    "author" => $userStory->getAuthor()->getValue(),
    "demo" => $userStory->getDemo(),
    "estimate" => $userStory->getEstimate(),
    "seq" => $userStory->getSeq()
    ]
    );
    }
    }

    View Slide

  98. 98
    User Interface
    Application
    Domain
    Infrastructure
    OK
    NG
    MVC
    Framework

    View Slide

  99. 99

    View Slide

  100. 100

    View Slide

  101. View Slide

  102. 102

    View Slide

  103. 103

    View Slide

  104. 104

    View Slide

  105. 105

    View Slide

  106. 106

    View Slide

  107. 107

    View Slide

  108. 108

    View Slide

  109. View Slide

  110. 110
    class BackLogApplicationService
    {
    ...
    public function addUserStory(AddUserStoryCommand $command)
    {
    $this->transaction->scope(function () use ($command) {
    $id = new UserStoryId(uniqid());
    $story = new UserStory(
    $id,
    $command->getStory(),
    $this->userContext->getId(),
    $command->getDemo()
    );
    $repository = new EloquentUserStoryRepository();
    $repository->save($story);
    });
    }
    }

    View Slide

  111. 111
    class BackLogApplicationService
    {
    ...
    public function addUserStory(AddUserStoryCommand $command)
    {
    $this->transaction->scope(function () use ($command) {
    $id = new UserStoryId(uniqid());
    $story = new UserStory(
    $id,
    $command->getStory(),
    $this->userContext->getId(),
    $command->getDemo()
    );
    $repository = new EloquentUserStoryRepository();
    $repository->save($story);
    });
    }
    }

    View Slide

  112. 112
    class BackLogApplicationService
    {
    /** @var UserStoryRepositoryInterface */
    private $userStoryRepository;
    ...
    public function addUserStory(AddUserStoryCommand $command)
    {
    $this->transaction->scope(function () use ($command) {
    $id = new UserStoryId(uniqid());
    $story = new UserStory(
    $id,
    $command->getStory(),
    $this->userContext->getId(),
    $command->getDemo()
    );
    $this->userStoryRepository->save($story);
    });
    }
    }

    View Slide

  113. 113
    class BackLogApplicationService
    {
    /** @var UserStoryRepositoryInterface */
    private $userStoryRepository;
    ...
    public function addUserStory(AddUserStoryCommand $command)
    {
    $this->transaction->scope(function () use ($command) {
    $id = new UserStoryId(uniqid());
    $story = new UserStory(
    $id,
    $command->getStory(),
    $this->userContext->getId(),
    $command->getDemo()
    );
    $this->userStoryRepository->save($story);
    });
    }
    }

    View Slide

  114. 114
    class BackLogApplicationService
    {
    /** @var Transaction */
    private $transaction;
    /** @var UserContextInterface */
    private $userContext;
    /** @var UserStoryRepositoryInterface */
    private $userStoryRepository;
    public function __construct(
    Transaction $transaction,
    UserContextInterface $userContext,
    UserStoryRepositoryInterface $userStoryRepository
    ) {
    $this->transaction = $transaction;
    $this->userContext = $userContext;
    $this->userStoryRepository = $userStoryRepository;
    }
    ...
    }

    View Slide

  115. 115
    class BackLogApplicationService
    {
    /** @var Transaction */
    private $transaction;
    /** @var UserContextInterface */
    private $userContext;
    /** @var UserStoryRepositoryInterface */
    private $userStoryRepository;
    public function __construct(
    Transaction $transaction,
    UserContextInterface $userContext,
    UserStoryRepositoryInterface $userStoryRepository
    ) {
    $this->transaction = $transaction;
    $this->userContext = $userContext;
    $this->userStoryRepository = $userStoryRepository;
    }
    ...
    }

    View Slide

  116. 116
    class BackLogApplicationService
    {
    /** @var Transaction */
    private $transaction;
    /** @var UserContextInterface */
    private $userContext;
    /** @var UserStoryRepositoryInterface */
    private $userStoryRepository;
    public function __construct(
    Transaction $transaction,
    UserContextInterface $userContext,
    UserStoryRepositoryInterface $userStoryRepository
    ) {
    $this->transaction = $transaction;
    $this->userContext = $userContext;
    $this->userStoryRepository = $userStoryRepository;
    }
    ...
    }

    View Slide

  117. 117

    View Slide

  118. 118
    class ProductionServiceProvider implements Provider
    {
    ...
    private function registerApplications()
    {
    $this->app->bind(BackLogApplicationService::class);
    ...
    }
    private function registerInfrastructures()
    {
    $this->app->bind(
    BackLogQueryServiceInterface::class,
    EloquentBackLogQueryService::class
    );
    $this->app->bind(
    UserStoryRepositoryInterface::class,
    EloquentUserStoryRepository::class
    );
    ...
    }
    }

    View Slide

  119. 119
    class ProductionServiceProvider implements Provider
    {
    ...
    private function registerApplications()
    {
    $this->app->bind(BackLogApplicationService::class);
    ...
    }
    private function registerInfrastructures()
    {
    $this->app->bind(
    BackLogQueryServiceInterface::class,
    EloquentBackLogQueryService::class
    );
    $this->app->bind(
    UserStoryRepositoryInterface::class,
    EloquentUserStoryRepository::class
    );
    ...
    }
    }

    View Slide

  120. 120
    class LocalServiceProvider implements Provider
    {
    ...
    private function registerInfrastructures()
    {
    $this->app->bind(
    BackLogQueryServiceInterface::class,
    FileBackLogQueryService::class
    );
    $this->app->bind(
    UserStoryRepositoryInterface::class,
    StubUserStoryRepository::class
    );
    $this->app->bind(FileUserStoryRepository::class);
    ...
    }
    }

    View Slide

  121. 121
    class LocalServiceProvider implements Provider
    {
    ...
    private function registerInfrastructures()
    {
    $this->app->bind(
    BackLogQueryServiceInterface::class,
    FileBackLogQueryService::class
    );
    $this->app->bind(
    UserStoryRepositoryInterface::class,
    StubUserStoryRepository::class
    );
    $this->app->bind(FileUserStoryRepository::class);
    ...
    }
    }

    View Slide

  122. 122
    class StubUserStoryRepository implements UserStoryRepositoryInterface {
    public function find(UserStoryId $id): ?UserStory
    {
    return new UserStory(
    new UserStoryId("test-id"),
    "test-story",
    new UserId("test-author-id"),
    "demo sequence",
    3,
    1
    );
    }
    public function save(UserStory $userStory): void
    {
    }
    }

    View Slide

  123. 123
    Eloquent
    UserStoryRepository
    BackLog
    ApplicationService

    View Slide

  124. 124
    Eloquent
    UserStoryRepository
    BackLog
    ApplicationService
    UserStoryRepository
    Interface

    View Slide

  125. 125
    Eloquent
    UserStoryRepository
    BackLog
    ApplicationService
    UserStoryRepository
    Interface

    View Slide

  126. 126

    View Slide

  127. 127

    View Slide

  128. 128

    View Slide

  129. 129
    あー
    モデリングとか
    コーディングしてたら
    オブジェクトの
    データ違ったなー

    View Slide

  130. 130
    あー
    モデリングとか
    コーディングしてたら
    オブジェクトの
    データ違ったなー
    テーブル定義
    変えないとな―

    View Slide

  131. 131
    あー
    モデリングとか
    コーディングしてたら
    オブジェクトの
    データ違ったなー
    テーブル定義
    変えないとな―
    ALTER TABLE ...

    View Slide

  132. 132
    あー
    モデリングとか
    コーディングしてたら
    オブジェクトの
    データ違ったなー
    テーブル定義
    変えないとな―
    ALTER TABLE ...

    View Slide

  133. 133
    class LocalServiceProvider implements Provider
    {
    ...
    private function registerInfrastructures()
    {
    $this->app->bind(
    BackLogQueryServiceInterface::class,
    FileBackLogQueryService::class
    );
    $this->app->bind(
    UserStoryRepositoryInterface::class,
    StubUserStoryRepository::class
    );
    $this->app->bind(FileUserStoryRepository::class);
    ...
    }
    }

    View Slide

  134. 134
    class StubUserStoryRepository implements UserStoryRepositoryInterface {
    public function find(UserStoryId $id): ?UserStory
    {
    return new UserStory(
    new UserStoryId("test-id"),
    "test-story",
    new UserId("test-author-id"),
    "demo sequence",
    3,
    1
    );
    }
    public function save(UserStory $userStory): void
    {
    }
    }

    View Slide

  135. 135
    class StubUserStoryRepository implements UserStoryRepositoryInterface {
    public function find(UserStoryId $id): ?UserStory
    {
    return new UserStory(
    new UserStoryId("test-id"),
    "test-story",
    new UserId("test-author-id"),
    "demo sequence",
    3,
    1
    );
    }
    public function save(UserStory $userStory): void
    {
    }
    }

    View Slide

  136. 136

    View Slide

  137. 137
    https://github.com/nrslib/repository-support-php

    View Slide

  138. 138
    class LocalServiceProvider implements Provider
    {
    ...
    private function registerInfrastructures()
    {
    $this->app->bind(
    BackLogQueryServiceInterface::class,
    FileBackLogQueryService::class
    );
    $this->app->bind(
    UserStoryRepositoryInterface::class,
    FileUserStoryRepository::class
    );
    $this->app->bind(FileUserStoryRepository::class);
    ...
    }
    }

    View Slide

  139. 139
    class FileUserStoryRepository implements UserStoryRepositoryInterface
    {
    use FileRepository;
    public function find(UserStoryId $id): ?UserStory
    {
    $story = $this->load($id->getValue());
    if (is_null($story)) {
    return null;
    } else {
    return $story;
    }
    }
    public function save(UserStory $userStory): void
    {
    $rawId = $userStory->getId()->getValue();
    $this->store($rawId, $userStory);
    }
    }

    View Slide

  140. 140
    class FileUserStoryRepository implements UserStoryRepositoryInterface
    {
    use FileRepository;
    public function find(UserStoryId $id): ?UserStory
    {
    $story = $this->load($id->getValue());
    if (is_null($story)) {
    return null;
    } else {
    return $story;
    }
    }
    public function save(UserStory $userStory): void
    {
    $rawId = $userStory->getId()->getValue();
    $this->store($rawId, $userStory);
    }
    }

    View Slide

  141. 141
    trait FileRepository
    {
    /** @var FileStore */
    private $store;
    /** @var null|string */
    protected $identifier = null;
    public function __construct()
    {
    $this->store = new FileStore();
    }
    public function loadAll()
    {
    return $this->store->getAll(
    $this->id()
    );
    }
    protected function store($key, $data)
    {
    $this->store->put($this->id(), $key, $data);
    }
    protected function load($key)
    {
    return $this->store->get($this->id(), $key);
    }
    private function id()
    {
    if (is_null($this->identifier)) {
    return __CLASS__;
    } else {
    return $this->identifier;
    }
    }
    }

    View Slide

  142. 142
    trait FileRepository
    {
    /** @var FileStore */
    private $store;
    /** @var null|string */
    protected $identifier = null;
    public function __construct()
    {
    $this->store = new FileStore();
    }
    public function loadAll()
    {
    return $this->store->getAll(
    $this->id()
    );
    }
    protected function store($key, $data)
    {
    $this->store->put($this->id(), $key, $data);
    }
    protected function load($key)
    {
    return $this->store->get($this->id(), $key);
    }
    private function id()
    {
    if (is_null($this->identifier)) {
    return __CLASS__;
    } else {
    return $this->identifier;
    }
    }
    }

    View Slide

  143. 143

    View Slide

  144. 144

    View Slide

  145. 145
    あー
    モデリングとか
    コーディングしてたら
    オブジェクトの
    データ違ったなー

    View Slide

  146. 146
    あー
    モデリングとか
    コーディングしてたら
    オブジェクトの
    データ違ったなー
    フィールド追加して
    .dat 消して終わり

    View Slide

  147. 147
    Production と Debug で環境を変えられる

    View Slide

  148. View Slide

  149. 149
    class UserStory
    {
    /** @var UserStoryId */
    private $id;
    /** @var string */
    private $story;
    /** @var UserId */
    private $author;
    /** @var string|null */
    private $demo;
    /** @var int|null */
    private $estimate;
    /** @var int|null */
    private $seq;
    public function __construct(
    UserStoryId $id,
    string $story,
    UserId $author,
    string $demo = null,
    int $estimate = null,
    int $seq = null
    ) {
    $this->id = $id;
    $this->story = $story;
    $this->author = $author;
    $this->demo = $demo;
    $this->estimate = $estimate;
    $this->seq = $seq;
    }
    ...
    }

    View Slide

  150. 150
    /**
    * Class UserStoryDataModel
    * @package Scrum¥EloquentInfrastructure¥Models
    *
    * @property integer $id
    * @property string $story
    * @property string $author
    * @property string|null $demo
    * @property integer|null $estimate
    * @property integer|null $seq
    */
    class UserStoryDataModel extends Model
    {
    protected $table = "user_stories";
    public $incrementing = false;
    protected $keyType = "string";
    public $guarded = [];
    }

    View Slide

  151. 151

    View Slide

  152. 152
    UserStory

    View Slide

  153. 153
    UserStory
    UserStory
    DataModel

    View Slide

  154. 154
    UserStory
    UserStory
    DataModel

    View Slide

  155. 155
    class UserStory extends Model
    {
    protected $table = "user_stories";
    public $incrementing = false;
    protected $keyType = "string";
    public $guarded = [];
    /** @var UserStoryId */
    private $id;
    ...
    }

    View Slide

  156. 156
    class UserStory extends Model
    {
    protected $table = "user_stories";
    public $incrementing = false;
    protected $keyType = "string";
    public $guarded = [];
    /** @var UserStoryId */
    private $id;
    ...
    }
    User
    Story

    View Slide

  157. 157
    class UserStory extends Model
    {
    protected $table = "user_stories";
    public $incrementing = false;
    protected $keyType = "string";
    public $guarded = [];
    /** @var UserStoryId */
    private $id;
    ...
    }
    User
    Story

    View Slide

  158. 158
    UserStory
    UserStory
    DataModel

    View Slide

  159. 159
    User Interface
    Application
    Domain
    Infrastructure
    OK
    NG
    MVC
    Framework

    View Slide

  160. 160
    User Interface
    Application
    Domain
    Infrastructure
    OK
    NG
    MVC
    Framework
    OK?

    View Slide

  161. 161
    Infrastructure
    User Interface
    Application
    Domain
    OK
    NG

    View Slide

  162. 162
    Infrastructure
    User Interface
    Application
    Domain
    OK
    NG

    View Slide

  163. 163
    Infrastructure
    User Interface
    Application
    Domain
    OK
    NG

    View Slide

  164. 164
    Infrastructure
    User Interface
    Application
    Domain
    OK
    NG

    View Slide

  165. 165
    Infrastructure
    User Interface
    Application
    Domain
    OK
    NG

    View Slide

  166. 166
    Infrastructure
    User Interface
    Application
    Domain
    OK
    NG

    View Slide

  167. 167
    https://alistair.cockburn.us/hexagonal-architecture/

    View Slide

  168. View Slide

  169. 169
    class BackLogApplicationService
    {
    /** @var Transaction */
    private $transaction;
    /** @var UserContextInterface */
    private $userContext;
    /** @var UserStoryRepositoryInterface */
    private $userStoryRepository;
    ...
    public function addUserStory(AddUserStoryCommand $command)
    {
    $this->transaction->scope(function () use ($command) {
    $id = new UserStoryId(uniqid());
    $story = new UserStory(
    $id,
    $command->getStory(),
    $this->userContext->getId(),
    $command->getDemo()
    );
    $this->userStoryRepository->save($story);
    });
    }
    }

    View Slide

  170. 170
    class BackLogApplicationService
    {
    /** @var Transaction */
    private $transaction;
    /** @var UserContextInterface */
    private $userContext;
    /** @var UserStoryRepositoryInterface */
    private $userStoryRepository;
    ...
    public function addUserStory(AddUserStoryCommand $command)
    {
    $this->transaction->scope(function () use ($command) {
    $id = new UserStoryId(uniqid());
    $story = new UserStory(
    $id,
    $command->getStory(),
    $this->userContext->getId(),
    $command->getDemo()
    );
    $this->userStoryRepository->save($story);
    });
    }
    }

    View Slide

  171. 171
    class BackLogApplicationService
    {
    /** @var Transaction */
    private $transaction;
    /** @var UserContextInterface */
    private $userContext;
    /** @var UserStoryRepositoryInterface */
    private $userStoryRepository;
    ...
    public function addUserStory(AddUserStoryCommand $command)
    {
    $this->transaction->scope(function () use ($command) {
    $id = new UserStoryId(uniqid());
    $story = new UserStory(
    $id,
    $command->getStory(),
    Auth::user()->getAuthIdentifier(),
    $command->getDemo()
    );
    $this->userStoryRepository->save($story);
    });
    }
    }

    View Slide

  172. 172
    class BackLogApplicationService
    {
    /** @var Transaction */
    private $transaction;
    /** @var UserContextInterface */
    private $userContext;
    /** @var UserStoryRepositoryInterface */
    private $userStoryRepository;
    ...
    public function addUserStory(AddUserStoryCommand $command)
    {
    $this->transaction->scope(function () use ($command) {
    $id = new UserStoryId(uniqid());
    $story = new UserStory(
    $id,
    $command->getStory(),
    Auth::user()->getAuthIdentifier(),
    $command->getDemo()
    );
    $this->userStoryRepository->save($story);
    });
    }
    }

    View Slide

  173. 173
    class BackLogApplicationService
    {
    /** @var Transaction */
    private $transaction;
    /** @var UserContextInterface */
    private $userContext;
    /** @var UserStoryRepositoryInterface */
    private $userStoryRepository;
    ...
    public function addUserStory(AddUserStoryCommand $command)
    {
    $this->transaction->scope(function () use ($command) {
    $id = new UserStoryId(uniqid());
    $story = new UserStory(
    $id,
    $command->getStory(),
    Auth::user()->getAuthIdentifier(),
    $command->getDemo()
    );
    $this->userStoryRepository->save($story);
    });
    }
    }

    View Slide

  174. 174
    interface UserContextInterface
    {
    function getId(): ?UserId;
    }

    View Slide

  175. 175
    interface UserContextInterface
    {
    function getId(): ?UserId;
    }
    class AuthUserContext implements UserContextInterface
    {
    function getId(): ?UserId
    {
    $user = Auth::user();
    if (is_null($user)) {
    return null;
    }
    $rawId = $user->getAuthIdentifier();
    $id = new UserId($rawId);
    return $id;
    }
    }

    View Slide

  176. 176
    class BackLogApplicationService
    {
    /** @var Transaction */
    private $transaction;
    /** @var UserContextInterface */
    private $userContext;
    /** @var UserStoryRepositoryInterface */
    private $userStoryRepository;
    ...
    public function addUserStory(AddUserStoryCommand $command)
    {
    $this->transaction->scope(function () use ($command) {
    $id = new UserStoryId(uniqid());
    $story = new UserStory(
    $id,
    $command->getStory(),
    $this->userContext->getId(),
    $command->getDemo()
    );
    $this->userStoryRepository->save($story);
    });
    }
    }

    View Slide

  177. 177
    class BackLogApplicationService
    {
    /** @var Transaction */
    private $transaction;
    /** @var UserContextInterface */
    private $userContext;
    /** @var UserStoryRepositoryInterface */
    private $userStoryRepository;
    ...
    public function addUserStory(AddUserStoryCommand $command)
    {
    $this->transaction->scope(function () use ($command) {
    $id = new UserStoryId(uniqid());
    $story = new UserStory(
    $id,
    $command->getStory(),
    $this->userContext->getId(),
    $command->getDemo()
    );
    $this->userStoryRepository->save($story);
    });
    }
    }

    View Slide

  178. 178
    class BackLogApplicationService
    {
    /** @var Transaction */
    private $transaction;
    /** @var UserContextInterface */
    private $userContext;
    /** @var UserStoryRepositoryInterface */
    private $userStoryRepository;
    ...
    public function addUserStory(AddUserStoryCommand $command)
    {
    $this->transaction->scope(function () use ($command) {
    $id = new UserStoryId(uniqid());
    $story = new UserStory(
    $id,
    $command->getStory(),
    $this->userContext->getId(),
    $command->getDemo()
    );
    $this->userStoryRepository->save($story);
    });
    }
    }

    View Slide

  179. 179
    class BackLogApplicationService
    {
    ...
    public function addUserStory(AddUserStoryCommand $command)
    {
    DB::beginTransaction();
    try {
    $id = new UserStoryId(uniqid());
    $story = new UserStory(
    $id,
    $command->getStory(),
    $this->userContext->getId(),
    $command->getDemo()
    );
    $this->userStoryRepository->save($story);
    DB::commit();
    } catch ($exception) {
    DB::rollBack();
    }
    }
    }

    View Slide

  180. 180
    class BackLogApplicationService
    {
    ...
    public function addUserStory(AddUserStoryCommand $command)
    {
    DB::beginTransaction();
    try {
    $id = new UserStoryId(uniqid());
    $story = new UserStory(
    $id,
    $command->getStory(),
    $this->userContext->getId(),
    $command->getDemo()
    );
    $this->userStoryRepository->save($story);
    DB::commit();
    } catch ($exception) {
    DB::rollBack();
    }
    }
    }

    View Slide

  181. 181
    class BackLogApplicationService
    {
    ...
    public function addUserStory(AddUserStoryCommand $command)
    {
    DB::beginTransaction();
    try {
    $id = new UserStoryId(uniqid());
    $story = new UserStory(
    $id,
    $command->getStory(),
    $this->userContext->getId(),
    $command->getDemo()
    );
    $this->userStoryRepository->save($story);
    DB::commit();
    } catch ($exception) {
    DB::rollBack();
    }
    }
    }

    View Slide

  182. 182
    interface Transaction
    {
    /**
    * @param callable $transactionScope
    * @return mixed
    */
    function scope(callable $transactionScope): ?object;
    function begin();
    function commit();
    function rollback();
    }

    View Slide

  183. 183
    class LaravelDbTransaction implements Transaction
    {
    function scope(callable $transactionScope): ?object
    {
    return DB::transaction($transactionScope);
    }
    function begin()
    {
    DB::beginTransaction();
    }
    function commit()
    {
    DB::commit();
    }
    function rollback()
    {
    DB::rollBack();
    }
    }

    View Slide

  184. 184
    $this->app->bind(Transaction::class, LaravelDbTransaction::class);

    View Slide

  185. 185
    class BackLogApplicationService
    {
    ...
    public function addUserStory(AddUserStoryCommand $command)
    {
    $this->transaction->begin();
    try {
    $id = new UserStoryId(uniqid());
    $story = new UserStory(
    $id,
    $command->getStory(),
    $this->userContext->getId(),
    $command->getDemo()
    );
    $this->userStoryRepository->save($story);
    $this->transaction->commit();
    } catch ($exception) {
    $this->transaction->rollback();
    }
    }
    }

    View Slide

  186. 186
    class NopTransaction implements Transaction
    {
    function scope(callable $transactionScope): ?object
    {
    return $transactionScope();
    }
    function begin()
    {
    }
    function commit()
    {
    }
    function rollback()
    {
    }
    }

    View Slide

  187. View Slide

  188. 188
    class BackLogController extends Controller
    {
    ...
    public function postAddUserStory(Request $request)
    {
    $request->validate([
    "story" => "required"
    ]);
    $story = $request->input("story");
    $command = new AddUserStoryCommand($story);
    $applicationService = new BackLogApplicationService(
    new LaravelDbTransaction(),
    new AuthUserContext(),
    new EloquentUserStoryRepository()
    );
    $applicationService->addUserStory($command);
    return redirect()->action([BackLogController::class, "index"]);
    }
    }

    View Slide

  189. 189
    class BackLogController extends Controller
    {
    ...
    public function postAddUserStory(Request $request)
    {
    $request->validate([
    "story" => "required"
    ]);
    $story = $request->input("story");
    $command = new AddUserStoryCommand($story);
    $applicationService = new BackLogApplicationService(
    new LaravelDbTransaction(),
    new AuthUserContext(),
    new EloquentUserStoryRepository()
    );
    $applicationService->addUserStory($command);
    return redirect()->action([BackLogController::class, "index"]);
    }
    }

    View Slide

  190. 190
    class UserStory
    {
    ...
    public function __construct(
    UserStoryId $id,
    string $story,
    UserId $author,
    string $demo = null,
    int $estimate = null,
    int $seq = null
    ) {
    if (strlen($story) == 0) {
    throw new ¥InvalidArgumentException("story is empty.")
    }
    $this->id = $id;
    $this->story = $story;
    $this->author = $author;
    $this->demo = $demo;
    $this->estimate = $estimate;
    $this->seq = $seq;
    }
    }

    View Slide

  191. 191
    class UserStory
    {
    ...
    public function __construct(
    UserStoryId $id,
    string $story,
    UserId $author,
    string $demo = null,
    int $estimate = null,
    int $seq = null
    ) {
    if (strlen($story) == 0) {
    throw new ¥InvalidArgumentException("story is empty.")
    }
    $this->id = $id;
    $this->story = $story;
    $this->author = $author;
    $this->demo = $demo;
    $this->estimate = $estimate;
    $this->seq = $seq;
    }
    }

    View Slide

  192. View Slide

  193. 193
    Infrastructure
    User Interface
    Application
    Domain
    OK
    NG

    View Slide

  194. 194
    Infrastructure
    User Interface
    Application
    Domain
    OK
    NG

    View Slide

  195. 195
    Infrastructure
    User Interface
    Application
    Domain
    OK
    NG

    View Slide

  196. 196
    Infrastructure
    User Interface
    Application
    Domain
    OK
    NG

    View Slide

  197. 197
    Infrastructure
    User Interface
    Application
    Domain
    OK
    NG

    View Slide

  198. 198
    Infrastructure
    User Interface
    Application
    Domain
    OK
    NG

    View Slide

  199. 199
    Infrastructure
    User Interface
    Application
    Domain
    OK
    NG

    View Slide

  200. 200
    Infrastructure
    User Interface
    Application
    Domain
    OK
    NG

    View Slide

  201. 201
    Infrastructure
    User Interface
    Application
    Domain
    OK
    NG
    1件~100件まで
    自由に取れます

    View Slide

  202. 202
    Infrastructure
    User Interface
    Application
    Domain
    OK
    NG
    1件~100件まで
    自由に取れます
    ドロップダウンで選択するので
    10,30,50,100 のいずれかです

    View Slide

  203. 203
    Infrastructure
    User Interface
    Application
    Domain
    OK
    NG
    1件~100件まで
    自由に取れます
    ドロップダウンで選択するので
    10,30,50,100 のいずれかです

    View Slide

  204. 204
    Infrastructure
    User Interface
    Application
    Domain
    OK
    NG
    Validation
    Module
    OK

    View Slide

  205. View Slide

  206. 206

    View Slide

  207. 207

    View Slide

  208. 208

    View Slide

  209. 209

    View Slide

  210. 210

    View Slide

  211. 211

    View Slide

  212. 212

    View Slide

  213. 213

    View Slide

  214. 214

    View Slide

  215. 215

    View Slide

  216. 216

    View Slide

  217. 217
    ID

    View Slide

  218. 218
    ID
    ID

    View Slide

  219. 219
    ID
    ID

    View Slide

  220. 220
    class UserApplicationService
    {
    ...
    public function create(string $email, string $password): UserCreateResult
    {
    return $this->transaction->scope(function () use ($email, $password) {
    $user = $this->userFactory->create($email, $password);
    $found = $this->userRepository->findByEmail($user->getEmail());
    if (!is_null($found)) {
    return UserCreateResult::fail(UserCreateError::EMAIL_ALREADY_USING);
    }
    $this->userRepository->save($user);
    return UserCreateResult::success($user->getId());
    });
    }
    ...
    }

    View Slide

  221. 221
    class UserApplicationService
    {
    ...
    public function create(string $email, string $password): UserCreateResult
    {
    return $this->transaction->scope(function () use ($email, $password) {
    $user = $this->userFactory->create($email, $password);
    $found = $this->userRepository->findByEmail($user->getEmail());
    if (!is_null($found)) {
    return UserCreateResult::fail(UserCreateError::EMAIL_ALREADY_USING);
    }
    $this->userRepository->save($user);
    return UserCreateResult::success($user->getId());
    });
    }
    ...
    }

    View Slide

  222. 222
    class UserApplicationService
    {
    ...
    public function create(string $email, string $password): UserCreateResult
    {
    return $this->transaction->scope(function () use ($email, $password) {
    $user = $this->userFactory->create($email, $password);
    $found = $this->userRepository->findByEmail($user->getEmail());
    if (!is_null($found)) {
    return UserCreateResult::fail(UserCreateError::EMAIL_ALREADY_USING);
    }
    $this->userRepository->save($user);
    return UserCreateResult::success($user->getId());
    });
    }
    ...
    }

    View Slide

  223. 223
    class UserApplicationService
    {
    ...
    public function create(string $email, string $password): UserCreateResult
    {
    return $this->transaction->scope(function () use ($email, $password) {
    $user = $this->userFactory->create($email, $password);
    $found = $this->userRepository->findByEmail($user->getEmail());
    if (!is_null($found)) {
    return UserCreateResult::fail(UserCreateError::EMAIL_ALREADY_USING);
    }
    $this->userRepository->save($user);
    return UserCreateResult::success($user->getId());
    });
    }
    ...
    }

    View Slide

  224. 224
    class UserApplicationService
    {
    ...
    public function create(string $email, string $password): UserCreateResult
    {
    return $this->transaction->scope(function () use ($email, $password) {
    $user = $this->userFactory->create($email, $password);
    $found = $this->userRepository->findByEmail($user->getEmail());
    if (!is_null($found)) {
    return UserCreateResult::fail(UserCreateError::EMAIL_ALREADY_USING);
    }
    $this->userRepository->save($user);
    return UserCreateResult::success($user->getId());
    });
    }
    ...
    }

    View Slide

  225. 225
    class UserApplicationService
    {
    ...
    public function create(string $email, string $password): UserCreateResult
    {
    return $this->transaction->scope(function () use ($email, $password) {
    $user = $this->userFactory->create($email, $password);
    $found = $this->userRepository->findByEmail($user->getEmail());
    if (!is_null($found)) {
    return UserCreateResult::fail(UserCreateError::EMAIL_ALREADY_USING);
    }
    $this->userRepository->save($user);
    return UserCreateResult::success($user->getId());
    });
    }
    ...
    }

    View Slide

  226. 226
    class UserApplicationService
    {
    ...
    public function create(string $email, string $password): UserCreateResult
    {
    return $this->transaction->scope(function () use ($email, $password) {
    $user = $this->userFactory->create($email, $password);
    $found = $this->userRepository->findByEmail($user->getEmail());
    if (!is_null($found)) {
    return UserCreateResult::fail(UserCreateError::EMAIL_ALREADY_USING);
    }
    $this->userRepository->save($user);
    return UserCreateResult::success($user->getId());
    });
    }
    ...
    }

    View Slide

  227. 227
    class UserApplicationService
    {
    ...
    public function create(string $email, string $password): UserCreateResult
    {
    return $this->transaction->scope(function () use ($email, $password) {
    $user = $this->userFactory->create($email, $password);
    $found = $this->userRepository->findByEmail($user->getEmail());
    if (!is_null($found)) {
    return UserCreateResult::fail(UserCreateError::EMAIL_ALREADY_USING);
    }
    $this->userRepository->save($user);
    return UserCreateResult::success($user->getId());
    });
    }
    ...
    }

    View Slide

  228. 228
    class AuthController extends Controller
    {
    public function postRegister(Request $request, UserApplicationService $service)
    {
    ...
    $email = $request->get("email");
    $password = $request->get("password");
    try {
    $service->create($email, $password);
    } catch ($exception) {
    $result = UserCreateError::EMAIL_ALREADY_USING;
    }
    ...
    }
    }

    View Slide

  229. 229
    class AuthController extends Controller
    {
    public function postRegister(Request $request, UserApplicationService $service)
    {
    ...
    $email = $request->get(“email”);
    $password = $request->get(“password”);
    $result = $service->create($email, $password);
    ...
    }
    }

    View Slide

  230. 230

    View Slide

  231. View Slide

  232. 232

    View Slide

  233. 233

    View Slide

  234. 234

    View Slide

  235. 235

    View Slide

  236. 236

    View Slide

  237. 237
    class RequestResponseLogger
    {
    public function handle(Request $request, Closure $next)
    {
    ... Logging Request
    $response = $next($request);
    ... Logging Response
    return $response;
    }
    }

    View Slide

  238. 238
    class RequestResponseLogger
    {
    public function handle(Request $request, Closure $next)
    {
    ... Logging Request
    $response = $next($request);
    ... Logging Response
    return $response;
    }
    }

    View Slide

  239. 239
    class RequestResponseLogger
    {
    public function handle(Request $request, Closure $next)
    {
    ... Logging Request
    $response = $next($request);
    ... Logging Response
    return $response;
    }
    }

    View Slide

  240. 240
    class RequestResponseLogger
    {
    public function handle(Request $request, Closure $next)
    {
    ... Logging Request
    $response = $next($request);
    ... Logging Response
    return $response;
    }
    }

    View Slide

  241. 241

    View Slide

  242. 242
    class BackLogApplicationService
    {
    ...
    public function estimateUserStory(string $aId, int $estimation): void
    {
    $this->transaction->scope(function () use ($aId, $estimation) {
    $id = new UserStoryId($aId);
    $story = $this->userStoryRepository->find($id);
    if (is_null($story)) {
    throw new NotFoundException($aId . " not found.");
    }
    $story->estimate($estimation);
    $this->userStoryRepository->save($story);
    });
    }
    }

    View Slide

  243. 243
    class BackLogApplicationService
    {
    ...
    public function estimateUserStory(string $aId, int $estimation): void
    {
    $this->transaction->scope(function () use ($aId, $estimation) {
    $id = new UserStoryId($aId);
    $story = $this->userStoryRepository->find($id);
    if (is_null($story)) {
    throw new NotFoundException($aId . " not found.");
    }
    $story->estimate($estimation);
    $this->userStoryRepository->save($story);
    });
    }
    }

    View Slide

  244. 244
    class BackLogApplicationService
    {
    ...
    public function estimateUserStory(string $aId, int $estimation): void
    {
    $this->transaction->scope(function () use ($aId, $estimation) {
    $id = new UserStoryId($aId);
    $story = $this->userStoryRepository->find($id);
    if (is_null($story)) {
    throw new NotFoundException($aId . " not found.");
    }
    $story->estimate($estimation);
    $this->userStoryRepository->save($story);
    });
    }
    }

    View Slide

  245. 245
    class BackLogApplicationService
    {
    ...
    public function estimateUserStory(string $aId, int $estimation): void
    {
    $this->transaction->scope(function () use ($aId, $estimation) {
    $id = new UserStoryId($aId);
    $story = $this->userStoryRepository->find($id);
    if (is_null($story)) {
    throw new NotFoundException($aId . " not found.");
    }
    $story->estimate($estimation);
    $this->userStoryRepository->save($story);
    });
    }
    }

    View Slide

  246. 246
    class BackLogApplicationService
    {
    ...
    public function estimateUserStory(string $aId, int $estimation): void
    {
    $this->transaction->scope(function () use ($aId, $estimation) {
    $id = new UserStoryId($aId);
    $story = $this->userStoryRepository->find($id);
    if (is_null($story)) {
    throw new NotFoundException($aId . " not found.");
    }
    $story->estimate($estimation);
    $this->userStoryRepository->save($story);
    });
    }
    }

    View Slide

  247. 247
    class BackLogApplicationService
    {
    ...
    public function estimateUserStory(string $aId, int $estimation): void
    {
    $this->transaction->scope(function () use ($aId, $estimation) {
    $id = new UserStoryId($aId);
    $story = $this->userStoryRepository->find($id);
    if (is_null($story)) {
    throw new NotFoundException($aId . " not found.");
    }
    $story->estimate($estimation);
    $this->userStoryRepository->save($story);
    });
    }
    }

    View Slide

  248. 248
    class ExceptionResolver
    {
    public function handle(Request $request, Closure $next)
    {
    $response = $next($request);
    if (!empty($response->exception)) {
    if ($response->exception instanceof NotFoundException){
    return response(view("notfound"));
    }
    }
    return $response;
    }
    }

    View Slide

  249. 249

    View Slide

  250. 250
    Infrastructure
    User Interface
    Application
    Domain
    OK
    NG

    View Slide

  251. 251
    2000
    2005
    2010
    2015
    2020
    SDプラン RSプラン

    View Slide

  252. 252

    View Slide

  253. 253

    View Slide

  254. 254

    View Slide

  255. 10年
    先を見据えて作る
    PHP WEBアプリケーション設計入門

    View Slide

  256. View Slide

  257. 257

    View Slide

  258. 258

    View Slide

  259. 259

    View Slide

  260. 260
    class UserApplicationService
    {
    /** @var UserRepositoryInterface */
    private $userRepository;
    ...
    public function retrieveById(string $aId): ?User
    {
    $id = new UserId($aId);
    $user = $this->userRepository->find($id);
    return $user;
    }
    }

    View Slide

  261. 261
    class AuthenticateService
    {
    /** @var UserRepositoryInterface */
    private $userRepository;
    public function __construct(UserRepositoryInterface $userRepository)
    {
    $this->userRepository = $userRepository;
    }
    public function authenticate(string $aEmail, string $password): bool
    {
    $email = new Email($aEmail);
    $user = $this->userRepository->findByEmail($email);
    if (is_null($user)) {
    return false;
    }
    $hasher = new PasswordHasher();
    return $hasher->checkByUser($password, $user);
    }
    }

    View Slide

  262. 262
    class OriginalUserProvider implements UserProvider
    {
    /** @var AuthenticateService */
    private $authenticateService;
    /** @var UserApplicationService */
    private $userService;
    public function __construct(
    AuthenticateService $authenticateService,
    UserApplicationService $userService
    ) {
    $this->authenticateService = $authenticateService;
    $this->userService = $userService;
    }
    ...
    public function validateCredentials(Authenticatable $user, array $credentials)
    {
    $password = $credentials["password"];
    $email = $credentials["email"];
    $result = $this->authenticateService->authenticate($email, $password);
    return $result;
    }
    }

    View Slide

  263. 263

    View Slide

  264. 264
    Auther Masanobu Naruse
    YouTube https://www.youtube.com/c/narusemi
    HomePage https://nrslib.com
    Twitter @nrslib

    View Slide