Продолжаем исследовать тему разделения логики запросов от логики команд.
Как уже говорилось в предыдущем посте, для моделей со сложным поведением имеет смысл отделить реализацию поведенческих аспектов от так называемой подсистемы отчетов. И мы говорили, что это даст нам возможность очень гибко разрабатывать модель для представления, не затрачивая ресурсы на решение проблем консолидации с моделью для поведения. А что это даст модели команд — начнем выяснять прямой сейчас.
Вместо абстрактного введения, небольшая иллюстрация. Вот так может выглядеть код некого условного заказа в нашем «очищенном» домене:
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 |
Комментариев нет:
Отправить комментарий