Имейте в виду, что переход "по проводам" на другой уровень (будь то база данных или сервер приложений) является одним из самых дорогих действий, которые вы можете сделать - обычно сетевой вызов занимает на несколько порядков больше, чем вызовы памяти.
Поэтому стоит структурировать свой API, чтобы избежать избыточных вызовов.
Подумайте, если ваш API похож на это:
// Check to see if a given patient exists
public bool PatientExists(int id);
// Load the specified patient; throws exception if not found
public Patient GetPatient(int id);
Тогда вы, вероятно, дважды попадете в базу данных или будете полагаться на хорошее кэширование, чтобы избежать этого.
Другое соображение заключается в следующем: в некоторых местах ваш код может иметь идентификатор «известный-хороший», в других - нет. Для каждого местоположения требуется отдельная политика относительно того, следует ли создавать исключение.
Вот шаблон, который я использовал для хорошего эффекта в прошлом - есть два метода:
// Load the specified patient; throws exception if not found
public Patient GetExistingPatient(int id);
// Search for the specified patient; returns null if not found
public Patient FindPatient(int id);
Понятно, что GetExistingPatient () можно построить, вызвав FindPatient ().
Это позволяет вашему вызывающему коду получить соответствующее поведение, генерировать исключение, если что-то пошло не так, и избегать обработки исключений в тех случаях, когда это не нужно.