Мне кажется, что ваша глобальная конфигурация является либо спецификацией , либо набором правил, как в движке правил .
В отличие от шаблонов, описанных в GOF book , в DDD некоторые строительные блоки / шаблоны являются более общими и могут применяться к различным типам объектов, которые у вас есть.
Например, Сущность - это то, что имеет жизненный цикл и имеет идентичность. Этапы жизненного цикла обычно следующие: создаются, сохраняются, восстанавливаются из хранилища, модифицируются, а затем жизненный цикл заканчивается удалением, архивированием, завершением и т. Д.
A Объект значения - это то, что не имеет идентичности, (в большинстве случаев) является неизменным, два экземпляра можно сравнить по равенству их свойств. Объект стоимости представляет важные понятия в наших доменах, такие как: Деньги в системе, которая занимается бухгалтерским учетом, банковским делом и т. Д., Vector3 и Matrix3 в системах, которые выполняют математические вычисления и симуляции, таких как системы моделирования (3dsMax, Maya), видеоигры и т. Д. Они содержат важное поведение.
Таким образом, все, что вам нужно отследить и идентифицировать, может быть Entity .
Вы можете иметь Спецификацию , которая является сущностью, Правило , которая является сущностью, Событие также может быть объектом, если ему присвоен уникальный идентификатор. В этом случае вы можете относиться к ним так же, как к любому другому объекту. Вы можете создавать агрегаты, иметь репозитории и сервисы и использовать EventSourcing при необходимости.
С другой стороны a Спецификация , a Правило , Событие или Команда также может быть Значения объектов .
Технические характеристики и Правила также могут быть Доменные службы .
Одной из важных вещей здесь является также Ограниченный контекст . Система, которая обновляет эти правила, вероятно, находится в другом ограниченном контексте , чем система, которая применяет эти правила. Также возможно, что это не так.
Вот пример.
Давайте создадим систему, в которой Клиент сможет покупать вещи. Эта система также будет иметь Скидки на Заказы , которые имеют определенные Правила .
Допустим, у нас есть правило, которое гласит: если Клиент сделал Заказ с более чем 5 LineItems он получает скидку. Если у этого ордера общая стоимость (скажем, 1000 $), он получает скидку.
Процент скидок может быть изменен Отделом продаж . Система продаж содержит OrderDicountPolicy агрегатов, которые она может изменять. С другой стороны, Система заказов только читает OrderDicountPolicy агрегатов и не сможет их изменять, так как это является обязанностью Отдел продаж .
Система продаж и Система заказов могут быть частью двух отдельных Ограниченных контекстов : Продажа и Заказы . Ограниченный контекст заказов зависит от Ограниченный контекст продаж .
Примечание. Я пропущу большинство деталей реализации и добавлю только релевантные элементы, чтобы сократить и упростить этот пример. Если его намерение неясно, я отредактирую и добавлю дополнительные сведения. UUID , DiscountPercentage и Деньги являются ценными объектами, которые я пропущу.
public interface OrderDiscountPolicy {
public UUID getID();
public DiscountPercentage getDiscountPercentage();
public void changeDiscountPercentage(DiscountPercentage percentage);
public bool canApplyDiscount(Order order);
}
public class LineItemsCountOrderDiscountPolicy implements OrderDiscountPolicy {
public int getLineItemsCount() { }
public void changeLineItemsCount(int count) { }
public bool canApplyDiscount(Order order) {
return order.getLineItemsCount() > this.getLineItemsCount();
}
// other stuff from interface implementation
}
public class PriceThresholdOrderDiscountPolicy implements OrderDiscountPolicy {
public Money getPriceThreshold() { }
public void changePriceThreshold(Money threshold) { }
public bool canApplyDiscount(Order order) {
return order.getTotalPriceWithoutDiscount() > this.getPriceThreshold();
}
// other stuff from interface implementation
}
public class LineItem {
public UUID getOrderID() { }
public UUID getProductID() { }
public Quantity getQuantity { }
public Money getProductPrice() { }
public Money getTotalPrice() {
return getProductPrice().multiply(getQuantity());
}
}
public enum OrderStatus { Pending, Placed, Approced, Rejected, Shipped, Finalized }
public class Order {
private UUID mID;
private OrderStatus mStatus;
private List<LineItem> mLineItems;
private DscountPercentage mDiscountPercentage;
public UUID getID() { }
public OrderStatus getStatus() { }
public DscountPercentage getDiscountPercentage() { };
public Money getTotalPriceWithoutDiscount() {
// return sum of all line items
}
public Money getTotalPrice() {
// return sum of all line items + discount percentage
}
public void changeStatus(OrderStatus newStatus) { }
public List<LineItem> getLineItems() {
return Collections.unmodifiableList(mLineItems);
}
public LineItem addLineItem(UUID productID, Quantity quantity, Money price) {
LineItem item = new LineItem(this.getID(), productID, quantity, price);
mLineItems.add(item);
return item;
}
public void applyDiscount(DiscountPercentage discountPercentage) {
mDiscountPercentage = discountPercentage;
}
}
public class PlaceOrderCommandHandler {
public void handle(PlaceOrderCommand cmd) {
Order order = mOrderRepository.getByID(cmd.getOrderID());
List<OrderDiscountPolicy> discountPolicies =
mOrderDiscountPolicyRepository.getAll();
for (OrderDiscountPolicy policy : discountPolicies) {
if (policy.canApplyDiscount(order)) {
order.applyDiscount(policy.getDiscountPercentage());
}
}
order.changeStatus(OrderStatus.Placed);
mOrderRepository.save(order);
}
}
public class ChangeOrderDiscountPolicyPercentageHandler {
public void handle(ChangeOrderDiscountPolicyPercentage cmd) {
OrderDiscountPolicy policy =
mOrderDiscountRepository.getByID(cmd.getPolicyID());
policy.changePercentage(cmd.getDiscountPercentage());
mOrderDiscountRepository.save(policy);
}
}
Вы можете использовать EventSourcing , если считаете, что он подходит для некоторых агрегатов. Книга DDD содержит главу о глобальных правилах и спецификациях .
Давайте посмотрим, что мы будем делать в случае распределенного приложения, например, с использованием микросервисов.
Допустим, у нас есть 2 службы: Служба Orders и OrdersDiscountService .
Есть несколько способов реализовать эту операцию. Мы можем использовать:
- Хореография с событиями
- Оркестровка с явным Saga или Process Manager
Вот как мы можем это сделать, если мы используем хореографию с событиями.
CreateOrderCommand -> OrdersService -> OrderCreatedEvent
OrderCreatedEvent -> OrdersDiscountService -> OrderDiscountAvailableEvent или OrderDiscountNotAvailableEvent
OrderDiscountAvailableEvent или OrderDiscountNotAvailableEvent -> Служба заказов -> OrderPlacedEvent
В этом примере для размещения заказа OrdersService будет ожидать OrderDiscountNotAvailableEvent или OrderDiscountNotAvailableEvent , поэтому он может применить скидку перед изменением статуса заказа на OrderPlaced .
Мы также можем использовать явную Saga для выполнения Orchestration между службами.
Эта сага будет содержать последовательность шагов процесса, чтобы она могла его выполнить.
- PlaceOrderCommand -> Saga
- Сага просит OrdersDiscountService , чтобы узнать, доступна ли скидка для этого Заказ .
- Если доступна скидка, Сага звонит Служба заказов , чтобы применить скидку
- Saga звонки OrdersService для установки статуса Order в OrderPlaced
Примечание: шаги 3 и 4 можно комбинировать
Возникает вопрос: * "Как OrdersDiscountService получить всю необходимую информацию для заказа для расчета скидок?" *
Этого можно добиться, добавив всю информацию о заказе в Событие , которое получит эта служба, или OrdersDiscountService позвоните Служба заказов , чтобы получить информацию.
Вот Отличное видео от Мартина Фолвера об архитектуре, управляемой событиями, в котором обсуждаются эти подходы.
Преимущество оркестровки с Saga состоит в том, что точный процесс явно определен в Saga и может быть найден, понят и исправлен.
Наличие неявных процессов, как в случае Хореография с событиями может быть сложнее для понимания, отладки и поддержки.
Недостатком саг является то, что мы определяем больше вещей.
Лично я склоняюсь к явной Сага , особенно для сложных процессов, но большинство систем, которые я работаю и вижу, используют оба подхода.
Вот некоторые дополнительные ресурсы:
https://blog.couchbase.com/saga-pattern-implement-business-transactions-using-microservices-part/
https://blog.couchbase.com/saga-pattern-implement-business-transactions-using-microservices-part-2/
https://microservices.io/patterns/data/saga.html
Архитектура LMAX очень интересно читать. Это не распределенная система, но управляемая событиями и записывает как входящие события / команды, так и исходящие события. Это интересный способ уловить все, что происходит в системе или сервисе.