Как назначить запрос разным людям в зависимости от типа запроса и инициатора в DDD - PullRequest
0 голосов
/ 10 мая 2018

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

Проект представляет собой систему запросов, в которой каждый запрос имеет несколько типов запросов.Когда запрос отправлен, он будет в состоянии AwaitingApproval и будет последовательно направляться разным людям в соответствии с набором правил, приведенным ниже: -

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

2) Если инициатором запроса является менеджер уровня 1, ему потребуются утверждения от уровня 2, уровняМенеджеры 3 и 4 уровня

3) Если инициатором является менеджер 2 уровня, запрос будет 2), но без необходимости утверждения 2 уровня по очевидным причинам

4) Если запроссодержит тип запроса, который увеличивает денежную стоимость, скажем,> $ 500, для этого потребуется одобрение менеджера 4-го уровня

Запрос на любом из этапов может быть Approved , Отклонено или Отклонено с изменениями . Одобрить это займет следующий уровень в цепочке одобрения. Отклонить полностью завершает процесс.

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

Первоначально,Я считал, что у нас есть совокупный маршрут запроса с RequestStatus с использованием шаблона состояния.

Поэтому у меня будет что-то вроде

class Request{
    _currentstate = new AwaitingApprovalState();
    void AssignTo(string person){
        _assignee = person;
    }
    void Approve(){
      _currentstate = _currentstate.Approve();
    }
}

class AwaitingApprovalState : IState{
    void Approve(){
       return new ApprovedState();
    }
}
class ApprovedState : IState{
    void Approve(){
         return new Level2ManagerApprovedState();
    }
}

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

Некоторые вопросы, которые возникают

1) Где ответственность за определение того, кто следующий менеджер в цепочке должен назначить запрос?Это относится к реализациям класса состояний или где-то еще, как в самом Запросе?2) В настоящее время новый запрос находится в AwaitingApprovalState , и если я его одобряю, он переходит прямо к ApprovedState .Куда идет логика, определяющая, что, поскольку мне не требуются промежуточные разрешения, он должен идти прямо в отдел обработки?3) Если есть отклонение с изменениями, как мы можем вернуться к предыдущим уровням - я рассмотрел какую-то сущность StatusHistory.

Возможно, я подумал, что это какой-то компонент рабочего процесса, но хочу этого избежатькак можно больше.

Любые указатели или идеи будут очень признательны

Ответы [ 3 ]

0 голосов
/ 11 мая 2018

1) Где ответственность за определение того, кто следующий менеджер в цепочке должен назначить запрос?Это относится к реализациям класса состояний или где-то еще, как в самом Запросе?

Это зависит.Это может быть в Request само по себе, это может быть в доменной службе.

Кроме того, я бы порекомендовал, если это возможно, не определять точно, кто является следующим валидатором, когда Запрос переходит в свое следующее состояние, но позже.Отправка уведомления и отображение запроса проверки на информационной панели являются следствием изменений состояния домена, но не самих изменений состояния - они не должны происходить атомарно при операции на Request, но могут произойти позже.

Если вам удастся отделить бит, который ищет данные валидатора для последующего запроса, от логики, определяющей, кто следующий тип валидаторов (менеджер уровня 1, менеджер уровня 2 и т. Д.), Вы, вероятно,Избавьтесь от сложного моделирования агрегата Request.

2) В настоящее время новый запрос находится в AwaitingApprovalState, и если я его одобряю, он переходит прямо к ApprovedState.Куда идет логика, определяющая, что из-за того, что мне не требуются какие-либо промежуточные утверждения, он должен идти прямо в отдел обработки?

То же, что 1)

3)Если есть отклонение с модификациями, как мы можем вернуться к предыдущим уровням - я рассмотрел какую-то сущность StatusHistory.

Вы можете определить, кто был предыдущей группой проверки, используя тот же видлогики как для определения следующей группы.Или вы можете хранить историю прошлых состояний в качестве частного члена Request вместе с _currentState

0 голосов
/ 13 мая 2018

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

Request #12354 submitted.
Request #12354 received level 2 approval: details....
Request #12354 received level 3 approval: details....

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

Где лежит ответственность за определение того, кто следующий менеджер в цепочке должен назначить запрос?

Нечто подобное может быть реализовано в доменной службе;если агрегат не содержит информации, которая ему необходима для работы, он должен попросить кого-то еще.

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

Manager levelFourManager = managers.getLevelFourManager(...)

Куда идет логика, которая определяет это, потому что я не знаютребовать каких-либо промежуточных разрешений, которые должны поступать прямо в отдел обработки?

Возможно, в конечном итоге в сам агрегат.

Ринат Абдуллин составил очень хорошее руководство по процессу развитияменеджеры , что очень соответствует разговору Грега Янга Stop Over Engineering .

У вас есть запрос в вашей модели, например

request.isReadyForProcessing()

В ранних версиях вашей модели запрос мог отвечать false, пока какой-нибудь оператор не сказал ему «да, вы готовы»;затем со временем вы начнете добавлять в простые случаи для вычисления.

boolean isReadyForProcessing() {
    return aHumanSaidImReadyForProcessing() || ImOneOfTheEasyCasesToProcess();
}

Что на самом деле означает «отправить в обработку», вероятно, не в совокупности.Мы могли бы снова позаимствовать идею доменного сервиса, на этот раз для связи с внешней системой

void notify(ProcessingClient client) {
    if (this.isReadyForProcessing()) {
        client.process(this.id);
    }
}

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

Частью модели домена *, как образца, является то, что наш домен требует координации / оркестрации сообщений между объектами в модели.Если бы нам не нужна была такая сложность, мы бы, вероятно, посмотрели на что-то более прямолинейное, например сценарии транзакций .Печатная версия Patterns of Enterprise Application Architecture посвящает их описанию на нескольких страницах.

Если есть отклонения с изменениями, как нам вернуться к предыдущим уровням - Iрассмотрели какую-то сущность StatusHistory.

Да, это - RejectWithModifications - это просто еще одно сообщение для записи в книгу, и это дает вам больше информации для рассмотрения при ответах на вопросы.

Request #12354 submitted.
Request #12354 received level 2 approval: details....
Request #12354 received level 3 approval: details....
Request #12354 rejected with modifications: details....

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

  1. Это не ваша вина.
  2. Литература слабая.

позволяет ли журнал событий называть его ActivityLog live для агрегата Request или его собственный агрегат, как в образцах Cargo DDD?

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

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

Возможно;это также запутано, потому что доменные события не обязательно обязательно , о которых люди говорят, когда говорят «источник событий».

Нужно видеть древесину для деревьев.

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

0 голосов
/ 11 мая 2018

для объяснения этого давайте предположим, что существуют типы запросов:

  1. Покупка (требуется одобрение менеджера, например: уровень 2 требует одобрения менеджера уровня 3 и выше)
  2. BusinessMeet (одобрение не требуется)

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

Теперь давайте посмотрим на текущую структуру, как бы мы определили ее в DDD:

Агрегат PurchaseRequest расширяет RequestAgg

  • requesttid
  • запрашивается
  • информация о покупке - описание покупки
  • запрашивается уровнем менеджера
  • ожидает рассмотрения в списках менеджеров - список менеджерас уровнем
  • , одобренным списками менеджеров - список менеджера с уровнем
  • следующий менеджер для утверждения - менеджер с уровнем
  • статус {утвержден, ожидает рассмотрения *

BusinessMeetRequest Aggregate расширяет RequestAgg

  • requesttid
  • , запрошенный
  • статус {утвержден, ожидает рассмотрения} - по умолчанию он должен быть утвержден

ApprovalRequestAgg

  • requesttid
  • идентификатор менеджера
  • тип запроса
  • состояние - (Утверждено, Отклонено)

При запросе пользователя он либонажмите api с запросом на покупку или BusinessMeetRequest

. В этом случае допустим, что пользователь нажал на запрос на покупку, после чего будет создан PurchaseRequestAgg.

На основе события PurchaseRequestCreated один ProcessManager прослушает событие и создастnew agg ApprovalRequestAgg с идентификатором менеджера.

Менеджер сможет увидеть запрос, который ему нужно одобрить из модели чтения ApprovalRequest.и чтобы увидеть информацию о запросе, поскольку ApprovalRequest имеет идентификатор запроса и тип запроса, он сможет получить фактический запрос на покупку, после чего он может либо одобрить, либо отклонить и отправить событие ApprovalRequestRejected или ApprovalRequestApproved.

BasedВ вышеупомянутом событии будет обновлено PurchaseRequestAgg.и PurchaseRequest Agg выдаст событие (скажем, после утверждения) PurchaseRequestAcceptedByManager.

Теперь кто-то будет слушать, и вышеуказанный цикл сработает.

** В приведенном выше решении единственной проблемой является добавление нового типазапроса займет время **

Другим способом может быть наличие одного RequestAgg.для запроса

RequestAgg - идентификатор запроса - тип - информация - статус

, а алгоритм предоставления обновлений менеджеру написан в ProcessManager.

Думаю, это поможет вам,если все еще есть сомнения, пинг снова:)

...