WedX - журнал о программировании и компьютерных науках

DDD Relate Aggregates в длительном процессе

Я работаю над проектом, в котором мы определяем два агрегата: Project и Task. Проект, в дополнение к другим атрибутам, имеет атрибут точек. Эти баллы распределяются по задачам по мере их определения пользователями. В варианте использования пользователь назначает баллы за какую-то задачу, но в проекте эти баллы должны быть доступны. В настоящее время мы моделируем это следующим образом:

  1. «task.RequestPoints(points)», этот метод создаст агрегированное PointsAssignment с атрибутами points и taskId, которое в своем конструкторе выдает доменное событие PointsAssignmentRequested.
  2. Обработчик выданного события извлечет проект, связанный с задачей, и агрегат PointsAssigment и вызовет метод «project.assignPoints(pointsAssigment, service)», то есть передаст агрегат PointAssignment в качестве параметра и сервис для расчета разница между текущими баллами задачи и желаемыми баллами. Если точки доступны, проект изменит свой атрибут точек и вызовет доменное событие «ProjectPointsAssigned», которое будет содержать атрибут pointsAssignmentId (в дополнение к другим)
  3. Обработчик этого последнего события извлечет PointsAssingment и подтвердит «pointsAssigment.Confirm()», этот агрегат выдаст доменное событие PointsAssigmentConfirmed.
  4. Обработчик этого последнего события вызовет связанную задачу и вызовет «task.AssignPoints (pointsAssignment.points)».

У меня вопрос: правильно ли передать на шаге 2 агрегатное присвоение баллов в методе проекта? Это был единственный способ, которым я нашел возможность связать агрегаты. Примечание. Мы создали агрегат PointsAssignment, чтобы в случае сбоя я мог сохранить ошибку «pointsAssignment.Reject(reasonText)» и отобразить ее пользователю, поскольку я использую итоговую согласованность (1 агрегат на транзакцию).

Мы думаем об использовании диспетчера процессов (PointsAssingmentProcess), но точно так же нам нужен третий совокупный PointsAssingment для корреляции этого процесса.


  • Вам действительно нужна масштабируемость? Я имею в виду, не могли бы вы просто заменить оба AR в одном TX? Вам не всегда нужно сталкиваться с проблемами возможной согласованности и обмена сообщениями, если это приложение среднего размера с одним сервером. 08.10.2020
  • Кроме того, не могли бы вы упростить это до project.assignPoints(taskId, amount)PointsAssignedToTask {taskId, amount}task.assignPoints(event.amount)? Единственное, что мне не нравится, так это то, что неясно, что нельзя просто вызвать task.assignPoints(…) напрямую. Возможно переименовать в task.acknowledgePointsAssigned() или даже просто task.apply(event). 08.10.2020
  • Вы читали мой комментарий? 08.10.2020
  • Я бы посмотрел саги. 12.10.2020
  • @plalx в транзакции, в которой присваиваются баллы, в задаче были изменены другие атрибуты, например, имя, описание и т. д. ... это форма, в которой пользователь изменяет значение баллов, имя, описание и отправляет 20.10.2020

Ответы:


1

Я бы сделал немного по другому (не значит правильнее). Вашему проекту не нужно ничего знать о PointsAssignment.

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

  • Команда RemovePoints -> проект -> удалить точки (точки)
  • AddPointsCommand -> проект-› addPoints(точки)

Затем у вас будет обработчик событий, который будет реагировать на PointsAssignmentRequested (я думаю, у этого парня есть идентификатор проекта и количество баллов и, возможно, поле статуса из того, что вы сказали)

Этот обработчик событий будет делать только:

on(PointsAssignmentRequested) -> команда отправки (RemovePointsCommand)

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

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

  • Ремупоинтсукцеедед
  • RemovePointsFailed // Помните, что у вас есть идентификатор корреляции из ранее сохраненного

Затем у вас будет окончательный обработчик событий, который будет делать:

  1. on(RemovePointsSucceeded) -> PointsAssignment.succeed() // Отправляет PointsAssignmentSucceeded

  2. on(PointsAssignmentSuceded) -> task.AssignPoints (pointsAssignment.points)

На стороне отказа

  1. on(RemovePointsFailed) -> PointsAssignment.fail() // Отправляет PointsAssignmentFailed

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

Я вижу семантику этой проблемы именно как банковский перевод.

У вас есть банковский счет (проект) У вас есть деньги на этом банковском счете (баллы) Вы переводите деньги через процесс перевода (назначение баллов) Вы переводите деньги на счет (задача)

На банковском счете должны быть только минимальные операции по снятию и внесению средств, ему не нужно ничего знать о процессе перевода.

Процесс перевода должен знать, из какого банка он снимается и на какой счет он вносится.

Я представляю себе, что ваша PointsAssignment похожа на

{
  "projectId":"X",
  "taskId":"Y",
  "points" : 10,
  "status" : ["issued", "succeeded", "failed"]
}
08.10.2020
  • но если я вызову project.Remove(points), как PointsAssingment узнает, что точки удалены? мне нужно передать идентификатор, например, project.Remove(points, pointsAssigmentId), поэтому проект будет генерировать событие домена ProjectPointsRemoved { points, pointsAssignmentId } 08.10.2020
  • Новые материалы

    Объяснение документов 02: BERT
    BERT представил двухступенчатую структуру обучения: предварительное обучение и тонкая настройка. Во время предварительного обучения модель обучается на неразмеченных данных с помощью..

    Как проанализировать работу вашего классификатора?
    Не всегда просто знать, какие показатели использовать С развитием глубокого обучения все больше и больше людей учатся обучать свой первый классификатор. Но как только вы закончите..

    Работа с цепями Маркова, часть 4 (Машинное обучение)
    Нелинейные цепи Маркова с агрегатором и их приложения (arXiv) Автор : Бар Лайт Аннотация: Изучаются свойства подкласса случайных процессов, называемых дискретными нелинейными цепями Маркова..

    Crazy Laravel Livewire упростил мне создание электронной коммерции (панель администратора и API) [Часть 3]
    Как вы сегодня, ребята? В этой части мы создадим CRUD для данных о продукте. Думаю, в этой части я не буду слишком много делиться теорией, но чаще буду делиться своим кодом. Потому что..

    Использование машинного обучения и Python для классификации 1000 сезонов новичков MLB Hitter
    Чему может научиться машина, глядя на сезоны новичков 1000 игроков MLB? Это то, что исследует это приложение. В этом процессе мы будем использовать неконтролируемое обучение, чтобы..

    Учебные заметки: создание моего первого пакета Node.js
    Это мои обучающие заметки, когда я научился создавать свой самый первый пакет Node.js, распространяемый через npm. Оглавление Глоссарий I. Новый пакет 1.1 советы по инициализации..

    Забудьте о Matplotlib: улучшите визуализацию данных с помощью умопомрачительных функций Seaborn!
    Примечание. Эта запись в блоге предполагает базовое знакомство с Python и концепциями анализа данных. Привет, энтузиасты данных! Добро пожаловать в мой блог, где я расскажу о невероятных..


    Для любых предложений по сайту: [email protected]