Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

Laravel でやってみるクリーンアーキテクチャ #phpconfuk

2,655 views

Published on

ドメイン駆動設計やユースケース駆動開発などの文脈でレイヤードアーキテクチャやクリーンアーキテクチャといった言葉をよく聞くようになりました。
「名前は聞いたことあるけど、敷居が高そう......」「本は読んだけど実際に実装するイメージがつかない......」そんなことを感じている方もいらっしゃるのではないでしょうか?
本トークではそんな方に向けて、簡単なアプリケーションの実装例とともに、クリーンアーキテクチャの考え方や実装する上での Laravel の機能についてお話します!

2018/06/29 開催の PHP カンファレンス福岡2019 (https://phpcon.fukuoka.jp/2019/) の発表資料です。

Published in: Software
  • (Unlimited)....ACCESS WEBSITE Over for All Ebooks ................ accessibility Books Library allowing access to top content, including thousands of title from favorite author, plus the ability to read or download a huge selection of books for your pc or smartphone within minutes ......................................................................................................................... DOWNLOAD FULL PDF EBOOK here { https://urlzs.com/UABbn } ......................................................................................................................... Download Full EPUB Ebook here { https://urlzs.com/UABbn } ......................................................................................................................... Download Full PDF EBOOK here { https://urlzs.com/UABbn }
       Reply 
    Are you sure you want to  Yes  No
    Your message goes here
  • -- DOWNLOAD THIS BOOKS INTO AVAILABLE FORMAT -- ......................................................................................................................... ......................................................................................................................... Download FULL PDF EBOOK here { http://bit.ly/2m77EgH } ......................................................................................................................... (Unlimited)
       Reply 
    Are you sure you want to  Yes  No
    Your message goes here
  • If you want to download or read this book, copy link or url below in the New tab ......................................................................................................................... DOWNLOAD FULL PDF EBOOK here { http://bit.ly/2m6jJ5M } .........................................................................................................................
       Reply 
    Are you sure you want to  Yes  No
    Your message goes here
  • Download or read that Ebooks here ... ......................................................................................................................... DOWNLOAD FULL PDF EBOOK here { http://bit.ly/2m6jJ5M } ......................................................................................................................... Download EPUB Ebook here { http://bit.ly/2m6jJ5M } ......................................................................................................................... Download Doc Ebook here { http://bit.ly/2m6jJ5M } ......................................................................................................................... .........................................................................................................................
       Reply 
    Are you sure you want to  Yes  No
    Your message goes here
  • Download or read that Ebooks here ... ......................................................................................................................... DOWNLOAD FULL PDF EBOOK here { http://bit.ly/2m6jJ5M } ......................................................................................................................... Download EPUB Ebook here { http://bit.ly/2m6jJ5M } ......................................................................................................................... Download Doc Ebook here { http://bit.ly/2m6jJ5M } ......................................................................................................................... .........................................................................................................................
       Reply 
    Are you sure you want to  Yes  No
    Your message goes here

Laravel でやってみるクリーンアーキテクチャ #phpconfuk

  1. 1. Laravel PHP Conference Fukuoka 2019 @okashoi WILLGATE, Inc.
  2. 2. • • 🙆 • • #phpconfuk #hall_fu 2
  3. 3. • 2 • 3 • 5 • 10 • 3 • 2 3
  4. 4. • 2 • 3 • 5 • 10 • 3 • 2 4
  5. 5. 🏁 • • • Laravel 
 5
  6. 6. 🙅 • Laravel • 6
  7. 7. • 2 • 3 • 5 • 10 • 3 • 2 7
  8. 8. 8 2017 2018
  9. 9. ”The Clean Architecture - The Clean Code Blog”, https://blog.cleancoder.com/uncle-bob/2012/08/13/the-clean-architecture.html 9
  10. 10. • 
 • • 10
  11. 11. • • • ※ PHP 11
  12. 12. “ 
 ” 12
  13. 13. • 2 • 3 • 5 • 10 • 3 • 2 13
  14. 14. 14
  15. 15. 15
  16. 16. 16 🙅
  17. 17. 17
  18. 18. 18
  19. 19. • • • • 19
  20. 20. 20
  21. 21. 21 ↑
  22. 22. 22
  23. 23. package foo; import bar; class Foo { bar.Bar x; function doFoo() { x.process(); } } 23 package bar; class Bar { function process() { // do something... } }
  24. 24. package foo; import bar; class Foo { bar.Bar x; function doFoo() { x.process(); } } 24 package bar; class Bar { function process() { // do something... } } foo bar = foo → bar
  25. 25. package foo; import bar; class Foo { bar.Bar x; function doFoo() { x.process(); } } 25 package bar; class Bar { function process() { // do something... } } foo bar = foo → bar
  26. 26. package foo; import bar; class Foo { bar.Bar x; function doFoo() { x.process(); } } 26 package bar; class Bar { function process() { // do something... } } foo → bar foo → bar
  27. 27. package foo; class Foo { Buz x; function doFoo() { x.process(); } } interface Buz { function process(); } 27 package bar; import foo; class Bar implements foo.Buz { function process() { // do something... } }
  28. 28. package foo; class Foo { Buz x; function doFoo() { x.process(); } } interface Buz { function process(); } 28 package bar; import foo; class Bar implements foo.Buz { function process() { // do something... } }
  29. 29. package foo; class Foo { Buz x; function doFoo() { x.process(); } } interface Buz { function process(); } 29 package bar; import foo; class Bar implements foo.Buz { function process() { // do something... } } foo bar = foo → bar
  30. 30. package foo; class Foo { Buz x; function doFoo() { x.process(); } } interface Buz { function process(); } 30 package bar; import foo; class Bar implements foo.Buz { function process() { // do something... } } bar foo = foo ← bar
  31. 31. package foo; class Foo { Buz x; function doFoo() { x.process(); } } interface Buz { function process(); } 31 package bar; import foo; class Bar implements foo.Buz { function process() { // do something... } } foo → bar foo ← bar
  32. 32. “ 
 
 ” 32
  33. 33. • 
 ➡ 
 33
  34. 34. 34 UI
  35. 35. • 2 • 3 • 5 • 10 • 3 • 2 35
  36. 36. CleanArchitecture Clean Architecture Kindle No. 3228/6016 36
  37. 37. • • https://github.com/okashoi/laravel-clean- architecture • • 37
  38. 38. 38
  39. 39. • • ID 
 39
  40. 40. readme 40
  41. 41. • • • POPO Plain Old PHP Object ➡ 41
  42. 42. • PHP 42 <?php namespace MyAppComponentsTasksEntities; use DatetimeImmutable; /** * Class Inbox * @package MyAppComponentsTasksEntities */ final class Inbox extends Task { /** * @var EstimatedTime|null */ private $estimatedTime;
  43. 43. • • 43 /** * @test * @expectedException MyAppComponentsTasksEntitiesEstimatedTimeNotSet */ public function 「Inbox」に対して「見積もり時間」が未設定のまま「着手日」を設定できないこと() { $id = Mockery::mock(Id::class); $name = new Name('test'); $inbox = new Inbox($id, $name); $startDate = new StartDate(new DateTimeImmutable('tomorrow')); $inbox->convertToScheduled($startDate); }
  44. 44. 44
  45. 45. 45
  46. 46. • • 
 • 
 46
  47. 47. InputBoundary / OutputBoundary • 
 47 <?php namespace MyAppComponentsTasksUseCasesCreateInbox; /** * Interface InputBoundary * @package MyAppComponentsTasksUseCasesCreateInbox */ interface InputBoundary { /** * @param InputData $input */ public function __invoke(InputData $input): void; }
  48. 48. InputBoundary / OutputBoundary • 
 48 <?php namespace MyAppComponentsTasksUseCasesCreateInbox; /** * Interface NormalOutputBoundary * @package MyAppComponentsTasksUseCasesCreateInbox */ interface NormalOutputBoundary { /** * @param NormalOutputData $output */ public function __invoke(NormalOutputData $output): void; }
  49. 49. Interactor 49 <?php namespace MyAppComponentsTasksUseCasesCreateInbox; use MyAppComponentsTasksEntities{Inbox, Task, Name, Note}; use MyAppComponentsTasksUseCases{IdProvider, TaskRepository}; /** * Class Interactor * @package MyAppComponentsTasksUseCasesCreateInbox */ final class Interactor implements InputBoundary { /** * @var IdProvider */ private $idProvider; /** * @var TaskRepository */ private $taskRepository; /** * @var NormalOutputBoundary */ private $normalOutputBoundary; /** * Interactor constructor. * @param IdProvider $idProvider * @param TaskRepository $taskRepository * @param NormalOutputBoundary $normalOutputBoundary */ public function __construct(IdProvider $idProvider, TaskRepository $t { $this->idProvider = $idProvider; $this->taskRepository = $taskRepository; $this->normalOutputBoundary = $normalOutputBoundary; } /** * @param InputData $input */ public function __invoke(InputData $input): void { $inbox = $this->produceEntity($input); $this->taskRepository->save($inbox); $normalOutput = $this->produceNormalOutputData($inbox); ($this->normalOutputBoundary)($normalOutput); } /** * @param InputData $input * @return Task
  50. 50. Interactor 50 <?php namespace MyAppComponentsTasksUseCasesCreateInbox; use MyAppComponentsTasksEntities{Inbox, Task, Name, Note}; use MyAppComponentsTasksUseCases{IdProvider, TaskRepository}; /** * Class Interactor * @package MyAppComponentsTasksUseCasesCreateInbox */ final class Interactor implements InputBoundary { /** * @var IdProvider */ private $idProvider; /** * @var TaskRepository */ private $taskRepository; /** * @var NormalOutputBoundary */ private $normalOutputBoundary; /** * Interactor constructor. * @param IdProvider $idProvider * @param TaskRepository $taskRepository * @param NormalOutputBoundary $normalOutputBoundary */ public function __construct(IdProvider $idProvider, TaskRepository $t { $this->idProvider = $idProvider; $this->taskRepository = $taskRepository; $this->normalOutputBoundary = $normalOutputBoundary; } /** * @param InputData $input */ public function __invoke(InputData $input): void { $inbox = $this->produceEntity($input); $this->taskRepository->save($inbox); $normalOutput = $this->produceNormalOutputData($inbox); ($this->normalOutputBoundary)($normalOutput); } /** * @param InputData $input * @return Task InputBoundary
  51. 51. Interactor 51 <?php namespace MyAppComponentsTasksUseCasesCreateInbox; use MyAppComponentsTasksEntities{Inbox, Task, Name, Note}; use MyAppComponentsTasksUseCases{IdProvider, TaskRepository}; /** * Class Interactor * @package MyAppComponentsTasksUseCasesCreateInbox */ final class Interactor implements InputBoundary { /** * @var IdProvider */ private $idProvider; /** * @var TaskRepository */ private $taskRepository; /** * @var NormalOutputBoundary */ private $normalOutputBoundary; /** * Interactor constructor. * @param IdProvider $idProvider * @param TaskRepository $taskRepository * @param NormalOutputBoundary $normalOutputBoundary */ public function __construct(IdProvider $idProvider, TaskRepository $t { $this->idProvider = $idProvider; $this->taskRepository = $taskRepository; $this->normalOutputBoundary = $normalOutputBoundary; } /** * @param InputData $input */ public function __invoke(InputData $input): void { $inbox = $this->produceEntity($input); $this->taskRepository->save($inbox); $normalOutput = $this->produceNormalOutputData($inbox); ($this->normalOutputBoundary)($normalOutput); } /** * @param InputData $input * @return Task DI ※ 

  52. 52. Interactor 52 <?php namespace MyAppComponentsTasksUseCasesCreateInbox; use MyAppComponentsTasksEntities{Inbox, Task, Name, Note}; use MyAppComponentsTasksUseCases{IdProvider, TaskRepository}; /** * Class Interactor * @package MyAppComponentsTasksUseCasesCreateInbox */ final class Interactor implements InputBoundary { /** * @var IdProvider */ private $idProvider; /** * @var TaskRepository */ private $taskRepository; /** * @var NormalOutputBoundary */ private $normalOutputBoundary; /** * Interactor constructor. * @param IdProvider $idProvider * @param TaskRepository $taskRepository * @param NormalOutputBoundary $normalOutputBoundary */ public function __construct(IdProvider $idProvider, TaskRepository $t { $this->idProvider = $idProvider; $this->taskRepository = $taskRepository; $this->normalOutputBoundary = $normalOutputBoundary; } /** * @param InputData $input */ public function __invoke(InputData $input): void { $inbox = $this->produceEntity($input); $this->taskRepository->save($inbox); $normalOutput = $this->produceNormalOutputData($inbox); ($this->normalOutputBoundary)($normalOutput); } /** * @param InputData $input * @return Task 

  53. 53. Interactor::__invoke() 
 53 /** * @param InputData $input */ public function __invoke(InputData $input): void { $inbox = $this->produceEntity($input); $this->taskRepository->save($inbox); $normalOutput = $this->produceNormalOutputData($inbox); ($this->normalOutputBoundary)($normalOutput); }
  54. 54. Interactor::__invoke() 
 54 /** * @param InputData $input */ public function __invoke(InputData $input): void { $inbox = $this->produceEntity($input); $this->taskRepository->save($inbox); $normalOutput = $this->produceNormalOutputData($inbox); ($this->normalOutputBoundary)($normalOutput); }
  55. 55. Interactor::__invoke() 
 55 /** * @param InputData $input */ public function __invoke(InputData $input): void { $inbox = $this->produceEntity($input); $this->taskRepository->save($inbox); $normalOutput = $this->produceNormalOutputData($inbox); ($this->normalOutputBoundary)($normalOutput); }
  56. 56. Interactor::__invoke() OutputBoundary 
 56 /** * @param InputData $input */ public function __invoke(InputData $input): void { $inbox = $this->produceEntity($input); $this->taskRepository->save($inbox); $normalOutput = $this->produceNormalOutputData($inbox); ($this->normalOutputBoundary)($normalOutput); }
  57. 57. 57
  58. 58. • • Repository • 🙆 • Eloquent 58
  59. 59. Web UI 59
  60. 60. Web UI • • Controller 
 void • 
 60
  61. 61. Controller 61 <?php namespace MyAppWebControllers; use IlluminateHttpRequest; use MyAppComponentsTasksUseCasesCreateInboxInputData; use MyAppComponentsTasksUseCasesCreateInboxInputBoundary; /** * Class CreateInbox * @package MyAppWebControllers */ final class CreateInbox extends Controller { /** * @param Request $request * @param InputBoundary $interactor */ public function __invoke(Request $request, InputBoundary $interactor) { $validated = $this->validate($request, [ 'name' => 'required|string|max:255', 'note' => 'string|nullable', ]); $input = new InputData($validated['name'], $validated['note'] ?? ''); $interactor($input); } }
  62. 62. Controller 62 <?php namespace MyAppWebControllers; use IlluminateHttpRequest; use MyAppComponentsTasksUseCasesCreateInboxInputData; use MyAppComponentsTasksUseCasesCreateInboxInputBoundary; /** * Class CreateInbox * @package MyAppWebControllers */ final class CreateInbox extends Controller { /** * @param Request $request * @param InputBoundary $interactor */ public function __invoke(Request $request, InputBoundary $interactor) { $validated = $this->validate($request, [ 'name' => 'required|string|max:255', 'note' => 'string|nullable', ]); $input = new InputData($validated['name'], $validated['note'] ?? ''); $interactor($input); } } Laravel HttpControllers
  63. 63. Controller 63 <?php namespace MyAppWebControllers; use IlluminateHttpRequest; use MyAppComponentsTasksUseCasesCreateInboxInputData; use MyAppComponentsTasksUseCasesCreateInboxInputBoundary; /** * Class CreateInbox * @package MyAppWebControllers */ final class CreateInbox extends Controller { /** * @param Request $request * @param InputBoundary $interactor */ public function __invoke(Request $request, InputBoundary $interactor) { $validated = $this->validate($request, [ 'name' => 'required|string|max:255', 'note' => 'string|nullable', ]); $input = new InputData($validated['name'], $validated['note'] ?? ''); $interactor($input); } } HTTP
  64. 64. Controller 64 <?php namespace MyAppWebControllers; use IlluminateHttpRequest; use MyAppComponentsTasksUseCasesCreateInboxInputData; use MyAppComponentsTasksUseCasesCreateInboxInputBoundary; /** * Class CreateInbox * @package MyAppWebControllers */ final class CreateInbox extends Controller { /** * @param Request $request * @param InputBoundary $interactor */ public function __invoke(Request $request, InputBoundary $interactor) { $validated = $this->validate($request, [ 'name' => 'required|string|max:255', 'note' => 'string|nullable', ]); $input = new InputData($validated['name'], $validated['note'] ?? ''); $interactor($input); } } InputBoundary Interactor
  65. 65. Controller 65 <?php namespace MyAppWebControllers; use IlluminateHttpRequest; use MyAppComponentsTasksUseCasesCreateInboxInputData; use MyAppComponentsTasksUseCasesCreateInboxInputBoundary; /** * Class CreateInbox * @package MyAppWebControllers */ final class CreateInbox extends Controller { /** * @param Request $request * @param InputBoundary $interactor */ public function __invoke(Request $request, InputBoundary $interactor) { $validated = $this->validate($request, [ 'name' => 'required|string|max:255', 'note' => 'string|nullable', ]); $input = new InputData($validated['name'], $validated['note'] ?? ''); $interactor($input); } }
  66. 66. Presenter • 66 <?php namespace MyAppWebPresentersCreateInbox; use IlluminateViewFactory; use MyAppWebPresentersPresenter as BasePresenter; use MyAppComponentsTasksUseCasesCreateInboxNormalOutputData; use MyAppComponentsTasksUseCasesCreateInboxNormalOutputBoundary; /** * Class CreateInboxPresenter * @package MyAppWebPresenters */ final class Presenter extends BasePresenter implements NormalOutputBoundary { // 中略 /** * @param NormalOutputData $output */ public function __invoke(NormalOutputData $output): void { $viewModel = new ViewModel( $output->taskId(), $output->taskName(), $output->taskNote(), ); $this->respond($this->view->make('web::tasks.create', compact(['viewModel']))); } }
  67. 67. Presenter • 67 <?php namespace MyAppWebPresentersCreateInbox; use IlluminateViewFactory; use MyAppWebPresentersPresenter as BasePresenter; use MyAppComponentsTasksUseCasesCreateInboxNormalOutputData; use MyAppComponentsTasksUseCasesCreateInboxNormalOutputBoundary; /** * Class CreateInboxPresenter * @package MyAppWebPresenters */ final class Presenter extends BasePresenter implements NormalOutputBoundary { // 中略 /** * @param NormalOutputData $output */ public function __invoke(NormalOutputData $output): void { $viewModel = new ViewModel( $output->taskId(), $output->taskName(), $output->taskNote(), ); $this->respond($this->view->make('web::tasks.create', compact(['viewModel']))); } } Interactor ViewModel
  68. 68. Presenter • 68 <?php namespace MyAppWebPresentersCreateInbox; use IlluminateViewFactory; use MyAppWebPresentersPresenter as BasePresenter; use MyAppComponentsTasksUseCasesCreateInboxNormalOutputData; use MyAppComponentsTasksUseCasesCreateInboxNormalOutputBoundary; /** * Class CreateInboxPresenter * @package MyAppWebPresenters */ final class Presenter extends BasePresenter implements NormalOutputBoundary { // 中略 /** * @param NormalOutputData $output */ public function __invoke(NormalOutputData $output): void { $viewModel = new ViewModel( $output->taskId(), $output->taskName(), $output->taskNote(), ); $this->respond($this->view->make('web::tasks.create', compact(['viewModel']))); } } View ※ Controller 
 void ……🙇
  69. 69. • • ServiceProvider 69
  70. 70. 70 <?php namespace AppProviders; use IlluminateSupportServiceProvider; use MyAppComponentsTasksUseCasesCreateInboxInputBoundary; use MyAppComponentsTasksUseCasesCreateInboxInteractor; use MyAppComponentsTasksUseCasesCreateInboxNormalOutputBoundary; use MyAppComponentsTasksUseCasesIdProvider; use MyAppComponentsTasksUseCasesTaskRepository as TaskRepositoryInterface; use MyAppDatabaseRepositoriesAutoIncrementTaskIdProvider; use MyAppDatabaseRepositoriesTaskRepository; use MyAppWebPresentersCreateInboxPresenter; class AppServiceProvider extends ServiceProvider { /** * Register any application services. * * @return void */ public function register() { $this->app->bind(IdProvider::class, AutoIncrementTaskIdProvider::class); $this->app->bind(TaskRepositoryInterface::class, TaskRepository::class); $this->app->bind(InputBoundary::class, Interactor::class); $this->app->bind(NormalOutputBoundary::class, Presenter::class); } /** * Bootstrap any application services. * * @return void */ public function boot() { // } }
  71. 71. Laravel • Laravel • DI ServiceContainer • 
 ServiceProvider • 
 71
  72. 72. @Laravel JP Conference 2019 https://speakerdeck.com/mikakane/laravel-package- development 72
  73. 73. 73
  74. 74. 74 packages 

  75. 75. 75 

  76. 76. 76 
 

  77. 77. 77 web routes 
 views
  78. 78. • 2 • 3 • 5 • 10 • 3 • 2 78
  79. 79. 79 UI
  80. 80. package foo; class Foo { Buz x; function doFoo() { x.process(); } } interface Buz { function process(); } 80 package bar; import foo; class Bar implements foo.Buz { function process() { // do something... } }
  81. 81. 🏁 • • • Laravel 
 81
  82. 82. • 2 • 3 • 5 • 10 • 3 • 2 82
  83. 83. / @okashoi @okashoi 83
  84. 84. • Hacker’s GATE 84
  85. 85. • Oysters 85
  86. 86. • Laravel JP Conference 2020 86https://conference2020.laravel.jp/
  87. 87. Ask the Speaker 
 😂 87

×