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

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

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

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

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

Shohei Okada

June 28, 2019
Tweet

More Decks by Shohei Okada

Other Decks in Programming

Transcript

  1. Laravel Ͱ΍ͬͯΈΔ
    ΫϦʔϯΞʔΩςΫνϟ
    PHP Conference Fukuoka 2019
    @okashoi WILLGATE, Inc.

    View Slide

  2. ͸͡Ίʹ
    • εϥΠυ͸ެ։ࡁΈͰ͢
    • ࣸਅ౳΋׻ܴͰ͢ 🙆
    • ײ૝ɾϑΟʔυόοΫ౳͓଴͍ͪͯ͠·͢ʂ
    • ϋογϡλά #phpconfuk #hall_fu
    2

    View Slide

  3. ໨࣍
    • ͜ͷτʔΫͷΊ͟͢ͱ͜Ζʢ2 ෼ʣ
    • ΫϦʔϯΞʔΩςΫνϟͷ֓ཁʢ3 ෼ʣ
    • ίΞͱͳΔߟ͑ํʢ5 ෼ʣ
    • ۩ମతͳίʔυྫʢ10 ෼ʣ
    • ·ͱΊʢ3 ෼ʣ
    • ࣗݾ঺հʢ2 ෼ʣ
    3

    View Slide

  4. ໨࣍
    • ͜ͷτʔΫͷΊ͟͢ͱ͜Ζʢ2 ෼ʣ
    • ΫϦʔϯΞʔΩςΫνϟͷ֓ཁʢ3 ෼ʣ
    • ίΞͱͳΔߟ͑ํʢ5 ෼ʣ
    • ۩ମతͳίʔυྫʢ10 ෼ʣ
    • ·ͱΊʢ3 ෼ʣ
    • ࣗݾ঺հʢ2 ෼ʣ
    4

    View Slide

  5. ͜ͷτʔΫͷΊ͟͢ͱ͜Ζ🏁
    • ΫϦʔϯΞʔΩςΫνϟͷίΞͱͳΔߟ͑ํ
    • ͦΕΛ࣮ݱ͢ΔͨΊͷ۩ମతͳίʔσΟϯά
    • ͦΕΒΛ࣮ફ͢Δ্Ͱͷ Laravel ͱͷ෇͖߹͍ํ
    ͜ΕΒΛ஌ͬͯ΋Β͍ɺͦͷҰ෦෼͚ͩͰ΋

    ͜Ε͔Βͷ։ൃʹ׆͔ͯ͠΋Β͏
    5

    View Slide

  6. ͜ͷτʔΫͰ࿩͞ͳ͍͜ͱ🙅
    • Laravel ͷػೳͷղઆ
    • ʰΫϦʔϯΞʔΩςΫνϟʱʹग़ͯ͘Δ֓೦
    ͷৄࡉͳղઆ
    6

    View Slide

  7. ໨࣍
    • ͜ͷτʔΫͷΊ͟͢ͱ͜Ζʢ2 ෼ʣ
    • ΫϦʔϯΞʔΩςΫνϟͷ֓ཁʢ3 ෼ʣ
    • ίΞͱͳΔߟ͑ํʢ5 ෼ʣ
    • ۩ମతͳίʔυྫʢ10 ෼ʣ
    • ·ͱΊʢ3 ෼ʣ
    • ࣗݾ঺հʢ2 ෼ʣ
    7

    View Slide

  8. ݪయ
    8
    2017 ೥ॳ൛ൃߦ 2018 ೥ॳ൛ൃߦ

    View Slide

  9. ΫϦʔϯΞʔΩςΫνϟͷਤ
    ग़యɿ”The Clean Architecture - The Clean Code Blog”,
    https://blog.cleancoder.com/uncle-bob/2012/08/13/the-clean-architecture.html
    9

    View Slide

  10. ΫϦʔϯΞʔΩςΫνϟͷਤ
    • ΫϦʔϯΞʔΩςΫνϟʹ͍ͭͯௐ΂Δͱ

    ·ͣ໨ʹ͢Δਤ
    • ຊτʔΫͰ͸֤૚ͷৄࡉͳղઆ͸͠ͳ͍
    • ͜ͷਤʹ஧࣮ʹ࣮૷͑͢͞Ε͹Α͍ɺͱ͍͏
    Θ͚Ͱ͸ͳ͍
    10

    View Slide

  11. ԿΛ໨తͱ͍ͯ͠Δ͔ʁ
    • ಠཱ։ൃՄೳ
    • ಠཱσϓϩΠՄೳʢ˞ʣ
    • ςετՄೳ
    ※ PHP ։ൃʹ͓͍ͯ͸Πϝʔδ͠ʹ͍͔͘΋
    11

    View Slide

  12. “ιϑτ΢ΣΞΞʔΩςΫνϟͷ໨త͸ɺ

    ٻΊΒΕΔγεςϜΛߏஙɾอक͢ΔͨΊʹ
    ඞཁͳਓࡐΛ࠷খݶʹ཈͑Δ͜ͱͰ͋Δɻ”
    12

    View Slide

  13. ໨࣍
    • ͜ͷτʔΫͷΊ͟͢ͱ͜Ζʢ2 ෼ʣ
    • ΫϦʔϯΞʔΩςΫνϟͷ֓ཁʢ3 ෼ʣ
    • ίΞͱͳΔߟ͑ํʢ5 ෼ʣ
    • ۩ମతͳίʔυྫʢ10 ෼ʣ
    • ·ͱΊʢ3 ෼ʣ
    • ࣗݾ঺հʢ2 ෼ʣ
    13

    View Slide

  14. ίΞͱͳΔߟ͑ํ
    ʮॏཁͳ΋ͷʯΛʮࠣࡉͳ΋ͷʯʹґଘͤ͞ͳ͍
    14

    View Slide

  15. ॏཁͳ΋ͷʁ
    15
    σʔλϕʔε
    ϑϨʔϜϫʔΫ
    ςετ
    6* 8FC
    03.

    View Slide

  16. ॏཁͳ΋ͷʁ
    16
    σʔλϕʔε
    ϑϨʔϜϫʔΫ
    ςετ
    6* 8FC
    03.
    🙅
    ⾠͋͘·ͰΫϦʔϯΞʔΩςΫνϟͷจ຺ͷ࿩

    View Slide

  17. ॏཁͳ΋ͷʁ
    ॏཁͳ΋ͷ͸ʮϏδωεϧʔϧʯ
    17

    View Slide

  18. ΫϦʔϯΞʔΩςΫνϟͷਤ
    18

    View Slide

  19. ॏཁͳ΋ͷʁ
    ॏཁͳ΋ͷ͸ʮϏδωεϧʔϧʯ
    • ΤϯςΟςΟ
    • ࠷ॏཁϏδωεϧʔϧ
    • Ϣʔεέʔε
    • ΞϓϦέʔγϣϯݻ༗ͷϏδωεϧʔϧ
    19

    View Slide

  20. ίΞͱͳΔߟ͑ํ
    ʮॏཁͳ΋ͷʯΛʮࠣࡉͳ΋ͷʯʹґଘͤ͞ͳ͍
    20

    View Slide

  21. ίΞͱͳΔߟ͑ํ
    ʮॏཁͳ΋ͷʯΛʮࠣࡉͳ΋ͷʯʹґଘͤ͞ͳ͍
    21
    ɹɹ ↑
    Ͳ͏΍ͬͯʁ

    View Slide

  22. ґଘؔ܎ٯస
    22

    View Slide

  23. యܕతͳґଘؔ܎ͷίʔυ
    package foo;
    import bar;
    class Foo {
    bar.Bar x;
    function doFoo() {
    x.process();
    }
    }
    23
    package bar;
    class Bar {
    function process() {
    // do something...
    }
    }

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  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...
    }
    }

    View Slide

  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...
    }
    }

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  32. “ιϑτ΢ΣΞΞʔΩςΫτ͸ɺ

    γεςϜʹ͋Δ͢΂ͯͷιʔείʔυͷ

    ґଘؔ܎ͷํ޲Λઈରతʹ੍ޚͰ͖Δ”
    32

    View Slide

  33. Կ͕خ͍͠ͷ͔ʁ
    • Ұൠ࿦ͱͯ͠ɺԿ͔Λมߋͨ͠ͱ͖

    ͦΕʹґଘ͍ͯ͠Δ΋ͷશͯʹӨڹ͕ٴͿ
    ➡ มߋ͞Εʹ͍͘΋ͷʹґଘ͢ΔΑ͏ʹ

    ઃܭ͢Δ͜ͱͰӨڹΛখ͘͞ཹΊΒΕΔ
    33

    View Slide

  34. ίΞͱͳΔߟ͑ํ
    ʮॏཁͳ΋ͷʯΛʮࠣࡉͳ΋ͷʯʹґଘͤ͞ͳ͍
    34
    σʔλϕʔεɾ
    UIɾϑϨʔϜϫʔΫͳͲ
    ༰қʹมߋͰ͖Δ
    Ϗδωεϧʔϧ
    มߋ͕ى͜Γʹ͍͘

    View Slide

  35. ໨࣍
    • ͜ͷτʔΫͷΊ͟͢ͱ͜Ζʢ2 ෼ʣ
    • ΫϦʔϯΞʔΩςΫνϟͷ֓ཁʢ3 ෼ʣ
    • ίΞͱͳΔߟ͑ํʢ5 ෼ʣ
    • ۩ମతͳίʔυྫʢ10 ෼ʣ
    • ·ͱΊʢ3 ෼ʣ
    • ࣗݾ঺հʢ2 ෼ʣ
    35

    View Slide

  36. CleanArchitecture ΋͏ͻͱͭͷਤ
    ग़యɿʰClean Architecture ୡਓʹֶͿιϑτ΢ΣΞͷߏ଄ͱઃܭʱ
    ʢKindle ൛ɺ ҐஔNo. 3228/6016ʣ 36

    View Slide

  37. αϯϓϧɿλεΫ؅ཧπʔϧ
    • ԼهϦϙδτϦͰ։ൃத
    • https://github.com/okashoi/laravel-clean-
    architecture
    • ͋͘·ͰʮҰྫʯͰ͋Δ͜ͱʹཹҙ
    • ͜ͷܗ͕ઈରతͳ౴͑Ͱ͸ͳ͍
    37

    View Slide

  38. ΤϯςΟςΟ
    38

    View Slide

  39. ·ͣ͸ϏδωεϧʔϧΛநग़
    • ʮ࢖͍ํʯʹґΒͳ͍ϧʔϧΛݟग़͢
    • ػೳҰཡͰ͸ͳ͍
    ʮλεΫΛ࡞੒Ͱ͖ΔʯʮλεΫ໊ΛมߋͰ͖Δʯ
    ʮλεΫ͸ඞͣλεΫIDͱλεΫ໊Λ࣋ͭʯ

    ɹʮணख೔ʹաڈͷ೔෇ΛೳಈతʹઃఆͰ͖ͳ͍ʯ
    39

    View Slide

  40. ࣗવݴޠʹىͯ͜͠ readme ʹॻ͍ͨ
    40

    View Slide

  41. ΤϯςΟςΟͷ࣮૷
    • நग़ͨ͠ϏδωεϧʔϧΛ࣮૷Ͱදݱ͢Δ
    • ϑϨʔϜϫʔΫ΍σʔλϕʔε౳ʹґଘͤ͞ͳ͍
    • POPOʢPlain Old PHP ObjectʣͰॻ͘
    ➡ ґଘ͢Δ΋ͷ͕ແ͍ͷͰಠཱͯ͠։ൃՄೳ
    41

    View Slide

  42. ϑϨʔϜϫʔΫʹґଘͤ͞ͳ͍
    • ґଘͤ͞Δͷ͸ PHP ૊ΈࠐΈͷΫϥεఔ౓
    42
    namespace MyApp\Components\Tasks\Entities;
    use DatetimeImmutable;
    /**
    * Class Inbox
    * @package MyApp\Components\Tasks\Entities
    */
    final class Inbox extends Task
    {
    /**
    * @var EstimatedTime|null
    */
    private $estimatedTime;

    View Slide

  43. Ϣχοτςετ
    • ґଘͷແ͍७ਮͳϩδοΫͳͷͰॻ͖΍͍͢
    • நग़ͨ͠Ϗδωεϧʔϧͷจݴ͕ͦͷ··ς
    ετέʔεʹͳΔ
    43
    /**
    * @test
    * @expectedException \MyApp\Components\Tasks\Entities\EstimatedTimeNotSet
    */
    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);
    }

    View Slide

  44. Ϣʔεέʔε
    44

    View Slide

  45. Ϣʔεέʔε
    45

    View Slide

  46. Ϣʔεέʔεͷ࣮૷
    • ΞϓϦέʔγϣϯݻ༗ͷϏδωεϧʔϧ
    • ೖग़ྗͱΤϯςΟςΟΛഔհ͢Δ

    ʮૢ࡞ʯͷΠϝʔδ
    • ೖग़ྗͷσʔλ͸ഔମ΍σʔλϕʔεʹ

    ґଘ͠ͳ͍ܗʹ͢Δ
    46

    View Slide

  47. InputBoundary / OutputBoundary
    • ೖྗʢग़ྗʣσʔλΛҾ਺ʹऔΔ

    ݺͼग़͠ՄೳͳΠϯλʔϑΣʔε
    47
    namespace MyApp\Components\Tasks\UseCases\CreateInbox;
    /**
    * Interface InputBoundary
    * @package MyApp\Components\Tasks\UseCases\CreateInbox
    */
    interface InputBoundary
    {
    /**
    * @param InputData $input
    */
    public function __invoke(InputData $input): void;
    }

    View Slide

  48. InputBoundary / OutputBoundary
    • ೖྗʢग़ྗʣσʔλΛҾ਺ʹऔΔ

    ݺͼग़͠ՄೳͳΠϯλʔϑΣʔε
    48
    namespace MyApp\Components\Tasks\UseCases\CreateInbox;
    /**
    * Interface NormalOutputBoundary
    * @package MyApp\Components\Tasks\UseCases\CreateInbox
    */
    interface NormalOutputBoundary
    {
    /**
    * @param NormalOutputData $output
    */
    public function __invoke(NormalOutputData $output): void;
    }

    View Slide

  49. Interactor
    49
    namespace MyApp\Components\Tasks\UseCases\CreateInbox;
    use MyApp\Components\Tasks\Entities\{Inbox, Task, Name, Note};
    use MyApp\Components\Tasks\UseCases\{IdProvider, TaskRepository};
    /**
    * Class Interactor
    * @package MyApp\Components\Tasks\UseCases\CreateInbox
    */
    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

    View Slide

  50. Interactor
    50
    namespace MyApp\Components\Tasks\UseCases\CreateInbox;
    use MyApp\Components\Tasks\Entities\{Inbox, Task, Name, Note};
    use MyApp\Components\Tasks\UseCases\{IdProvider, TaskRepository};
    /**
    * Class Interactor
    * @package MyApp\Components\Tasks\UseCases\CreateInbox
    */
    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 ͷ࣮૷

    View Slide

  51. Interactor
    51
    namespace MyApp\Components\Tasks\UseCases\CreateInbox;
    use MyApp\Components\Tasks\Entities\{Inbox, Task, Name, Note};
    use MyApp\Components\Tasks\UseCases\{IdProvider, TaskRepository};
    /**
    * Class Interactor
    * @package MyApp\Components\Tasks\UseCases\CreateInbox
    */
    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ʣ
    ※ґଘͯ͠Δ΋ͷΛϞοΫԽͯ͠

    ɹςετ͕ॻ͖΍͍͢

    View Slide

  52. Interactor
    52
    namespace MyApp\Components\Tasks\UseCases\CreateInbox;
    use MyApp\Components\Tasks\Entities\{Inbox, Task, Name, Note};
    use MyApp\Components\Tasks\UseCases\{IdProvider, TaskRepository};
    /**
    * Class Interactor
    * @package MyApp\Components\Tasks\UseCases\CreateInbox
    */
    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
    ॲཧຊମ

    ʢ࣍εϥΠυʹղઆଓ͘ʣ

    View Slide

  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);
    }

    View Slide

  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);
    }

    View Slide

  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);
    }

    View Slide

  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);
    }

    View Slide

  57. σʔλϕʔε
    57

    View Slide

  58. σʔλϕʔε
    • ϢʔεέʔεͰఆٛͨ͠ΠϯλʔϑΣʔεΛ࣮૷
    • Repository ύλʔϯΛ࢖͏ͷΛΑ͘ݟΔ
    • ͜ͷ͋ͨΓ͸ϑϨʔϜϫʔΫʹґଘͤͯ͞΋🙆
    • Eloquent Λ࢖͏ͳΒ͜͜Ͱ࢖͏
    58

    View Slide

  59. WebʗUI
    59

    View Slide

  60. WebʗUI
    • ϢʔεέʔεͰఆٛͨ͠ΠϯλʔϑΣʔεΛ࣮૷
    • ਤʹ஧࣮ʹ΍ΔͳΒ Controller ϝιουͷ

    ໭Γ஋͸ void
    • ͕ɺ͜ͷܗʹ͸ͦ͜·Ͱͩ͜ΘΒͳͯ͘Α͍

    ʢมଇతͳΧελϚΠζ͕ඞཁʹͳΔʣ
    60

    View Slide

  61. Controller
    61
    namespace MyApp\Web\Controllers;
    use Illuminate\Http\Request;
    use MyApp\Components\Tasks\UseCases\CreateInbox\InputData;
    use MyApp\Components\Tasks\UseCases\CreateInbox\InputBoundary;
    /**
    * Class CreateInbox
    * @package MyApp\Web\Controllers
    */
    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);
    }
    }

    View Slide

  62. Controller
    62
    namespace MyApp\Web\Controllers;
    use Illuminate\Http\Request;
    use MyApp\Components\Tasks\UseCases\CreateInbox\InputData;
    use MyApp\Components\Tasks\UseCases\CreateInbox\InputBoundary;
    /**
    * Class CreateInbox
    * @package MyApp\Web\Controllers
    */
    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 ͷ Http\Controllers ͱಉ͡Α͏ʹॻ͚Δ

    View Slide

  63. Controller
    63
    namespace MyApp\Web\Controllers;
    use Illuminate\Http\Request;
    use MyApp\Components\Tasks\UseCases\CreateInbox\InputData;
    use MyApp\Components\Tasks\UseCases\CreateInbox\InputBoundary;
    /**
    * Class CreateInbox
    * @package MyApp\Web\Controllers
    */
    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ϦΫΤετˠ७ਮͳೖྗσʔλʹม׵

    View Slide

  64. Controller
    64
    namespace MyApp\Web\Controllers;
    use Illuminate\Http\Request;
    use MyApp\Components\Tasks\UseCases\CreateInbox\InputData;
    use MyApp\Components\Tasks\UseCases\CreateInbox\InputBoundary;
    /**
    * Class CreateInbox
    * @package MyApp\Web\Controllers
    */
    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ʣΛݺͼग़͢

    View Slide

  65. Controller
    65
    namespace MyApp\Web\Controllers;
    use Illuminate\Http\Request;
    use MyApp\Components\Tasks\UseCases\CreateInbox\InputData;
    use MyApp\Components\Tasks\UseCases\CreateInbox\InputBoundary;
    /**
    * Class CreateInbox
    * @package MyApp\Web\Controllers
    */
    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);
    }
    }

    View Slide

  66. Presenter
    • આ໌
    66
    namespace MyApp\Web\Presenters\CreateInbox;
    use Illuminate\View\Factory;
    use MyApp\Web\Presenters\Presenter as BasePresenter;
    use MyApp\Components\Tasks\UseCases\CreateInbox\NormalOutputData;
    use MyApp\Components\Tasks\UseCases\CreateInbox\NormalOutputBoundary;
    /**
    * Class CreateInboxPresenter
    * @package MyApp\Web\Presenters
    */
    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 Slide

  67. Presenter
    • આ໌
    67
    namespace MyApp\Web\Presenters\CreateInbox;
    use Illuminate\View\Factory;
    use MyApp\Web\Presenters\Presenter as BasePresenter;
    use MyApp\Components\Tasks\UseCases\CreateInbox\NormalOutputData;
    use MyApp\Components\Tasks\UseCases\CreateInbox\NormalOutputBoundary;
    /**
    * Class CreateInboxPresenter
    * @package MyApp\Web\Presenters
    */
    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 ʹม׵

    View Slide

  68. Presenter
    • આ໌
    68
    namespace MyApp\Web\Presenters\CreateInbox;
    use Illuminate\View\Factory;
    use MyApp\Web\Presenters\Presenter as BasePresenter;
    use MyApp\Components\Tasks\UseCases\CreateInbox\NormalOutputData;
    use MyApp\Components\Tasks\UseCases\CreateInbox\NormalOutputBoundary;
    /**
    * Class CreateInboxPresenter
    * @package MyApp\Web\Presenters
    */
    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 ʹ͢ΔͨΊͳͷͰؾʹ͠ͳ͍Ͱ……🙇

    View Slide

  69. ࢓্͛
    • ΠϯλʔϑΣʔεͱ࣮૷ͷΫϥεΛ݁ͼ͚ͭΔ
    • ServiceProvider Ͱ΍Δ
    69

    View Slide

  70. 70
    namespace App\Providers;
    use Illuminate\Support\ServiceProvider;
    use MyApp\Components\Tasks\UseCases\CreateInbox\InputBoundary;
    use MyApp\Components\Tasks\UseCases\CreateInbox\Interactor;
    use MyApp\Components\Tasks\UseCases\CreateInbox\NormalOutputBoundary;
    use MyApp\Components\Tasks\UseCases\IdProvider;
    use MyApp\Components\Tasks\UseCases\TaskRepository as TaskRepositoryInterface;
    use MyApp\Database\Repositories\AutoIncrementTaskIdProvider;
    use MyApp\Database\Repositories\TaskRepository;
    use MyApp\Web\Presenters\CreateInbox\Presenter;
    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()
    {
    //
    }
    }

    View Slide

  71. Laravel ͱͷ෇͖߹͍ํ
    • Laravel ͷػೳΛ࢖͏ͳΒ֎ଆͷ૚Ͱ࢖͏
    • ڧྗͳ DI ͷ࢓૊Έʹ৐͔ͬΔʢServiceContainerʣ
    • ύοέʔδʹ෼͚ͯ։ൃͯ͠

    ServiceProvider ͰͦΕΒΛ౷߹͢Δ
    • ී௨ʹॻ͘Ҏ্ʹґଘؔ܎Λҙࣝ͢ΔͷͰ

    ૬ੑ͕͍͍ؾ͕͍ͯ͠Δ
    71

    View Slide

  72. ࢀߟεϥΠυ@Laravel JP Conference 2019
    https://speakerdeck.com/mikakane/laravel-package-
    development 72

    View Slide

  73. σΟϨΫτϦߏ੒
    73

    View Slide

  74. σΟϨΫτϦߏ੒
    74
    packages ҎԼʹ

    ύοέʔδΛ࡞ͬͯΏ͘

    View Slide

  75. σΟϨΫτϦߏ੒
    75
    ؔ৺ࣄ୯ҐͰσΟϨΫτϦΛ੾Γ

    ϏδωεϧʔϧΛ࣮૷

    View Slide

  76. σΟϨΫτϦߏ੒
    76
    σʔλͷѻ͏ॲཧ͚ͩͰͳ͘

    ϚΠάϨʔγϣϯ΋

    ಉҰͷύοέʔδʹೖΕΔ

    View Slide

  77. σΟϨΫτϦߏ੒
    77
    web ͱ͍͏ׅΓͰ routes ΍

    views ΋ಉҰύοέʔδʹ

    View Slide

  78. ໨࣍
    • ͜ͷτʔΫͷΊ͟͢ͱ͜Ζʢ2 ෼ʣ
    • ΫϦʔϯΞʔΩςΫνϟͷ֓ཁʢ3 ෼ʣ
    • ίΞͱͳΔߟ͑ํʢ5 ෼ʣ
    • ۩ମతͳίʔυྫʢ10 ෼ʣ
    • ·ͱΊʢ3 ෼ʣ
    • ࣗݾ঺հʢ2 ෼ʣ
    78

    View Slide

  79. ίΞͱͳΔߟ͑ํ
    ʮॏཁͳ΋ͷʯΛʮࠣࡉͳ΋ͷʯʹґଘͤ͞ͳ͍
    79
    σʔλϕʔεɾ
    UIɾϑϨʔϜϫʔΫͳͲ
    ༰қʹมߋͰ͖Δ
    Ϗδωεϧʔϧ
    มߋ͕ى͜Γʹ͍͘

    View Slide

  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...
    }
    }

    View Slide

  81. ͜ͷτʔΫͷΊ͟͢ͱ͜Ζ🏁
    • ΫϦʔϯΞʔΩςΫνϟͷίΞͱͳΔߟ͑ํ
    • ͦΕΛ࣮ݱ͢ΔͨΊͷ۩ମతͳίʔσΟϯά
    • ͦΕΒΛ࣮ફ͢Δ্Ͱͷ Laravel ͱͷ෇͖߹͍ํ
    ͜ΕΒΛ஌ͬͯ΋Β͍ɺͦͷҰ෦෼͚ͩͰ΋

    ͜Ε͔Βͷ։ൃʹ׆͔ͯ͠΋Β͏
    81

    View Slide

  82. ໨࣍
    • ͜ͷτʔΫͷΊ͟͢ͱ͜Ζʢ2 ෼ʣ
    • ΫϦʔϯΞʔΩςΫνϟͷ֓ཁʢ3 ෼ʣ
    • ίΞͱͳΔߟ͑ํʢ5 ෼ʣ
    • ۩ମతͳίʔυྫʢ10 ෼ʣ
    • ·ͱΊʢ3 ෼ʣ
    • ࣗݾ঺հʢ2 ෼ʣ
    82

    View Slide

  83. ࣗݾ঺հ
    Ԭా ਖ਼ฏ / ͓͔͠ΐ͍
    @okashoi
    @okashoi
    ΞʔΩςΫτ ݉ ٕज़޿ใ
    83

    View Slide

  84. ࣗݾ঺հ
    ίϛϡχςΟ׆ಈ
    • Hacker’s GATE ӡӦ
    84

    View Slide

  85. ࣗݾ঺հ
    ίϛϡχςΟ׆ಈ
    • एख΋ͷͮ͘Γूஂ Oysters ϝϯόʔ
    85

    View Slide

  86. ࣗݾ঺հ
    ίϛϡχςΟ׆ಈ
    • Laravel JP Conference 2020 ίΞελοϑ
    86
    https://conference2020.laravel.jp/

    View Slide

  87. ࣗݾ঺հ
    Ask the Speaker ΍ ࠙਌ձͰ

    ͓࿩͕Ͱ͖ͨΒخ͍͠Ͱ͢ 😂
    87

    View Slide