Декларативные vs императивные валидаторы

в 12:56, , рубрики: .net, валидация, декларативное программирование, Программирование, Совершенный код, функциональное программирование, метки: , , , , ,

Хотел бы начать тему о недостатках декларативного подхода с простого примера – процедуры валидации.

Во многих системах (в большинстве?) валидаторы различных бизнес-объектов задаются в декларативном стиле – в виде атрибутов, XML конфигураций и др. Иногда валидаторы генерируются автоматически на основе структуры базы данных (длинны колонок например) и т.д.

Насколько оправдан декларативный подход когда мы задаем валидацию, насколько он удобен? Я предлагаю рассмотреть сложный случай, когда разрабатывается, например, B2Bсистема и каждый клиент, подключенный к системе, может в некоторых случаях иметь разные настройки валидации. Кроме того, предположим, что разработка ведется в команде в параллельных бранчах и нам нужно периодически объединять (merge) их. Да, и еще система предполагает локализацию валидационных сообщений.

Я работаю в стеке технологий MS, поэтому примеры буду брать из этой области.
Возьмем, для примера, простейший класс:
image
Это те данные, которые будут отправляться с клиентской стороны на сервер при создании некоего заказа.

Валидация с помощью Enterprise Library

Вот как можно задать валидацию для этого класса в декларативном стиле с помощью EnterpriseLibrary – это UI (все ноды развернуты):

image

А это XML, который генерирует данный дизайнер:

image

Альтернатива

Какова может быть простейшая альтернатива? Обычно это называют (несколько пренебрежительно) hardcoded (C#):

image

Я в работе иногда использую такой подход, когда данные с web-страницы отправляются на сервер не в виде набора значений формы, а сразу в виде готового JSON-а, который на серверной стороне может быть десериализирован – в нашем случае в OrderEntity. Если мы используем C# код для валидации на серверной стороне, то с помощью такой полезной штуки как SharpKit можно автоматически сгенерировать JavaScript код для валидации нашего объекта на клиентской стороне:

image

Пока все просто. Теперь предположим, что для одного клиента нашей B2Bсистемы, например, DateAvailable будет обязательна, для другого – TotalPrice будет допустима меньше ноля (да, такое тоже бывает, например, если несколько заказов идут «пачкой» и итоговая сумма все равно положительная и т.п.). Кроме того, предположим, что соответствующие изменения внесены в параллельных бранчах. Вот как может выглядеть сравнение конфигурационных файлов Enterprise Library:

image

В свою очередь, результаты сравнения «hardcoded» валидаций гораздо более понятны:

image

Выводы

Давайте еще усложним ситуацию. Пускай нам нужно не просто осуществлять проверку TotalPrice>0, но и показывать сообщение именно напротив DiscountPercent или AdditionalDiscount, если эти величины слишком большие и цена уменьшается до нуля. Кроме того, может быть например проверка DateAvailable>= DateRequired и т.п. Да и членов класса может быть не десяток а сотня.
В любом реальном приложении есть масса валидационной логики, которую нельзя задать с помощью UI или XML или атрибутов. Получается, если мы все таки используем декларативные инструменты, часть логики будет в атрибутах, часть в конфигурациях и часть — в коде. Да, еще же есть и клиентская часть. Таким образом мы получаем кашу, когда относительно простое, по сути, действие, такое как валидация, размывается по всей системе, причем в разнородных ее частях.

Опять же – речь не об Enterprise Library и не не только о валидации. Речь о том, какие преимущества, в каких случаях дает декларативный подход, и, самое главное – каковы мотивы его применения при очевидных недостатках. В каких случаях декларативный стиль обязательно должен быть заменен императивным.
Таким образом, думаю, тему можно считать открытой.

Автор: scrivener


* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js