Mais conteúdo relacionado Semelhante a Laravel でやってみるクリーンアーキテクチャ #phpconfuk (20) Mais de Shohei Okada (20) Laravel でやってみるクリーンアーキテクチャ #phpconfuk9. ”The Clean Architecture - The Clean Code Blog”,
https://blog.cleancoder.com/uncle-bob/2012/08/13/the-clean-architecture.html
9
23. package foo;
import bar;
class Foo {
bar.Bar x;
function doFoo() {
x.process();
}
}
23
package bar;
class Bar {
function process() {
// do something...
}
}
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. 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. 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. 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. 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. 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. 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. 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
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);
}
47. InputBoundary / OutputBoundary
•
47
<?php
namespace MyAppComponentsTasksUseCasesCreateInbox;
/**
* Interface InputBoundary
* @package MyAppComponentsTasksUseCasesCreateInbox
*/
interface InputBoundary
{
/**
* @param InputData $input
*/
public function __invoke(InputData $input): void;
}
48. InputBoundary / OutputBoundary
•
48
<?php
namespace MyAppComponentsTasksUseCasesCreateInbox;
/**
* Interface NormalOutputBoundary
* @package MyAppComponentsTasksUseCasesCreateInbox
*/
interface NormalOutputBoundary
{
/**
* @param NormalOutputData $output
*/
public function __invoke(NormalOutputData $output): void;
}
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. 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. 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. 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. 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. 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. 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. 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);
}
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. 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. 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. 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. 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. 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. 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. 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 ……🙇
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()
{
//
}
}
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...
}
}
86. • Laravel JP Conference 2020
86https://conference2020.laravel.jp/