Имеется ряд вопросов по проектированию.
1. При работе с хранилищем, вроде реляционной БД репозитории получаются монстрами при наличии связей. С одной стороны понятно, что репозиторий должен доставать агрегат полностью, удобно. С другой, eager loading превращается в попахивающий кусок кода в методах репозитория. Проксирование для lazy loading тоже не особо радует. Прихожу к адаптерам на каждую связь, реализующие некий интерфейс RelationAdapter. Сами адаптеры можно, в принципе, хранить в неком пулле. Получается что-то вроде
Код: Выделить всё
interface RelationAdapter
{
public function getRelationName():string;
public function relate();
}
interface AttributeRelation
{
public function getAttributesByUserId(int $id): array;
}
class UserAttributesRelation implements RelationAdapter
{
private $repository;
public function __construct(AttributeRelation $repository)
{
$this->repository = $repository;
}
public function getRelationName(): string
{
return 'attributes';
}
public function relate($params)
{
$this->repository->getAttributesByUserId($params);
}
}
class AttributeRepository implements AttributeRelation {
public function getAttributesByUserId(int $id): array
{
// Выбираем и конвертируем в сущности
return $attributes;
}
}
class UserRepository
{
private $relationPool;
public function __construct(RelationPool $relationPool)
{
$this->relationPool = $relationPool;
}
public function get(array $with = [])
{
if(empty($row = $query->all())) {
throw new UserNotFoundException('User not found');
}
$this->applyWith($with, $row);
}
protected function applyWith($with = [], &$row)
{
foreach($adapters = $this->relationPool->getActiveRelations($with) as $adapter)
{
$row[$adapter->getRelationName()] = $adapter->relate($row['id']);
}
}
}
class RelationPool
{
private $adapters = [];
public function __construct(array $adapters = [])
{
foreach($adapters as $adapter) {
$this->add($adapter);
}
}
public function add(RelationAdapter $adapter)
{
$this->adapters[$adapter->getRelationName()] = $adapter;
}
public function getActiveRelations(array $with)
{
// отсеиваем незапрошенные связи
return $adapters;
}
}
Кто-то скажет, что использование аргумента $with в методах репозиториев может не понадобиться для репозиториев других хранилищ, но тут, как мне кажется ничего страшного нет, т.к. мы можем использовать репозиторий пользователя работающий с MySQL, а репозиторий атрибутов работающий с... XML файлами, например (c) Паук. Что скажете насчёт такой реализации связей? Стоит ли выносить подобный пул в самостоятельный компонент (читай объект), который можно вызывать извне? (Создав пул для пулов )
2. Если с eager выборкой таких связей вроде всё хорошо при таком подходе, то что делать с Lazy? Пришли опять-таки в голову мысли о некой реализации ленивого загрузчика для AttributesValueObject, который сможет при запросе атрибутов лениво их подгружать, используя всё тот же адаптер. Получается такой объект будет знать только об интерфейсе адаптера.
Как я понимаю, по сути у нас интерфейсы адаптеров, сущности, VO и его декоратор находятся в доменном слое? Или я что то не так понимаю?
P.S.: вопрос в принципе касается не DDD, а проектирования в целом. Где, ИМХО слои в разумном виде будут давать только положительный результат, посему хочу знать по ответственности таких слоёв в данном случае.