В прошлый раз я озадачился двумя вопросами о Clean Architecture: как работать со структурами данных типа дерева и какими все же должны быть Сущности. Подумав и почитав ветки, пришел к следующему.
Структура данных Сущности должна отображаться либо в ней самой, либо в специально созданной другой сущности. Но при этом служебным данным в них не место. Например, если нужно иметь дерево задач, я могу эмулировать его свойством children, куда помещать задачи-непосредственные потомки. Или же создать отдельную сущность TaskTree, которая будет содержать иерархию Task. Думаю, на теперешнем этапе более приемлем первый вариант.
public $children = [];
Разумеется, должны добавиться методы, обрабатывающие эту иерархию, в нашем случае addChild()
, removeChild()
и другие. Чтобы не грузить каждый раз всю иерархию, можно добавить поле $hasChildren
, которое будет показывать, есть ли у задачи потомки.
Далее. Как я понял из комментариев, Сущность в первую очередь должна определять бизнес-правила обработки себя. Заполнение, выборка, валидация — все, что работает на высшем, самом абстрактном уровне иерархии приложения. При этом непосредственного доступа к данным она может и не иметь. То есть, Сущность может и должна быть абстрактным классом, от которой наследуются конкретные реализации с ORM, например. Такое усложнение и вызвано, по-видимому, наличием слоя ORM, который сам работает с БД, но при этом должен реализовать требуемые бизнес-методы сущности. Это говорит о том, что у меня все неправильно. Впрочем, ломать сейчас не буду. Есть частные мнения, что сущности все же могут быть конкретными классами с нормальными полями данных, формироваться из данных БД они могут в Репозиториях. Кроме того, хочу, чтобы изменения в коде были естественными, а пока не вижу необходимости менять реализацию класса.
На сегодняшний день реализован сценарий создания задачи.
$taskCreator = new Interactor\Task\Creation($this->taskRepo, $this->userRepo, $this->sessionService);
$this->boolResult = $taskCreator->execute($data);
При создании в него инжектируются репозитории задачи и пользователя, а также сервис для работы с сессией. Для выполнения он получает на вход данные новой задачи, которые дополняет ID создателя и вызывает метод создания задачи в репозитории.
$data['userId'] = $this->session->getLoggedInUserId();
$this->taskRepo->create($data);
В чем достоинство такого кода? Создание задачи отделено от обслуживающего вызов кода (в данном случае, теста). Бизнес-логика реализуется внутри Интерактора, только он знает, какие поля выбрать, куда записать и как сохранить. Но при этом абстрагируется от конкретного способа сохранения — сейчас за это отвечает mock-репозиторий , работающий с оперативной памятью.
Недостатков у кода много, о них пока не буду. Интересно посмотреть, как такой код может взаимодействовать с запросами извне.
Comments: 4 RSS
1 aktuba 31-05-2014 22:38
Чет какая-то жесть у тебя... Ты уверен, что пишешь на php, а не на java? У тебя слишком много врапперов ради врапперов.
Почему-бы не заюзать Dependency Injection? Для чего постоянно передавать одно и то же? В итоге останется:
2 Александр Купреев 31-05-2014 23:20
Сейчас жесть, а то ли еще будет?
Мне казалось, в интеракторе я использую DI - передаю в конструктор классы, с которыми оно там внутри работает.
Ты имеешь в виду, заполнить поля задачи и вернуть ее из Интерактора? А в базу где писать? Если внутри, то надо жестко прописывать класс, которым пишу. А тут передаю его в конструкторе.
В таком подходе работаю впервые, поэтому что-то могу делать неправильно. Интересно попробовать, насколько применимо на практике. В теории все красиво.
3 aktuba 31-05-2014 23:32
DI круто реализован в Phalcon, посмотри - рекомендую. Суть - при старте приложения все сервисы складываются в DI, если какой-то не прописан - используется дефолтный. Далее, в любом месте приложения можно использовать конструкцию $this->di->get('session'), например. Итог - не надо передавать одни и те же сущности от метода к методу.
Это вери-вери бед... Нельзя (НИКОГДА) влезать в данные. Нужна служебная информация - передавай отдельной сущностью/массивом.
4 Александр Купреев 31-05-2014 23:52
DI в Phalcon посмотрю, спасибо.
Согласен. Это тоже мне не нравится, а сделал по принципу "пока сойдет, потом исправлю". Вот так из красоты вырастает говнокод.