Применение CQRS для управления запасами - PullRequest
2 голосов
/ 25 марта 2012

Я все еще пытаюсь понять, как применять DDD и, совсем недавно, CQRS в реальном производственном бизнес-приложении.В моем случае я работаю над системой управления запасами.Он работает как серверное приложение, предоставляемое через REST API нескольким клиентским приложениям.Я сфокусировался на уровне домена, которому должны следовать API и клиенты.

Командная сторона домена используется для создания нового Заказа и позволяет изменять, отменять, помечать Заказ как выполненный и отправленный / завершенный.,У меня, конечно, есть запрос, который возвращает список заказов в системе (в виде облегченных DTO только для чтения) из хранилища.Другой запрос возвращает PickList, используемый сотрудниками склада для выгрузки товаров с полок для выполнения определенных заказов.Для создания PickList существуют вычисления, правила и т. Д., Которые необходимо оценить, чтобы определить, какие заказы готовы к выполнению.Например, если все позиции заказа есть в наличии.Мне нужно прочитать тот же список заказов, выполнить итерацию по списку и применить эти правила и расчеты, чтобы определить, какие элементы должны быть включены в список выбора.

Это не простой запрос, поэтому как он вписывается вмодель?

ОБНОВЛЕНИЕ

Хотя я могу поддерживать (хранить) набор списков выбора, они действительно являются динамическими, пока сотрудник не получит следующий список выбора.Рассмотрим следующий сценарий:

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

Получен второй заказ.Обработчик событий теперь должен ЗАМЕНИТЬ исходные списки выбора с одним или несколькими новыми списками выбора, оптимизированными для обоих ожидающих заказов.

Аналогично после получения третьего Заказа.

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

Сотрудник склада # 1 запрашиваетPickList.Первый PickList извлекается и печатается.

Получен четвертый Заказ.Как и прежде, обработчик удаляет второй PickList из очереди (единственный оставшийся) и восстанавливает один или несколько PickList на основе второго PickList и нового Order.

Ассемблер PickList ''будет повторять эту логику всякий раз, когда будет получен новый ордер.

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

Ответы [ 3 ]

1 голос
/ 19 февраля 2016

Хотя я могу поддерживать (хранить) набор списков выбора, они действительно являются динамическими, пока сотрудник не получит следующий список выбора.Рассмотрим следующий сценарий:

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

Получен второй заказ.Обработчик событий теперь должен ЗАМЕНИТЬ исходные списки выбора с одним или несколькими новыми списками выбора, оптимизированными для обоих ожидающих заказов.

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

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

То есть я ожидал бы поток, который выглядит как

onOrderPlaced(List<OrderItems> items)
    warehouse.reserveItems(List<OrderItems> items)
        // At this point, the items are copied into an unasssigned
        // items collection.  In other words, the aggregate knows
        // that the items have been ordered, and are not currently
        // assigned to any picklist
        fire(ItemsReserved(items))

onPickListRequested(Id<Employee> employee)
    warehouse.assignPickList(Id<Employee> employee, PickListOptimizier optimizer)
         // PickListOptimizer is your calculation, rules, etc that know how
         // to choose the right items to put into the next pick list from a
         // a given collection of unassigned items.  This is a stateless domain
         // *domain service* -- it provides the query that the warehouse aggregate needs
         // to figure out the right change to make, but it *doesn't* change
         // the state of the aggregate -- that's the aggregate's responsibility

        List<OrderItems> pickedItems = optimizer.chooseItems(this.unassignedItems);
        this.unassignedItems.removeAll(pickedItems);

        // This mockup assumes we can consider PickLists to be entities
        // within the warehouse aggregate.  You'd need some additional
        // events if you wanted the PickList to have its own aggregate
        Id<PickList> = PickList.createId(...);
        this.pickLists.put(id, new PickList(id, employee, pickedItems))
        fire(PickListAssigned(id, employee, pickedItems);

onPickListCompleted(Id<PickList> pickList)
    warehouse.closePicklist(Id<PickList> pickList)
        this.pickLists.remove(pickList)
        fire(PickListClosed(pickList)

onPickListAbandoned(Id<PickList> pickList)
    warehouse.reassign(Id<PickList> pickList)
        PickList list = this.pickLists.remove(pickList)
        this.unassignedItems.addAll(list.pickedItems)
        fire(ItemsReassigned(list.pickedItems)

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

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

0 голосов
/ 04 июля 2013

Похоже, у вас есть две роли / процессы и, возможно, также два совокупных корня - продавец работает с заказами, работник склада с списками выбора.

AssemblePicklistsCommand() запускается из обработки заказа и воссоздает все неназначенные списки выбора.

Работник склада запускает AssignPicklistCommand(userid), который пытается выбрать наиболее подходящий неназначенный список выбора и назначить его ему (или ничего не делать, если у него уже есть активный список выбора).Затем он может использовать GetActivePicklistQuery(userid) для получения списка выбора, выбирать элементы с помощью PickPicklistItemCommand(picklistid, item, quantity) и, наконец, MarkPicklistCompleteCommand(), чтобы сигнализировать о порядке выполнения.

AssemblePicklist и AssignPicklist должны блокировать друг друга (последовательная обработка, оптимистичная конкуренция?)но связь между AssignPicklist и GetActivePicklist является чистой - либо вам назначен список выбора, либо нет.

0 голосов
/ 26 марта 2012

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

...