Делаю ToDoLog - часть 1

Просмотров: 2988Комментарии: 4
Технологии

В прошлый раз я озадачился двумя вопросами о 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-репозиторий , работающий с оперативной памятью.

Недостатков у кода много, о них пока не буду. Интересно посмотреть, как такой код может взаимодействовать с запросами извне.

Комментариев: 4 RSS

1 aktuba 31-05-2014 22:38

Чет какая-то жесть у тебя... Ты уверен, что пишешь на php, а не на java? У тебя слишком много врапперов ради врапперов.

new Interactor\Task\Creation($this->taskRepo, $this->userRepo, $this->sessionService);

Почему-бы не заюзать Dependency Injection? Для чего постоянно передавать одно и то же? В итоге останется:

$task =  Interactor\Task($data);

2 Александр Купреев 31-05-2014 23:20

Сейчас жесть, а то ли еще будет? smile

Мне казалось, в интеракторе я использую DI - передаю в конструктор классы, с которыми оно там внутри работает.

$task =  Interactor\Task($data);

Ты имеешь в виду, заполнить поля задачи и вернуть ее из Интерактора? А в базу где писать? Если внутри, то надо жестко прописывать класс, которым пишу. А тут передаю его в конструкторе.

В таком подходе работаю впервые, поэтому что-то могу делать неправильно. Интересно попробовать, насколько применимо на практике. В теории все красиво.

3 aktuba 31-05-2014 23:32

DI круто реализован в Phalcon, посмотри - рекомендую. Суть - при старте приложения все сервисы складываются в DI, если какой-то не прописан - используется дефолтный. Далее, в любом месте приложения можно использовать конструкцию $this->di->get('session'), например. Итог - не надо передавать одни и те же сущности от метода к методу.

$data['userId'] = $this->session->getLoggedInUserId();

Это вери-вери бед... Нельзя (НИКОГДА) влезать в данные. Нужна служебная информация - передавай отдельной сущностью/массивом.

4 Александр Купреев 31-05-2014 23:52

DI в Phalcon посмотрю, спасибо.

Это вери-вери бед... Нельзя (НИКОГДА) влезать в данные. Нужна служебная информация - передавай отдельной сущностью/массивом.

Согласен. Это тоже мне не нравится, а сделал по принципу "пока сойдет, потом исправлю". Вот так из красоты вырастает говнокод.

Оставьте комментарий!


Используйте нормальные имена.

     

  

Если вы уже зарегистрированы как комментатор или хотите зарегистрироваться, укажите пароль и свой действующий email. При регистрации на указанный адрес придет письмо с кодом активации и ссылкой на ваш персональный аккаунт, где вы сможете изменить свои данные, включая адрес сайта, ник, описание, контакты и т.д., а также подписку на новые комментарии.

MaxSiteAuth. Войти через loginza

(обязательно)