Я все еще ищу лучшие практики для проверки модели предметной области. Это хорошо, чтобы поставить проверку в конструкторе модели предметной области? мой пример проверки модели домена выглядит следующим образом:
public class Order
{
private readonly List<OrderLine> _lineItems;
public virtual Customer Customer { get; private set; }
public virtual DateTime OrderDate { get; private set; }
public virtual decimal OrderTotal { get; private set; }
public Order (Customer customer)
{
if (customer == null)
throw new ArgumentException("Customer name must be defined");
Customer = customer;
OrderDate = DateTime.Now;
_lineItems = new List<LineItem>();
}
public void AddOderLine //....
public IEnumerable<OrderLine> AddOderLine { get {return _lineItems;} }
}
public class OrderLine
{
public virtual Order Order { get; set; }
public virtual Product Product { get; set; }
public virtual int Quantity { get; set; }
public virtual decimal UnitPrice { get; set; }
public OrderLine(Order order, int quantity, Product product)
{
if (order == null)
throw new ArgumentException("Order name must be defined");
if (quantity <= 0)
throw new ArgumentException("Quantity must be greater than zero");
if (product == null)
throw new ArgumentException("Product name must be defined");
Order = order;
Quantity = quantity;
Product = product;
}
}
Спасибо за все ваши предложения.
источник
Несмотря на то, что этот вопрос немного устарел, я хотел бы добавить кое-что стоящее:
Я хотел бы согласиться с @MichaelBorgwardt и расширить его, подняв тестируемость. В книге «Эффективная работа с устаревшим кодом» Майкл Фезерс много говорит о препятствиях для тестирования, и одно из этих препятствий - это «трудно построить» объекты. Построение недопустимого объекта должно быть возможным, и, как предполагает Фаулер, зависящие от контекста проверки достоверности должны быть в состоянии идентифицировать эти условия. Если вы не можете понять, как создать объект в тестовой системе, у вас будут проблемы с тестированием вашего класса.
Что касается обоснованности, мне нравится думать о системах управления. Системы управления работают, постоянно анализируя состояние выхода и применяя корректирующие действия, когда выход отклоняется от заданного значения, это называется управлением с обратной связью. Управление в замкнутом контуре изначально ожидает отклонения и действует для их исправления, и именно так работает реальный мир, поэтому во всех реальных системах управления обычно используются контроллеры с замкнутым контуром.
Я думаю, что использование контекстно-зависимой валидации и простота создания объектов облегчит работу вашей системы в будущем.
источник
Как я уверен, вы уже знаете ...
Проверка достоверности данных, передаваемых в качестве параметров c'tor, определенно действительна в конструкторе - в противном случае вы, возможно, разрешаете создание недопустимого объекта.
Однако (и это только мое мнение, на данный момент я не могу найти хороших документов) - если проверка данных требует сложных операций (таких как асинхронные операции - возможно, проверка на основе сервера при разработке приложения для настольного компьютера), тогда лучше вставьте функцию инициализации или явной проверки некоторого вида, и члены
null
установят значения по умолчанию (такие как ) в c'or.Кроме того, как примечание, как вы включили его в свой пример кода ...
Если вы не выполняете дальнейшую проверку (или другую функциональность)
AddOrderLine
, я бы, скорее всего, представилList<LineItem>
объект как свойство, а не выступил быOrder
как фасад .источник
AddLineItem
метод. На самом деле, для DDD это предпочтительнее. ЕслиList<LineItem>
изменить на пользовательский объект коллекции, то открытое свойство и все, что зависело отList<LineItem>
свойства, подвержены изменениям, ошибкам и исключениям.Проверка должна быть выполнена как можно скорее.
Валидация в любом контексте, модель домена или любой другой способ написания программного обеспечения, должны служить целям того, ЧТО вы хотите проверить и на каком уровне вы находитесь в данный момент.
Исходя из вашего вопроса, я думаю, что ответ будет разделить проверку.
Проверка свойства проверяет, является ли значение этого свойства правильным, например, когда диапазон от 1 до 10 исключен.
Проверка объекта гарантирует, что все свойства объекта действительны в сочетании друг с другом. например, BeginDate до EndDate. Предположим, что вы читаете значение из хранилища данных, и BeginDate и EndDate инициализируются в DateTime.Min по умолчанию. При установке BeginDate нет никаких оснований для применения правила «must be before EndDate», так как это не применяется к YET. Это правило следует проверять ПОСЛЕ того, как были установлены все свойства. Это можно вызвать на уровне совокупного корня
Валидация также должна быть предварительно сформирована для совокупного (или совокупного корневого) объекта. Объект Order может содержать действительные данные, как и его OrderLines. Но затем бизнес-правило гласит, что ни один заказ не может превышать 1000 долларов. Как вы будете применять это правило, в некоторых случаях это разрешено. Вы не можете просто добавить свойство «не проверять сумму», так как это приведет к злоупотреблению (рано или поздно, возможно, даже вы, просто чтобы убрать этот «неприятный запрос»).
Далее идет проверка на уровне представления. Вы действительно собираетесь отправить объект по сети, зная, что он потерпит неудачу? Или вы сэкономите пользователю этот бурдон и сообщите ему, как только он введет недопустимое значение. Например, в большинстве случаев ваша среда разработки будет работать медленнее, чем производство. Хотели бы вы подождать 30 секунд, прежде чем вас проинформируют о том, что «вы забыли это поле СНОВА во время еще ДРУГОГО тестового прогона», особенно когда есть ошибка в работе, которая должна быть исправлена, когда босс дышит вам в шею?
Предполагается, что проверка на уровне постоянства должна быть максимально приближена к проверке значения свойства. Это поможет предотвратить исключения при чтении ошибок «null» или «invalid value» при использовании картографов любого типа или простых старых считывателей данных. Использование хранимых процедур решает эту проблему, но требует написать ту же логику оценки СНОВА и выполнить ее СНОВА. А хранимые процедуры - это область администрирования БД, поэтому не пытайтесь также выполнять ЕГО работу (или, что еще хуже, беспокоите его этой «мелкой добычей, за которую ему не платят»).
так сказать с некоторыми известными словами «это зависит», но по крайней мере теперь вы знаете, ПОЧЕМУ это зависит.
Хотелось бы разместить все это в одном месте, но, к сожалению, этого сделать нельзя. Выполнение этого приведет к зависимости от «объекта Бога», содержащего ВСЕ проверки для ВСЕХ слоев. Вы не хотите идти по этому темному пути.
По этой причине я только выкидываю исключения проверки уровня свойства. На всех других уровнях я использую ValidationResult с методом IsValid, чтобы собрать все «нарушенные правила» и передать их пользователю в одном AggregateException.
При распространении вверх по стеку вызовов я снова собираю их в AggregateExceptions, пока не достигну уровня представления. Сервисный уровень может выдать это исключение прямо клиенту в случае WCF как FaultException.
Это позволяет мне взять исключение и либо разделить его, чтобы показать отдельные ошибки в каждом элементе управления вводом, либо сгладить его и отобразить в одном списке. Выбор за вами.
Вот почему я также упомянул валидацию презентации, чтобы максимально замкнуть их.
Если вам интересно, почему у меня также есть проверка на уровне агрегации (или, если хотите, на уровне обслуживания), это потому, что у меня нет хрустального шара, говорящего мне, кто будет использовать мои услуги в будущем. У вас будет достаточно проблем с поиском собственных ошибок, чтобы другие люди не допускали ошибок ваших, введя неверные данные. Например, вы управляете приложением A, но приложение B передает некоторые данные, используя ваш сервис. Угадай, кого они спрашивают первыми, когда есть ошибка? Администратор приложения B с радостью сообщит пользователю «с моей стороны нет ошибок, я просто передаю данные».
источник