Как назвать метод?

Вурдалак

Продвинутый новичок
— Необходимо авторизовывать юзера по одноразовой протухающей через определенное время ссылке
Я бы сделал TemporaryToken ($id, UserId $userId, DateTimeImmutable $expiresAt) + TemporaryTokenRepository.
User'у насрать на токены, тем более временные, зачем ему это?
 

Redjik

Джедай-мастер
ну преположим...
а рандомизатор куда воткнуть - в фабрику токенов?
 

Вурдалак

Продвинутый новичок
Как-нибудь так, например (domain service):
PHP:
final class TemporaryTokenIssuer
{
    public function __construct(Clock $clock, TokenGenerator $tokenGenerator)
    {
        // ...
    }

    public function issueToken(UserId $userId)
    {
        return TemporaryToken::issue($this->tokenGenerator->generate(), $userId, $this->clock->after('30 minutes'));
    }
}
 

Redjik

Джедай-мастер
угу, ну норм, у меня такой же вариант получился в итоге... остальные отбросились
только я токен как ValueObject спроектировал - поэтому была привязка к юзеру...
А как Entity его ты сделал - сразу все на места встало
 

Sufir

Я не волшебник, я только учусь
Делаешь два интерфейса. User и к примеру EditableUser extends User. С репами то же самое. Для публичного использования отдаешь через публичные методы User. Для служебного оперируешь EditableUser (например поставщик данных). При этом саму модель можно сделать независимой от поставщиков данных. Поставщики данных как драйверы как угодно можно реализовать. Потребители модели об этом знать не будут и не должны. Будет такая схема
Опять же, мне не понятно как EditableUser extends User тут может помоч, Ведь мне нужно будет User пришедший извне подменить на EditableUser для дальнейших манипуляций. Есть другая мысли, если мы убираем из сущности не только все публичные сеттеры, но и геттеры, значит отсаются исключительно методы, вроде $grade->addPupil(), $order->approve(), $user->lock() и т.д. В таком случае и свойства из сущности можно убрать в отдельный объект. Это будет простой DTO со всеми необходимыми свойствами (для Doctrine он будет сущностью, в её терминах). И над ним уже производить все манипуляции, внутри методов сущности. Или опять ерунда?

И ещё, если мы по сеттерам. А вот такая ситуация, есть заказы и т.д. там всё понятно: $order->addProduct($product), $order->approve() и т.д. Конкретный пример. У менеджера/оператора есть возможность добавления и редактирования параметров продукта (title, shortTitle, url, description, features[]). На уровне UI это форма с кучей полей и возможностью добавления неограниченного количества features. Как мне это выразить в коде? Product::createFromArray(array) и $product->update(array, $staffId)? Выглядит как-то не слишком изящно, а создать сущность и изменять ей свойства как-то нужно.
 
Последнее редактирование:

Redjik

Джедай-мастер
Sufir, ну говорю, я вижу вариант решения только через bindTo - это эмуляция дружественных методов или доступа по пакету.
В интернетах есть еще вариант debug_backtrace(1); и проверка обьекта... но у bindTo на порядок меньше оверхед и костыльность.

НО. это на самом деле не нужно ибо, консистетность и синхронизация должна происходить на уровне инфраструктуры (если мы не работаем с AggregateRoot)
Инфраструктура знает все о Домене - и вот тут надо не просрать момент и не забыть, что данные нужно синхронизировать.

Пример. Классика. Автор и книги. Бизнес модель обосновывается бизнес задачами: приложение-библиотека, со страничками автора (биография книги) и со страничками книг (авторы, аннотация).
Под Aggregate Root не подходят... у нас тупо две сущности
PHP:
class Book
{
    //у книги мнимум один автор
    public function __construct(Author $author)
    {
        $this->addAuthors($author);
    }
   
    public function getAuthors()
    {
        return $this->authors;
    }
   
    //можно добавлять
    public function addAuthors(Author $author)
    {
        $this->authors->add($author);
    }
   
    //можно удалять
    public function removeAuthor(Author $author)
    {
        $this->authors->remove($author);
    }
}

class Author
{
    public function getBooks()
    {
        return $this->books;   
    }
}
Причем мы даже не замарачиваемся с авторами и не даем им публичного интерфейса для работы, нам не нужно на уровне домена синхронизировать ничего.
Ибо хоть мы и работаем с доменом, но мы знаем, что будет происходит на уровне приложения, а там будет что-то типо

PHP:
// Одмин решил создать книгу
// Сначала он выбирает автора скажем из выпадающего списка
// Помнит, что автор на V начинается
$authors = $authorRepository->findByStr('V');
// тут ему подгружаются авторы и он уже выбирает одного, отправляет запрос на создание книги, где автор с id = 3
// соответсвенно метод уже в другом месте и $authors выгружены из памяти

$author = $authorRepository->findByInternalId(3);
$book = BookFactory::create($author);
$bookRepository->save($book);

// и вот тут самое интересное происходит
// если у нас реляционная базка, то на уровне инфраструктуры все автоматом синхронизируется
// но если скажем у нас Репо это тупо хранение в памяти
// то на уровне репо нам надо позаботится о том, чтобы у автора эта книга появилась

//короче далее одмин решает зайти на страницу автора с тем же id=3 и увидеть все его книги

$author = $authorRepository->findByInternalId(3);
$author->getBooks();
 

Redjik

Джедай-мастер
из этого вывод:
ой далеко не факт, что так уж важно отделять инфраструктуру...
не - важно конечно, но не при работе с данными, ну поменяем мы mysql на redis скажем,мало того, чтовсю инфраструктуру переписывать - так еще и 90% что с синхронизацей накосячишь
 

Вурдалак

Продвинутый новичок
Ты не допускаешь, что циклическая зависимость Author <-> Book в принципе может быть концептуально неправильным подходом? Ты достаточно смело это считаешь непреложной истиной.
 

AmdY

Пью пиво
Команда форума
Redjik, хрень это всё. Давай рассмотрим задачу в нормальной плоскости.

У нас есть система в которой есть сущности Author. Например, наш форум. И тут админ решает, что нужно бы добавить любимые книги. Появляется сущность Book. И появляется проблема, как связать эти сущности. Патчить Author ты не деложен, а иногда не можешь т.к. система должна быть upgrade safe и на этом Author может быть завязана куча чужой логики. Получается типичный bounded context. Потому тебе надо заводить промежуточный AuthorBooks и AuthorBooksService обрабатывать связи через него. На уровне доктрины это должно рулиться через http://doctrine-orm.readthedocs.org/en/latest/reference/inheritance-mapping.html

У тебя получается стабильная система, со стабильными Author и Books, работами с которыми идёт через одну точку и если надо будет убрать эту точку, то система продолжит работать без надобности менять другие сущности.

Вариант через bindTo хороший, но это monkey patching, который лучше заменить делегированием через отдельный объект, как Doctrine работает со связями или DateTime
 
Последнее редактирование:

Redjik

Джедай-мастер
Ты не допускаешь, что циклическая зависимость Author <-> Book в принципе может быть концептуально неправильным подходом? Ты достаточно смело это считаешь непреложной истиной.
не допускаю, зачем?
ты не допускаешь, что ты тысячекратно усложняешь задачу, чтобы она вписалась в придуманные тобой правила (у Эванса нет такого), а если задачу не удаестя разложить по этим правилам, значит бизнес задача говно и бизнес сам не знает чо хочет - давайте я вам ченить другое придумаю и скину ссылки на кучу воды... time and money как бы краеугольный камень в DDD

Redjik, хрень это всё. Давай рассмотрим задачу в нормальной плоскости.
В какой нормальной плоскости, я описал задачу и описал ее решение, ты описал абсолютно другую задачу и не дал решения как такового...
Я тоже могу так же набросить на вентилятор... а вдруг понадобится не только любимые книги, но и фильмы... давайте тогда еще эмуляцию EAV сделаем на то что юзреу нравится, чтобы мы могли динамичненько сущности добавлять.

Твое решение типичное ManyMany через 3юю таблицу. В домене это все не важно.
 

Sufir

Я не волшебник, я только учусь
PHP:
// тут ему подгружаются авторы и он уже выбирает одного, отправляет запрос на создание книги, где автор с id = 3
// соответсвенно метод уже в другом месте и $authors выгружены из памяти

$author = $authorRepository->findByInternalId(3);
$book = BookFactory::create($author);
$bookRepository->save($book);
C авторами хорошо, конечно, но там ещё "одмин" заполнил ISBN, название, книги, описание и добавил несколько тегов. А через неделю он решил исправить описание, добавить теги и ещё пару параметров изменить (дозаполнить необязательные). Покажи как это будет выглядеть? Всё-таки сеттеры?

PHP:
//короче далее одмин решает зайти на страницу автора с тем же id=3 и увидеть все его книги

$author = $authorRepository->findByInternalId(3);
$author->getBooks();
С "одмином" ясно. Теперь такая ситуация, я взялся выполнить некоторую доработку, на основе разработанной тобой системы. Мне нужно сделать добавление книг автору где-то ещё. Я вижу $author->getBooks(), который возвращает коллекцию книжек и с полным правом $author->getBooks()->add($newBook).
 
Последнее редактирование:

Вурдалак

Продвинутый новичок
у Эванса нет такого
Эванс как раз советует избавляться от двунаправленной связи.

ты не допускаешь, что ты тысячекратно усложняешь задачу
А ты не допускаешь, что неконсистентное состояние модели без вызова ->save() с какой-то магией внутри — это что-то ненормальное?
 

Redjik

Джедай-мастер
C авторами хорошо, конечно, но там ещё "одмин" заполнит ISBN, название, книги, описание и добавил несколько тегов. Покажи как это будет выглядеть.
А там тупо ValueObject что-нить типа BookData, со свойствами - name, description ...
PHP:
$book = BookFactory::create($bookData,$author);
С "одмином" ясно. Теперь такая ситуация, я взялся выполнить некоторую доработку, на основе разработанной тобой системы. Мне нужно сделать добавление книг автору где-то ещё. Я вижу $author->getBooks(), который возвращает коллекцию книжек и с полным правом $author->getBooks()->add($newBook).
PHP:
class Author
{
    .....
    /**
    * @var ArrayCollection
    */
    private $books;
   
    /**
    * @return ReadOnlyArrayCollection
    */
    public function getBooks()
    {
        return $this->books;
    }
}
 

Redjik

Джедай-мастер
Эванс как раз советует избавляться от двунаправленной связи.
Да, но не всегда это возможно и не всегда это нужно по time and money... он сам говорит, не нужно прям мего сильно заморачиваться, идеально все равно не получится
 

Sufir

Я не волшебник, я только учусь
А там тупо ValueObject что-нить типа BookData, со свойствами - name, description ...
PHP:
$book = BookFactory::create($bookData,$author);
PHP:
class Author
{
    .....
    /**
    * @var ArrayCollection
    */
    private $books;

    /**
    * @return ReadOnlyArrayCollection
    */
    public function getBooks()
    {
        return $this->books;
    }
}
Ну, вот это примерно то что я и предполагал. $bookData только скорее DTO чем ValueObject, по моему.

P.S.: Нетбинс, кстати, для автокомплита в таком случае методы обоих интерфейсов выводить будет.
 

Вурдалак

Продвинутый новичок
Да, но не всегда это возможно и не всегда это нужно по time and money... он сам говорит, не нужно прям мего сильно заморачиваться, идеально все равно не получится
Ты вообще не считаешь нужным «заморачиваться», ты не ставишь своё решение под сомнение ни в каком виде.
 

Redjik

Джедай-мастер
Ты вообще не считаешь нужным «заморачиваться», ты не ставишь своё решение под сомнение ни в каком виде.
ну почему же...
я задавал вопросы об общих случаях, как их разруливать - но ты вечно придумываешь какие то хаки и обходные пути или уходишь от ответа
задал вопрос по книги-авторы, но получил ответ - тут нет связи много ко многим... в твоей вселеной НИКОГДА не может быть связи много ко многим в принципе в домене?

я нормально ставлю свои решения под сомнения, консултируюсь спрашиваю ... пытаюсь найти решение
и по мне так вполне оправданное решение получилось в книги-авторы как пример СИНХРОНИЗАЦИИ ... ведь 10 раз именно про синхронизацию спрашивал

в итоге
Синхронизация на уровне инфраструктуры
Обоснование:
А почему бы и нет, раз репозиторий того же сводного корня распихивает обьекты по свойствам, что почему-то считается нормой.
 

Вурдалак

Продвинутый новичок
А почему бы и нет, раз репозиторий того же сводного корня распихивает обьекты по свойствам, что почему-то считается нормой.
Это считается нормой, потому что прозрачно: как если бы объект был добавлен в массив в памяти и, соответственно, извлечён оттуда. Он не меняется. Рефлексия тут позволяет восстановить состояние.

где ты у меня какую либо магию видишь в примере?
Я называю в данном контексте «магией» любую модификацию объекта модели внутри репозитория.

Тут правда нужна оговорка: в реальности есть MySQL, который в отличие от нормальных СУБД не имеет sequence (как в Postgres, например), поэтому id-шник действительно задаётся «магически» внутри, если хочется именно автоинкрементный int.

уходишь от ответа
Потому что я пока не знаю как сделать лучше, я делюсь сомнениями. Вывод книг автора, например, решается элементарно через $bookRepository->findByAuthor().
 
Сверху