Мой ответ может быть немного не по теме, но я все равно публикую его, потому что я чувствую, что хочу сказать, что это немного для широкой темы для комментариев.
Вы говорите
Как известно, сервисный уровень отвечает за обновление (и, конечно, чтение, запись и удаление) моделей (или я могу назвать их сущностями).
Дело в том,этот сервисный уровень не должен использоваться таким образом.Я имею в виду - у вас вообще не должно быть так называемых услуг.То, что вы описываете, известно как модель анемичной области .Это комбинация сущностей и сервисов, где сущности являются простыми структурами данных (а не собственными объектами!), А сервисы используются для выполнения операций над сущностями.
По словам Мартина Фаулера
[...] существует набор сервисных объектов, которые захватывают всю логику домена, выполняют все вычисления и обновляют объекты модели результатами.Эти сервисы работают поверх модели домена и используют модель домена для данных.
И это плохо.
Объединение методов и данных внутри одного объекта,Тогда то, что вы будете искать, это шаблон проектирования Unit of Work.На самом деле Entity Framework уже реализует этот шаблон.Что касается реализации этого подхода с расширенными объектами с помощью EF (или фактически любого ORM, который вы можете использовать в .NET), я рекомендую пост Вона Вернона о проектировании агрегатов с EF .Невозможно суммировать весь этот пост, но во избежание ответа только по ссылке: в основном Вон предлагает два метода: создание отдельного интерфейса с помощью класса реализации и объекта домена, для которого создается резервная копия чем-то, что он называет объектом состояния.
Чтобы ответить на комментарии: в основном да - предложение сводить данные и методы вместе, вместо того, чтобы иметь одну так называемую сущность (которая в большинстве случаев является простой структурой данных, используемой только для получения данных из БД) и отдельный класс, называемый сервисом, который выполняет действия надсущность.
Какая бы хорошая и забавная идея это ни звучало, это все еще теоретически, верно?Вторая ссылка о том, как применить этот подход к работе.Естественно, когда вы начнете проводить рефакторинг своего кода, поворачивающегося из модели анемичного домена в модель расширенного домена, вам придется задать себе вопрос - как мне сохранить мой богатый объект в базе данных?Я имею в виду - вся цель этого состоит в том, чтобы заключить бизнес (доменную) логику в объект без необходимости иметь дело со всем этим техническим беспорядком.Мы стремимся к тому, чтобы как можно более чистый объект C # имел как можно меньше зависимостей.Давайте поговорим о примерах.Представьте, что у вас есть Order
и Product
класс.
[Table("orders")]
class Order
{
[Key]
public int Id { get; set; }
public List<Product> Products { get; set; }
// rest omitted for clarity
}
[Table("products")]
class Product
{
[Key]
public int Id { get; set; }
[Required]
public string Name { get; set; }
// rest omitted for clarity
}
В модели с анемичной областью у вас будет служба, выполняющая такие действия, как
class OrderService
{
public void AddProduct(int orderId, int productId)
{
Order order = this.orderRepository.FirstOrDefault(orderId);
Product product = this.productRepository.FirstOrDefault(productId);
if(!order.Products.Contains(product)
{
order.Products.Add(product);
}
this.orderRepository.Save(order);
}
}
С моделью с расширенной областью у вас будет класс, подобныйthis
[Table("orders")]
class Order
{
[Key]
public int Id { get; set; }
public List<Product> Products { get; set; }
public bool AddProduct(Product product)
{
if(this.Products.Contains(product))
{
return false;
}
order.Products.Add(product);
return true;
}
}
Этот класс будет использоваться в контроллере как
class OrderController
{
public ActionResult AddProduct(int orderId, int productId){
Order order = this.orderRepository.FirstOrDefault(orderId);
Product product = this.productRepository.FirstOrDefault(productId);
bool productAdded = order.AddProduct(product);
// do something else
}
}
Обратите внимание на две вещи: 1. Мы переместили технические вещи в контроллер и инкапсулировали бизнес-логику внутри класса Order
(давайтепритвориться, что if(this.Products.Contains(product)
- наше единственное бизнес-правило) [хорошо] 1. Order
класс должен иметь только деловые вещи.Специфичные для ORM аннотации не вносят ничего, кроме технического шума, когда дело доходит до чтения и понимания этого класса, не говоря уже о том, что у нас есть ненужные зависимости внутри нашей модели [плохая вещь]
Вон обсуждает два способа решения этой проблемы.Вы либо извлекаете отдельный интерфейс с помощью своих бизнес-методов
interface IOrder
{
bool AddProduct(Product product);
}
, а затем реализуете его в своем классе, например class Order : IOrder
.Недостатки этого решения указаны в сообщении в блоге
Вездесущий язык на самом деле не подкреплен использованием интерфейсов, таких как IProduct, IBacklogItem и т. Д. IProduct и IBacklogItem не на нашем вездесущем языке, а Product и BacklogItem. Таким образом, клиентские имена должны быть Product, BacklogItem и тому подобное. Мы могли бы сделать это просто, назвав интерфейсы Product, BacklogItem, Release и Sprint, но это означало бы, что нам придется придумывать разумные имена для классов реализации. Давайте просто остановимся там и перейдем ко второму и связанному с ним вопросу.
и
На самом деле нет веских причин для создания отдельного интерфейса. Маловероятно, что мы когда-либо создадим две или более реализации IProduct или любого другого интерфейса. Лучшая причина, по которой мы имеем создание отдельного интерфейса, - это когда может быть или есть несколько реализаций, и это просто не произойдет в этом базовом домене.
Другое решение заключается в создании двух объектов - один только для бизнес-логики, а другой отвечает за отслеживание изменений и используется для связи с базой данных. При таком подходе у вас есть бизнес-объект без каких-либо признаков несущественных с деловой точки зрения деталей. Вон заключает
В конце концов, наша цель - держаться подальше от Entity Framework
Это действительно широкий предмет. Трудно попытаться объяснить это таким способом - на эту тему есть целые книги ;-). Как правило, я бы порекомендовал вам прочитать книгу Вернона под названием Реализация доменного дизайна . Речь идет о DDD, но также показывает, как можно написать правильный объектно-ориентированный код.