ситуация
Ранее этим вечером я дал ответ на вопрос о StackOverflow.
Вопрос:
Редактирование существующего объекта должно быть сделано на уровне хранилища или в сервисе?
Например, если у меня есть Пользователь, у которого есть задолженность. Я хочу изменить свой долг. Должен ли я сделать это в UserRepository или в сервисе, например, BuyingService, получив объект, отредактировав его и сохранив?
Мой ответ:
Вы должны оставить ответственность за мутирование объекта с тем же объектом и использовать хранилище для извлечения этого объекта.
Пример ситуации:
class User {
private int debt; // debt in cents
private string name;
// getters
public void makePayment(int cents){
debt -= cents;
}
}
class UserRepository {
public User GetUserByName(string name){
// Get appropriate user from database
}
}
Комментарий, который я получил:
Бизнес-логика действительно должна быть в сервисе. Не в модели.
Что говорит интернет?
Итак, это заставило меня искать, так как я никогда (сознательно) не использовал уровень обслуживания. Я начал читать по шаблону сервисного уровня и шаблону единицы работы, но пока не могу сказать, что убежден, что сервисный уровень нужно использовать.
Возьмем, к примеру, эту статью Мартина Фаулера об анти-паттерне анемичной доменной модели:
В доменном пространстве есть объекты, многие из которых названы в честь имен существительных, и эти объекты связаны с богатыми отношениями и структурой, которые имеют истинные доменные модели. Уловка возникает, когда вы смотрите на поведение, и вы понимаете, что на этих объектах почти нет поведения, что делает их чуть более мешками добытчиков и сеттеров. Действительно, часто эти модели поставляются с правилами проектирования, которые говорят, что вы не должны помещать какую-либо доменную логику в доменные объекты. Вместо этого есть набор сервисных объектов, которые захватывают всю логику домена. Эти сервисы живут поверх модели домена и используют модель домена для данных.
(...) Логика, которая должна быть в объекте домена, - это логика домена - проверки, вычисления, бизнес-правила - как бы вы это ни называли.
Мне показалось, что именно такова была ситуация: я защищал манипулирование данными объекта, вводя методы внутри этого класса, которые делают именно это. Однако я понимаю, что это должно быть задано в любом случае, и это, вероятно, больше связано с тем, как эти методы вызываются (используя репозиторий).
У меня также было ощущение, что в этой статье (см. Ниже) уровень обслуживания больше рассматривается как фасад, который делегирует работу базовой модели, а не как фактический трудоемкий уровень.
Уровень приложения [его имя для Service Layer]: определяет задания, которые должно выполнять программное обеспечение, и направляет выразительные доменные объекты для решения проблем. Задачи, за которые отвечает этот уровень, значимы для бизнеса или необходимы для взаимодействия с прикладными уровнями других систем. Этот слой держится тонким. Он не содержит бизнес-правил или знаний, а только координирует задачи и делегирует работу для совместной работы объектов домена в следующем слое вниз. Он не имеет состояния, отражающего бизнес-ситуацию, но может иметь состояние, отражающее ход выполнения задачи для пользователя или программы.
Что здесь усилено :
Сервисные интерфейсы. Сервисы предоставляют сервисный интерфейс, на который отправляются все входящие сообщения. Вы можете рассматривать интерфейс службы как фасад, который предоставляет бизнес-логику, реализованную в приложении (обычно логику на бизнес-уровне), потенциальным потребителям.
И здесь :
Уровень обслуживания должен быть лишен какой-либо прикладной или бизнес-логики и должен быть сосредоточен в первую очередь на нескольких проблемах. Он должен обернуть вызовы бизнес-уровня, перевести ваш домен на общий язык, понятный вашим клиентам, и обработать среду связи между сервером и запрашивающим клиентом.
Это серьезный контраст с другими ресурсами, которые говорят о сервисном уровне:
Уровень обслуживания должен состоять из классов с методами, которые являются единицами работы с действиями, которые принадлежат одной и той же транзакции.
Или второй ответ на вопрос, который я уже связал:
В какой-то момент вашему приложению понадобится бизнес-логика. Кроме того, вы можете проверить входные данные, чтобы убедиться, что не запрошено что-то плохое или недействительное. Эта логика принадлежит вашему уровню обслуживания.
"Решение"?
Следуя рекомендациям в этом ответе , я пришел к следующему подходу, использующему уровень обслуживания:
class UserController : Controller {
private UserService _userService;
public UserController(UserService userService){
_userService = userService;
}
public ActionResult MakeHimPay(string username, int amount) {
_userService.MakeHimPay(username, amount);
return RedirectToAction("ShowUserOverview");
}
public ActionResult ShowUserOverview() {
return View();
}
}
class UserService {
private IUserRepository _userRepository;
public UserService(IUserRepository userRepository) {
_userRepository = userRepository;
}
public void MakeHimPay(username, amount) {
_userRepository.GetUserByName(username).makePayment(amount);
}
}
class UserRepository {
public User GetUserByName(string name){
// Get appropriate user from database
}
}
class User {
private int debt; // debt in cents
private string name;
// getters
public void makePayment(int cents){
debt -= cents;
}
}
Заключение
Все вместе мало что изменилось: код контроллера перешел на уровень обслуживания (что хорошо, так что у этого подхода есть свои плюсы). Однако это не похоже на то, что имеет какое-либо отношение к моему первоначальному ответу.
Я понимаю, что шаблоны проектирования - это руководящие принципы, а не правила, установленные в камне, которые должны быть реализованы при любой возможности. Тем не менее, я не нашел однозначного объяснения уровня обслуживания и того, как его следует рассматривать.
Это средство просто извлечь логику из контроллера и поместить ее в службу вместо этого?
Предполагается ли сформировать договор между контроллером и доменом?
Должен ли быть слой между доменом и уровнем сервиса?
И, наконец, что не менее важно: после оригинального комментария
Бизнес-логика действительно должна быть в сервисе. Не в модели.
Это верно?
- Как бы я представил свою бизнес-логику в сервисе вместо модели?