Мы реализуем большинство наших бизнес-правил в базе данных, используя хранимые процедуры.
Я никогда не могу решить, как лучше всего передать ошибки нарушения ограничения данных из базы данных обратно в пользовательский интерфейс. Ограничения, о которых я говорю, больше связаны с бизнес-правилами, чем с целостностью данных.
Например, ошибка БД, такая как «Невозможно вставить строку с повторяющимся ключом», совпадает с бизнес-правилом «вы не можете иметь более одного Foo с одним именем». Но мы «реализовали» его в наиболее здравом смысле: как уникальное ограничение, которое выдает исключение при нарушении правила.
Другие правила, такие как «Вам разрешено только 100 Foos в день», не вызывают ошибок, скажем так, они изящно обрабатываются пользовательским кодом, таким как return empty dataset
, который код приложения проверяет и передает обратно пользовательский интерфейс.
И в этом заключается загвоздка. Наш код пользовательского интерфейса выглядит следующим образом (это код веб-сервисов AJAX.NET, но подойдет любая инфраструктура ajax):
WebService.AddFoo("foo", onComplete, onError); // ajax call to web service
function onComplete(newFooId) {
if(!newFooId) {
alert('You reached your max number of Foos for the day')
return
}
// update ui as normal here
}
function onError(e) {
if(e.get_message().indexOf('duplicate key')) {
alert('A Foo with that name already exists');
return;
}
// REAL error handling code here
}
(В качестве примечания: я замечаю, что это именно то, что делает stackoverflow, когда вы слишком быстро отправляете комментарии: сервер генерирует ответ HTTP 500
и пользовательский интерфейс его перехватывает.)
Итак, вы видите, что мы обрабатываем нарушения бизнес-правил в двух местах, одно из которых (то есть уникальная ошибка containt) обрабатывается как особый случай кода, который должен обрабатывать real ошибки (не нарушения бизнес-правил), поскольку .NET распространяет исключения вплоть до обработчика onError()
.
Это неправильно. Мои варианты, я думаю:
- перехватить исключение «нарушение дублированного ключа» на уровне сервера приложений и преобразовать во все, что ожидает пользовательский интерфейс в качестве флага «нарушение бизнес-правил»,
- выгрузить ошибку (скажем, с
"select name from Foo where name = @Name"
) и вернуть то, что сервер приложений ожидает в качестве флага "нарушено бизнес-правило",
- в том же значении, что и 2): использовать уникальное ограничение, встроенное в слой db, и вслепую
insert into Foo
, перехватывая любые исключения и преобразовывая их во все, что ожидает сервер приложений, поскольку флаг "нарушено бизнес-правило"
- вслепую
insert into Foo
(например, 3) и пусть это исключение распространяется на пользовательский интерфейс, плюс заставит сервер приложений вывести нарушения бизнес-правил как действительные Exceptions
(вместо 1). Таким образом, ВСЕ ошибки обрабатываются в коде onError()
(или аналогичном) слоя пользовательского интерфейса.
Что мне нравится в 2) и 3), так это то, что нарушения бизнес-правил "выбрасываются" , где они реализуются : в хранимых процессах. Что мне не нравится в 1) и 3), так это то, что я думаю , что они включают в себя глупые проверки, такие как "if error.IndexOf('duplicate key')"
, точно так же, как то, что в настоящее время находится в слое пользовательского интерфейса.
Редактировать : мне нравится 4), но большинство людей говорят, что использовать Exception
s только в исключительных обстоятельствах.
Итак, как вы, элегантно, относитесь к распространению нарушений бизнес-правил вплоть до пользовательского интерфейса?