Репозитории и связи

Обсуждаем, как правильно строить приложения
Ответить
Аватара пользователя
BrusSENS
Сообщения: 565
Зарегистрирован: 2012.07.26, 06:51
Откуда: Новороссийск
Контактная информация:

Репозитории и связи

Сообщение BrusSENS »

Всем доброго времени!
Имеется ряд вопросов по проектированию.
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) Паук. Что скажете насчёт такой реализации связей? Стоит ли выносить подобный пул в самостоятельный компонент (читай объект), который можно вызывать извне? (Создав пул для пулов :D)

2. Если с eager выборкой таких связей вроде всё хорошо при таком подходе, то что делать с Lazy? Пришли опять-таки в голову мысли о некой реализации ленивого загрузчика для AttributesValueObject, который сможет при запросе атрибутов лениво их подгружать, используя всё тот же адаптер. Получается такой объект будет знать только об интерфейсе адаптера.
Как я понимаю, по сути у нас интерфейсы адаптеров, сущности, VO и его декоратор находятся в доменном слое? Или я что то не так понимаю?

P.S.: вопрос в принципе касается не DDD, а проектирования в целом. Где, ИМХО слои в разумном виде будут давать только положительный результат, посему хочу знать по ответственности таких слоёв в данном случае.
Native Web - небольшой блог о веб разработке (временно на ремонте)
Режим обслуживания сайта для Yii 2.x.x
Ответить