Мне кажется, что ваша глобальная конфигурация является либо спецификацией или набор правил, как в системе правил.
В отличие от шаблонов, описанных в книге GOF, в DDD , некоторые строительные блоки / шаблоны являются более общими и могут применяться к различным типам объектов, которые у вас есть.
Например, Entity - это что-то, что имеет жизненный цикл и идентичность. Этапы жизненного цикла обычно следующие: создание, сохранение, реконструкция из хранилища, изменение, а затем его жизненный цикл заканчивается удалением, архивированием, завершением и т. Д.
Объект значения - это то, что не имеет идентичности, (большую часть времени) является неизменным, два экземпляра можно сравнить по равенству их свойств. Объект значения представляет важные концепции в наших областях, например: Деньги в системе, которая занимается бухгалтерским учетом, банковским делом и т. д., Vector3 и Matrix3 в системах, выполняющих математические вычисления и моделирование, таких как системы моделирования (3dsMax, Maya), видеоигры и т. д. Они содержат важные особенности.
Таким образом, все, что вам нужно отслеживать и что имеет идентификационные данные, может быть Entity.
У вас может быть Спецификация, которая является сущностью, Правило, которое является сущностью, Событие также может быть объектом, если ему присвоен уникальный идентификатор. В этом случае вы можете относиться к ним так же, как к любому другому объекту. Вы можете формировать агрегаты, иметь репозитории и службы и при необходимости использовать EventSourcing.
С другой стороны, Спецификация, Правило, Событие или Команда также могут быть объектами значений.
Спецификации и Правила также могут быть Доменными службами.
Здесь также важен ограниченный контекст. Система, обновляющая эти правила, вероятно, находится в другом ограниченном контексте, чем система, которая применяет эти правила. Также возможно, что это не так.
Вот пример.
Давайте создадим систему, в которой Клиент может покупать товары. В этой системе также будут действовать скидки на заказы, для которых действуют особые правила. .
Допустим, у нас есть правило, которое гласит: если Клиент сделал Заказ с более чем 5 <сильными > LineItems он получает скидку. Если этот Заказ имеет общую стоимость некоторой суммы (скажем, 1000 долларов США), он получает скидку.
Процент скидок может быть изменен отделом продаж. В системе продаж есть агрегаты OrderDicountPolicy, которые она может изменять. С другой стороны, Система заказов считывает только агрегаты OrderDicountPolicy и не может их изменять, поскольку это входит в обязанности отдела продаж.
Система продаж и система заказов могут быть частью двух отдельных ограниченных контекстов < / em>: Продажи и Заказы. Ограниченный контекст заказов зависит от ограниченного контекста продаж.
Примечание: я пропущу большинство деталей реализации и добавлю только соответствующие вещи, чтобы сократить и упростить этот пример. Если намерение неясно, я отредактирую и добавлю дополнительные сведения. UUID, DiscountPercentage strong > и Деньги - это объекты-значения, которые я пропущу.
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 службы: OrdersService и OrdersDiscountService.
Есть несколько способов реализовать эту операцию. Мы можем использовать:
- Хореография с событиями
- Оркестровка с явным использованием Saga или диспетчера процессов
Вот как мы можем это сделать, если будем использовать хореографию с событиями.
CreateOrderCommand -> OrdersService -> OrderCreatedEvent
OrderCreatedEvent -> OrdersDiscountService -> OrderDiscountAvailableEvent или OrderDiscountNotAvailableEvent
OrderDiscountAvailableEvent или OrderDiscountNotAvailableEvent -> OrdersService -> OrderPlacedEvent
В этом примере для размещения заказа OrdersService будет ждать OrderDiscountNotAvailableEvent или OrderDiscountNotAvailableEvent em>, чтобы он мог применить скидку перед изменением статуса заказа на OrderPlaced.
Мы также можем использовать явную Saga для оркестрации между сервисами.
Эта сага будет содержать последовательность шагов процесса, чтобы он мог его выполнить.
- PlaceOrderCommand -> Сага
- Saga запрашивает у OrdersDiscountService, доступна ли скидка на этот заказ em. >.
- Если доступна скидка, Saga вызывает OrdersService, чтобы применить скидку.
- Saga вызывает OrdersService, чтобы установить статус Order strong> на OrderPlaced
Примечание. Шаги 3 и 4 можно комбинировать
Возникает вопрос: * "Как OrdersDiscountService получает всю необходимую информацию для Заказа для расчета скидок?" *
Это может быть достигнуто путем добавления всей информации о заказе в Событие, которое будет получать эта служба, или с помощью OrdersDiscountService позвоните в OrdersService, чтобы получить информацию.
Вот отличное видео от Мартина Фолвера по архитектуре, управляемой событиями, в котором обсуждаются эти подходы.
Преимущество оркестрации с сагой заключается в том, что точный процесс явно определен в саге < / strong> и его можно найти, понять и отладить.
Наличие неявных процессов, таких как Хореография с событиями, может быть труднее для понимания, отладки и поддержки.
Обратной стороной саг является то, что мы определяем больше вещей.
Лично я предпочитаю явную 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 очень интересна для чтения. Это не распределенная система, но управляемая событиями и записывает как входящие события / команды, так и исходящие события. Это интересный способ фиксировать все, что происходит в системе или службе.
person
expandable
schedule
05.05.2019