1. Adelf

    Adelf Laravel&PhpStorm Команда форума

    Сообщения:
    3.347
    Ваш город:
    Казань
    Address:
    Kazan, Russia
    Country:
    Location on Map:
    Хотел тут набросать пример простого домена и наткнулся на дилемму.
    Обычная фриланс биржа. Термины брал из upwork. Кастомером постится Job и туда апплаятся фрилансеры. каждый со своей hourRate.
    Поскольку хочется этим примером показать и Information hiding, не стал делать Freelancer::getHourRate(), а создание обьекта Proposal(Freelancer, HourRate, CoverLetter) возложил на фрилансера.
    И тут вопрос. Для меня нормальный вариант, это когда Service(Application) Layer достает нужный агрегат рут, еще немного данных и делает один вызов метода агрегат рута(может я и не прав). попросить фрилансера создать обьект Proposal а потом добавить его в Job - это доменная логика в недоменном слое.

    Остаются два варианта:

    PHP:
    final class Job
    {
        public function 
    newProposal(Freelancer $freelancerstring $coverLetterMessage)
        {
            
    $this->proposals[] = $freelancer->makeProposal($coverLetterMessage);
        }
    }
    ИЛИ

    PHP:
    final class Freelancer
    {
        public function 
    apply(Job $jobstring $coverLetterMessage)
        {
            
    $job->newProposal(new Proposal($this$this->hourRate$coverLetterMessage));
        }
    }
    В первом проблема такая, что Job знает все, что надо фрилансеру для создания Proposal. coverLetter например, ненужное для него знание.
    Во-втором все ок, но в дальнейших операциях агрегат рутом всегда будет Job. И это напрягает.

    Полный пример двух вариантов - https://gist.github.com/adelf/a3c8a88879c1a200122ca1dd5030ba9d
     
  2. Adelf

    Adelf Laravel&PhpStorm Команда форума

    Сообщения:
    3.347
    Ваш город:
    Казань
    Address:
    Kazan, Russia
    Country:
    Location on Map:
    Вспомнилась тут модель акторов.
    Актор Freelancer создает Proposal и кидает сообщение. Актор Job ловит это сообщение и делает что нужно.
     
  3. WMix

    WMix герр M:)ller Партнер клуба

    Сообщения:
    6.333
    Ваш город:
    Berlin
    Address:
    Berlin, Germany
    Country:
    Location on Map:
    По мне обе модели странные, (ближе вторая) я вижу это так: есть работа, есть предложения, которые создаются разработчиками. у разработчика есть несколько предложений на разные работы. Отсюда предложение имеет ссылку на разработчика и на работу. Агрегатом может быть как работа так и разработчик или предложениe, в зависимости от контекста.

    Работодатель создает джоб, работник делает предложение для конкретной работы, работодатель принимает/отклоняет предложения.

    PHP:
    class Job{
        private 
    $employer;
    }

    class 
    Proposal{
        private 
    $job;
        private 
    $freelancer;
    }

    class 
    Employer{
        public function 
    makeJob( ...$blabla ){
            return new 
    Job($this$blabla);
        }
        public function 
    acceptProposal(Proposal $proposal){...}
        public function 
    declineProposal(Proposal $proposal){...}
    }

    class 
    Freelancer{
        public function 
    makeProposal(Job $jobstring $coverLetterMessage){
            return new 
    Proposal($this$job$coverLetterMessage);
        }
    }
     
  4. Вурдалак

    Вурдалак I'd like to model your domain

    Сообщения:
    6.173
    Ваш город:
    Russia, Moscow
    Address:
    Moscow, Russia
    Country:
    Location on Map:
    Не стоит передавать ссылки на aggregate root'ы.
    Это и технически будет мешать (AR должен быть консистентен в любой момент времени; есть другой AR становится его частью, то придётся делать что-то типа SELECT ... FOR UPDATE для двух сущностей; следовательно, одна база, отсюда куча ограничений).
    Также это будет мешать моделям: сущности «Freelancer» и «Job» вполне могут быть в разных BC, в разных микросервисах. Допускаю, что может быть полезно иметь read model Freelancer или value object в BC с Job, но сама сущность (особенно для проекта уровня upwork) — вряд ли. Нужно передавать просто идентификаторы.

    Proposal можно сделать AR/VO, it depends (или тем, и другим, если в разных BC).

    Попробуй пользоваться командами/событиями.

    А на upwork разве нельзя указывать различный hour rate для разных предложений работы? Я не помню уже, но мне кажется, что hour rate желательно подставлять как дефолтное значение в UI, где можно предложить услуги, где это всегда можно поправить для конкретной работы. Тут как раз пример того, что Freelancer и Job у тебя как-то жёстко связаны.

    Я понимаю, что может быть передавать ссылки на объекты — это красиво и выглядит, так, как было бы в идеальном мире ООП с машинами, у которых бесконечные ресурсы. Но реальность такова, то AR не должен содержать прямые ссылки на внешние объекты, только id. А в event sourcing такой подход в принципе не будет работать.

    Написать new ClassName — это даже если и бизнес-логика, то сверхпримитивная. Не нужно мучить себя такой ерундой. Как я уже сказал, фрилансер может быть вообще в другом BC, а в этот просто прилетают команды на добавление Proposal'ов.
     
    Последнее редактирование: 8 янв 2019
    Adelf нравится это.
  5. Adelf

    Adelf Laravel&PhpStorm Команда форума

    Сообщения:
    3.347
    Ваш город:
    Казань
    Address:
    Kazan, Russia
    Country:
    Location on Map:
    Ага. Примерно как я и сказал.
    Да, простое предположение что фрилансер к этому BC имеет крайне малое отношение сразу все раскидывает по полочкам. Спасиб.