Продолжаем исследовать тему разделения логики запросов от логики команд.
Как уже говорилось в предыдущем посте, для моделей со сложным поведением имеет смысл отделить реализацию поведенческих аспектов от так называемой подсистемы отчетов. И мы говорили, что это даст нам возможность очень гибко разрабатывать модель для представления, не затрачивая ресурсы на решение проблем консолидации с моделью для поведения. А что это даст модели команд — начнем выяснять прямой сейчас.
Вместо абстрактного введения, небольшая иллюстрация. Вот так может выглядеть код некого условного заказа в нашем «очищенном» домене:
class Order : RootAggregate
{
readonly IList _lines = new List();
readonly DateTime _createdDate;
decimal _totalAmount;
public Order(DateTime createdDate)
{
_createdDate = createdDate;
}
public void AddProduct(string productName, int quantity, decimal price)
{
_lines.Add(new OrderLine(productName, quantity, price));
UpdateTotal();
}
private void UpdateTotal()
{
_totalAmount = _lines.Sum(x => x.Price*x.Quantity);
}
}
public class OrderLine : ValueObject
{
public string ProductName { get; private set; }
public int Quantity { get; private set; }
public decimal Price { get; private set; }
public OrderLine(string productName, int quantity, decimal price)
{
ProductName = productName;
Quantity = quantity;
Price = price;
}
}
Главная особенность приведенного кода в том, что снаружи доступ к состоянию агрегата закрыт. Т.е. вообще никаких шансов узнать из чего состоит выбранный заказ. Да и не нужно этого — ведь для оценки состояния модели у нас есть отчеты. А в модели поведения возможно только управление поведением.
Все было бы хорошо, если бы у этой стройной идеи не было бы исключений. Иногда состояния бывают нужны внутри самого домена. Например, для заказа на сумму, превышающую некое значение мы должны применить стандартную скидку примерно в таком духе:
var orderIsApplicableForDiscountSpecification =
new OrderIsApplicableForDiscountSpecification();
if (orderIsApplicableForDiscountSpecification.Matches(order))
order.ApplyDiscount(new StandardDiscount());
где сама спецификация реализована таким образом:
class OrderIsApplicableForDiscountSpecification
{
public bool Matches(Order order)
{
return order.TotalAmount >= 10000;
}
}
Таким образом появляется и свойство TotalAmount у заказа.
На самом деле, никакой мистики здесь нет — все вышепоказанное ни что иное, как обычная банальная инкапсуляция. Просто в свете применения принципа CQS 80% случаев использования информации о состоянии модели просто уходят в небытие и вместе с этим уходит вся необходимая инфраструктура в виде публичных свойств.
| Опубликовать | Tweet |
Комментариев нет:
Отправить комментарий