Могут ли объекты внутри агрегата быть доступными или видимыми извне агрегату? - PullRequest
0 голосов
/ 20 декабря 2018

Я новичок в DDD, и для многих из вас мой вопрос может показаться тривиальным.

Рассмотрим пример со студентом и курсом.

Студент может записаться на курс, только если студентвозраст выше минимального возраста, необходимого для зачисления на этот курс.

С моей точки зрения, Студент и Курс могут рассматриваться как совокупные, где Студент является корневой сущностью, Курс дочерней сущности, и возраст инварианта для уважения.

Студент должен иметь метод Student.SubscribeTo (курс курса), а метод должен применять инвариант Student.Age >= Course.MinAge и в противном случае генерировать исключение.

Правильно ли это в подходе DDD?Или я должен перейти на SubscribeTo только CourseId?Student.SubscribeTo (int CourseId)

С моей точки зрения, если нет способа нарушить инвариант, доступ к курсу извне к агрегату должен быть разрешен.Если я изменю Course.MinAge в некоторых других местах своего кода, я не нарушу свои бизнес-требования, так как хочу, чтобы возраст учитывался только при подписке на курс, и я не против, если позже Course.MinAge изменится.

Другой случай, если в бизнес-требованиях указано: при изменении Course.MinAge учащиеся, уже записанные на курс, должны быть исключены из курса, если Student.Age < Course.MinAge.

Ответы [ 2 ]

0 голосов
/ 23 декабря 2018

Я также изучаю DDD, и несколько дней назад я задаю вопрос о похожей проблеме, которую вы можете найти здесь .

Я понял, что единственный реальный ответ: это зависит .Нет правильных или неправильных подходов как таковых , все должно служить цели бизнес-проблемы и ее решению.Хотя есть руководящие принципы и практические правила, которые могут быть очень полезны, например:

  • Не моделируйте реальность.Модель предметной области - это абстракция , предназначенная для решения конкретной проблемы.
  • Не моделируйте на основе взаимосвязи данных.Каждая ассоциация должна быть там для обеспечения соблюдения правила или инварианта, а не потому, что эти объекты связаны в реальной жизни.Начните моделирование на основе поведения.
  • Предпочитайте небольшие агрегаты.
  • Предпочитайте изменять один агрегат одновременно (случай использования / транзакция) и используйте конечную согласованность для обновления других агрегатов.
  • Создавайте связи между агрегатами только по идентичности.

Я думаю, что проблема в вашем сценарии заключается в том, что многое отсутствует.Кому принадлежит эта ассоциация и почему?Есть ли другой вариант использования, охватывающий как студента, так и курс?Почему вы ставите student.SubscribeTo(course) вместо course.enroll(student)?Помните, что целью DDD является решение сложной логики предметной области, чтобы она сияла при приближении к стороне записи модели, стороне чтения может быть выполнено многими различными способами без больших затратсвязей в модели.

Для того, что вы сказали, просто проверка возраста, вероятно, не является инвариантом, который требует создания большой совокупности:

Если я изменю Course.MinAge в некоторых других местах моего кода я не нарушаю свои бизнес-требования, так как хочу, чтобы возраст соблюдался только при подписке на курс, и я не против, если позже Course.MinAge изменится.

Тогда нет оснований для принудительного обеспечения постоянства согласованности Student и Course (в данном конкретном контексте / сценарии), и нет необходимости включать их в одну и ту же совокупность.Если единственное правило, которое вам нужно соблюдать, это Student.Age >= Course.MinAge, вы можете придерживаться простого:

Student.SubscribeTo(Course course)

, где Student и Course не являются частью одного и того же агрегата.Нет ничего против загрузки двух разных агрегатов и использует их в одной транзакции, , пока вы изменяете только один .(Что ж, нет ничего против изменения двух агрегатов в одной и той же транзакции, это просто практическое правило, но, вероятно, вам не нужно разбивать его в этом случае).

Здесь Student.SubscribeTo принудительно выполнитправило относительно возраста.Я должен сказать, что это звучит «странно», чтобы позволить Student подтвердить свой собственный возраст, но, возможно, это как раз в вашей модели (помните, не моделируйте реальность, моделируйте решения).В результате у Student будет новое состояние, содержащее идентификатор курса, а Course останется неизменным.

Другой случай, если требования бизнеса указывают: когда Course.MinAge меняет уже зачисленных студентовкурс должен быть удален из курса, если Student.Age < Course.MinAge.

Здесь вы должны сначала ответить (с помощью эксперта по домену) еще на несколько вопросов: Почему они должны быть удалены?Должны ли они быть удалены немедленно?Что если они посещают занятия в этот момент?Что означает удаление студента?

Скорее всего, в домене нет реальной необходимости удалять учеников одновременно с изменением MinAge (например, когдаоперация считается успешной только тогда, когда все происходит, а если нет, то ничего не происходит).Студенты, возможно, должны войти в новое состояние, которое может быть решено в конечном счете.Если это так, то вам также не нужно включать их в одну совокупность.

Отвечая на вопрос в названии, неудивительно: это зависит .Вся причина иметь агрегат состоит в том, чтобы защитить инварианты каким-то образом связанного набора сущностей.Совокупность не является HAS-A отношением (необязательно).Если вам нужно защитить инвариант, охватывающий несколько сущностей, вы можете сделать их совокупными и выбрать сущность в качестве совокупного корня;этот корень является единственной точкой доступа для изменения агрегата, поэтому каждый инвариант всегда применяется.Разрешение прямой ссылки на сущность внутри агрегата нарушает эту защиту: теперь вы можете изменять эту сущность без ведома корня.Поскольку сущности внутри агрегата не доступны извне, эти сущности имеют только локальную идентичность и не имеют значения как автономные объекты без ссылки на корень.Тем не менее, можно запросить у корня сущности внутри агрегата.

Вы можете , иногда передавать ссылку на внутреннюю сущность в другой агрегат, если это временная ссылкаи никто не изменяет эту ссылку вне совокупного корня.Это, однако, запутывает модель и границы становятся размытыми.Лучшим подходом является передача копии этой сущности или, что лучше, передача неизменного представления этой сущности (вероятно, объекта значения), чтобы не было возможности нарушить инварианты.Если вы думаете, что нет инварианта, который нужно разбить, передавая ссылку на внутреннюю сущность, то, возможно, нет никакой причины иметь агрегат для начала.

0 голосов
/ 21 декабря 2018

Я думаю, что ваша совокупная сумма неверна.Сущность Course может существовать сама по себе, она не является дочерней сущностью объекта Student.У курса есть свой жизненный стиль: например, если ученик покидает школу, курс продолжает существовать.Идентификатор курса не зависит от идентификатора студента.Студент может хранить идентификатор курса, но это разные агрегаты.

В любом случае, на ваш вопрос о передаче только идентификатора курса методу "student.subscribeTo", если они были агрегатными, ответ - нет,Вы не можете передать идентификатор дочерней сущности в операцию агрегата, поскольку дочерние сущности не имеют глобальной идентичности, известной за пределами агрегата.У них есть локальный идентификатор в совокупности.

ОБНОВЛЕНИЕ:

Поскольку курс и ученик - это две совокупности, правило "возраст учащегося должен быть выше минимального возраста, необходимого для зачислениякурс "не является инвариантом.Зачем?Поскольку инвариант - это бизнес-правило о состоянии всего агрегата, оно всегда должно быть транзакционно согласованным.Агрегат определяет границу согласованности транзакций.

Таким образом, это правило является правилом проверки, которое необходимо проверять, когда студент подписывается на курс (метод student.subscribeTo).Поскольку агрегаты не должны использовать репозитории, вы можете передать доменную службу этому методу, а агрегат студента должен выполнить двойную диспетчеризацию в доменную службу, чтобы получить курс по идентификатору курса.

Посмотритев главе о совокупности Красной книги IDDD Вона Вернона (стр. 361-363) или в статье того же автора:

http://www.informit.com/articles/article.aspx?p=2020371&seqNum=4

Надеюсь, что это поможет.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...