Где вы проводите проверку? - PullRequest
11 голосов
/ 01 июня 2009

Надеюсь, вы увидите проблему, которую я описываю в сценарии ниже. Если не понятно, пожалуйста, дайте мне знать.

У вас есть приложение, которое разбито на три слоя,

  • уровень пользовательского интерфейса, может быть веб-формой asp.net или окном (используется для редактирования персональных данных)
  • уровень бизнес-сервисов среднего уровня, скомпилированный в dll (PersonServices)
  • слой доступа к данным, скомпилированный в dll (PersonRepository)

В моем интерфейсе я хочу создать новый объект Person, установить некоторые свойства, такие как FirstName, LastName, в соответствии с тем, что было введено в пользовательском интерфейсе пользователя, и вызвать PersonServices.AddPerson, передавая только что созданный Person , (AddPerson не должен быть статичным, это просто для простоты, в любом случае AddPerson в конечном счете вызовет AddPerson Репозитория, который затем сохранит данные.)

Теперь часть, о которой я хотел бы услышать ваше мнение, это проверка. Где-то вдоль линии, что вновь созданный Человек должен быть проверен. Вы можете сделать это на стороне клиента, что было бы просто, но что, если бы я хотел проверить Person в моем методе PersonServices.AddPerson. Это гарантирует, что любой человек, которого я хочу сохранить, будет проверен и удалит любую зависимость от уровня пользовательского интерфейса, выполняющего работу. Или, может быть, проверить как в пользовательском интерфейсе, так и на уровне бизнес-сервера. Звучит хорошо, пока верно?

Итак, для простоты я обновлю метод PersonService.AddPerson для выполнения следующих проверок проверки - Проверьте, если FirstName и LastName не пусты - Убедитесь, что этот новый человек еще не существует в моем хранилище

И этот метод вернет True, если все проверки пройдены и Персона сохранена, False, если Проверка не пройдена или Персона не сохранена.

Но этого логического значения, которое возвращает AddPerson, недостаточно для меня на уровне пользовательского интерфейса, чтобы дать пользователю четкую причину сбоя процесса сохранения. Так что же делать одинокому разработчику? В конечном счете, я хотел бы, чтобы метод AddPerson мог гарантировать, что то, что он собирается сохранить, является действительным, и если нет, иметь возможность сообщать причины, по которым он не является недействительным, для моего уровня пользовательского интерфейса.

Просто, чтобы ваши соки текли, некоторые способы решения этой проблемы могут быть: (Некоторые из этих решений, на мой взгляд, отстойные, но я просто помещаю их туда, чтобы вы поняли, что я пытаюсь решать)

  • Вместо AddPerson, возвращающего логическое значение, он может возвращать int (т. Е. 0 = Успех, ненулевое значение равно ошибке, а число указывает причину, по которой произошел сбой.

  • В AddPerson генерировать пользовательские исключения при сбое проверки. Каждый тип пользовательского исключения будет иметь свое собственное сообщение об ошибке. Кроме того, каждое пользовательское исключение будет достаточно уникальным, чтобы его можно было отследить на уровне пользовательского интерфейса

  • Пусть AddPerson вернет какой-то пользовательский класс, свойства которого будут указывать, пройдена ли проверка или нет, и по каким причинам это произошло

  • Не уверен, что это можно сделать в VB или C #, но прикрепите какое-либо свойство к Person и его базовым свойствам. Это «прикрепленное» свойство может содержать такие вещи, как информация для проверки

  • Вставьте сюда свою идею или шаблон

  • И, может быть, еще здесь

Извините за длинный вопрос, но я определенно хотел бы услышать ваше мнение по этому поводу.

Спасибо!

Ответы [ 8 ]

7 голосов
/ 02 июня 2009

Несколько слоев проверки подходят для многослойных приложений.

Пользовательский интерфейс может выполнять самые простые и быстрые проверки (все ли обязательные поля присутствуют, используют ли они соответствующие наборы символов и т. Д.), Чтобы немедленно получать обратную связь, когда пользователь делает опечатку.

Однако бизнес-логика должна иметь львиную долю ответственности за валидацию ... и на этот раз это не проблема, если она "повторяется", т. Е. Если бизнес-уровень повторно проверяет что-то, что уже должно было быть проверено в UI - BL должен проверять все бизнес-правила (эта двойная проверка на правильность UI позволяет нескольким различным UI-клиентам, которые не все могут быть идеальными в своих проверках - например, специальный клиент на смартфоне, который может не иметь хорошего javascript, и так далее - и, немного, защита от злонамеренно взломанных клиентов.

Когда бизнес-логика сохраняет «проверенные» данные в БД, этот слой должен выполнять свои собственные проверки - БД хороши в этом, и, опять же, не беспокойтесь о некотором повторении - - задача БД - обеспечить целостность данных (возможно, вам понадобятся разные способы подачи данных в один день, например, «массовый загрузчик» для импорта нескольких людей из другого источника, и это ключ к обеспечению того, чтобы все эти способы загружались данные всегда соблюдают правила целостности данных); некоторые правила, такие как уникальность и ссылочная целостность, действительно лучше всего применяются в БД, в частности, по соображениям производительности.

Когда БД возвращает сообщение об ошибке (данные, не вставленные как ограничение X, будут нарушены) на бизнес-уровень, задача последнего состоит в том, чтобы переосмыслить эту ошибку в бизнес-терминах и передать результаты в пользовательский интерфейс, чтобы проинформировать пользователя; и, конечно, BL должен аналогичным образом предоставлять четкую и полную информацию о нарушении бизнес-правил для пользовательского интерфейса, снова для отображения пользователю.

Таким образом, «пользовательский объект» - это, очевидно, «единственный путь» (в некоторых сценариях я бы просто сделал его объектом JSON, например). Хранение объекта Person (для сохранения его свойства «проблем с валидацией»), когда БД отказывается сохранять его, не выглядит как простой и понятный метод, поэтому я не думаю, что этот вариант будет особенным; но если вам это нужно (например, чтобы включить функцию «расскажи мне снова, что было не так»), возможно, если клиент ушел до того, как ответ был готов, и ему нужно плавно перезапуститься позже, или список таких объектов для последующего аудита и т. д.) затем «пользовательский объект ошибки проверки» может быть также добавлен к этому списку ... но это «вторичная проблема», главное, чтобы BL ответил на UI таким объектом (который также может быть использован для предоставления полезной информации, не связанной с ошибками, если вставка действительно удалась).

4 голосов
/ 04 июня 2009

Просто быстрый (и, надеюсь, полезный) комментарий: когда вам интересно, где разместить проверку, попробуйте сделать вид, что скоро вы полностью воссоздаете свой слой пользовательского интерфейса, используя технологию, с которой вы еще не очень хорошо знакомы **. Постарайтесь не допустить того, чтобы какая-либо бизнес-логика, похожая на валидацию, вам наверняка придется переписать в новой технологии.

Вы найдете исключения - бизнес-логика, которая в конечном итоге оказывается на уровне вашего пользовательского интерфейса, но, тем не менее, это полезное соображение.

** Мобильный разработчик, Silverlight, Voice XML, что угодно - делая вид, что вы не знаете технологию своего «нового» уровня пользовательского интерфейса, вы можете абстрагироваться от своих опасений и получить меньше информации о реализации.

3 голосов
/ 02 июня 2009

Важными моментами являются:

  • С точки зрения внешнего интерфейса (ов), средний уровень должен выполнить все проверки, вы никогда не знаете, попытается ли кто-нибудь попытаться обойти проверку вашего внешнего интерфейса, поговорив напрямую с вашим посредником Уровень (по какой-либо причине)
  • Средний уровень может принять решение делегировать часть этой проверки на уровень БД (например, ограничения целостности данных)
  • При желании вы можете дублировать некоторую проверку в пользовательском интерфейсе, но это должно делаться только для повышения производительности (чтобы избежать переходов на средний уровень для распространенных сценариев, таких как пропущенные обязательные поля, неправильно отформатированные данные и т. Д.) Эти проверки не должны никогда заменять их выполнение на среднем уровне
2 голосов
/ 16 сентября 2009

Я не согласен с комментарием Дэвида Басараба о том, что одинаковые проверки должны присутствовать во всех слоях. Это противоречит парадигме ответственности слоев по одной причине. Во-вторых, хотя основное намерение состоит в том, чтобы сделать слои (или компоненты) слабо связанными, также важно, чтобы уровень ответственности (и, следовательно, доверие) был наделен на уровнях. Хотя может потребоваться дублирование некоторых проверок в пользовательском интерфейсе и бизнес-уровне (поскольку уровень пользовательского интерфейса может быть обойден попытками взлома), однако не рекомендуется повторять проверки на каждом уровне. Каждый уровень должен выполнять только те проверки, за которые они несут ответственность. Самым большим недостатком повторяющихся проверок на всех уровнях является избыточность кода, что может вызвать кошмар обслуживания.

2 голосов
/ 04 июня 2009

Проверка должна проводиться на всех трех уровнях.

Когда я нахожусь в проекте, я предполагаю, что делаю основу, которая в большинстве случаев не так. Каждый слой является отдельным и должен проверять все входные слои перед выполнением операции

Каждый уровень может иметь разные способы сделать это, не обязательно, чтобы они все использовали одно и то же, но в идеале все они должны использовать одну и ту же проверку с возможностью настройки.

Вы никогда не хотите пропускать плохие данные в базу данных. Таким образом, вы никогда не сможете доверять данным, полученным на бизнес-уровне. Это нужно проверить.

На бизнес-уровне вы никогда не можете доверять уровню пользовательского интерфейса, и вы должны проверить его, чтобы предотвратить ненужные вызовы на уровень базы данных. Слой пользовательского интерфейса работает так же.

1 голос
/ 04 июня 2009

Кроме того, пользовательский интерфейс мог бы передавать данные, необходимые для построения объекта Person, через какой-либо объект PersonBuilder, чтобы создание объекта консолидировалось на уровне домена / бизнеса, и вы можете поддерживать объект Person в состоянии, которое всегда соответствует. Это имеет больше смысла для более сложных сущностей, однако даже для простых, это хорошо для централизации создания объектов, так же, как вы централизуете постоянство и т. Д.

1 голос
/ 04 июня 2009

Если уровень R ближе к пользователю (или любому входному потоку, который вы не контролируете), чем уровень S, то уровень S должен проверять все данные, полученные с уровня R. Это не означает, что уровень R не должен проверять данные. Для пользователя будет лучше, если графический пользовательский интерфейс предупредит его, что он делает ошибку, прежде чем он попытается выполнить новую транзакцию. Но независимо от того, насколько пуленепробиваема проверка в вашем графическом интерфейсе, следующий уровень не должен доверять какой-либо проверке.

Это предполагает, что ваша база данных находится полностью под вашим контролем. Если нет, у вас есть большие проблемы.

1 голос
/ 01 июня 2009

Многое из этого больше стиля, чем вещества. Я лично поддерживаю возвращение объектов статуса как гибкое и расширяемое решение. Я бы сказал, что я думаю, что в игре есть пара классов валидации, во-первых, «соответствуют ли эти данные человека договору о том, кем он является?» и второе: "Эти данные человека нарушают ограничения в базе данных?" Я думаю, что первая проверка может и должна быть сделана на клиенте. Второе должно быть сделано на среднем уровне. С этим делением вы можете обнаружить, что единственные причины, по которым сбой сохранения может быть неудачным, - это 1) нарушение ограничений уникальности или 2) что-то катастрофическое. Затем вы можете вернуть false для первого случая и сгенерировать исключение для другого.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...