Проектирование на основе домена: служба домена, служба приложения
Может кто-нибудь объяснить разницу между доменом и службами приложений, приведя несколько примеров? И, если служба является службой домена, я бы поместил фактическую реализацию этой службы в сборку домена, и если да, я бы также внедрил репозитории в эту службу домена? Некоторая информация была бы действительно полезна.
6 ответов
Услуги бывают трех видов: доменные, прикладные и инфраструктурные.
- Доменные сервисы: инкапсулируетбизнес-логику, которая не вписывается в доменный объект и НЕ является типичной операцией CRUD - они принадлежат репозиторию.
- Службы приложений: используются внешними потребителями для связи с вашей системой (например, веб-службы). Если потребителям нужен доступ к операциям CRUD, они будут выставлены здесь.
- Инфраструктурные сервисы: Используются для выявления технических проблем (например, MSMQ, поставщик электронной почты и т. Д.).
Держать доменные службы вместе с вашими доменными объектами разумно - все они сосредоточены на доменной логике. И да, вы можете внедрить репозитории в свои сервисы.
Службы приложений обычно используют как доменные службы, так и хранилища для обработки внешних запросов.
Надеюсь, это поможет!
(Если вам не хочется читать, внизу есть резюме:-)
Я тоже боролся с точным определением сервисов приложений. Хотя ответ Виджая был очень полезен для моего процесса мышления месяц назад, я не согласен с его частью.
Другие источники
Там очень мало информации о сервисах приложений. Такие темы, как совокупные корни, репозитории и доменные сервисы, широко обсуждаются, но сервисы приложений упоминаются лишь кратко или вообще не упоминаются.
В статье MSDN Magazine "Введение в доменно-управляемый дизайн" описываются службы приложений как способ преобразования и / или представления модели вашего домена для внешних клиентов, например, как служба WCF. Так Vijay описывает сервисы приложений. С этой точки зрения сервисы приложений являются интерфейсом для вашего домена.
Статьи Джеффри Палермо об Луковой Архитектуре (часть первая, вторая и третья) хорошо читаются. Он рассматривает службы приложений как понятия уровня приложения, такие как сеанс пользователя. Хотя это ближе к моему пониманию сервисов приложений, оно все еще не соответствует моим мыслям по этому вопросу.
Мои мысли
Я начал думать о службах приложений как о зависимостях, предоставляемых приложением. В этом случае приложение может быть настольным приложением или службой WCF.
Домен
Время для примера. Вы начинаете с вашего домена. Здесь реализованы все сущности и любые доменные службы, которые не зависят от внешних ресурсов. Любые доменные понятия, которые зависят от внешних ресурсов, определяются интерфейсом. Вот возможный макет решения (название проекта выделено жирным шрифтом):
Мое решение - My.Product.Core (My.Product.dll) - DomainServices IExchangeRateService Товар ProductFactory IProductRepository
Product
а также ProductFactory
классы были реализованы в основной сборке. IProductRepository
это то, что, вероятно, поддерживается базой данных. Реализация этого не относится к области и поэтому определяется интерфейсом.
Сейчас мы сосредоточимся на IExchangeRateService
, Бизнес-логика для этого сервиса реализуется внешним веб-сервисом. Однако его концепция все еще является частью домена и представлена этим интерфейсом.
инфраструктура
Реализация внешних зависимостей является частью инфраструктуры приложения:
Мое решение + My.Product.Core (My.Product.dll) - My.Product.Infrastructure.dll (My.Product.Infrastructure.dll) - DomainServices XEExchangeRateService SqlServerProductRepository
XEExchangeRateService
реализует IExchangeRateService
обслуживание домена путем общения с xe.com. Эта реализация может использоваться вашими приложениями, использующими модель вашего домена, включая сборку инфраструктуры.
заявка
Обратите внимание, что я еще не упомянул службы приложений. Мы посмотрим на них сейчас. Допустим, мы хотим предоставить IExchangeRateService
реализация, которая использует кэш для быстрого поиска. Схема этого класса декоратора может выглядеть следующим образом.
public class CachingExchangeRateService : IExchangeRateService
{
private IExchangeRateService service;
private ICache cache;
public CachingExchangeRateService(IExchangeRateService service, ICache cache)
{
this.service = service;
this.cache = cache;
}
// Implementation that utilizes the provided service and cache.
}
Обратите внимание на ICache
параметр? Эта концепция не является частью нашего домена, поэтому это не служба домена. Это сервис приложений. Это зависимость нашей инфраструктуры, которая может быть предоставлена приложением. Давайте представим приложение, которое демонстрирует это:
Мое решение - My.Product.Core (My.Product.dll) - DomainServices IExchangeRateService Товар ProductFactory IProductRepository - My.Product.Infrastructure.dll (My.Product.Infrastructure.dll) - ApplicationServices ICACHE - DomainServices CachingExchangeRateService XEExchangeRateService SqlServerProductRepository - My.Product.WcfService (My.Product.WcfService.dll) - ApplicationServices MemcachedCache IMyWcfService.cs + MyWcfService.svc + Web.config
Все это объединяется в приложении следующим образом:
// Set up all the dependencies and register them in the IoC container.
var service = new XEExchangeRateService();
var cache = new MemcachedCache();
var cachingService = new CachingExchangeRateService(service, cache);
ServiceLocator.For<IExchangeRateService>().Use(cachingService);
Резюме
Полное приложение состоит из трех основных слоев:
- домен
- инфраструктура
- приложение
Доменный уровень содержит доменные объекты и автономные доменные службы. Любые доменные концепции (включая доменные службы, но также и репозитории), которые зависят от внешних ресурсов, определяются интерфейсами.
Уровень инфраструктуры содержит реализацию интерфейсов от уровня домена. Эти реализации могут вводить новые не доменные зависимости, которые должны быть предоставлены приложению. Это сервисы приложений и представлены интерфейсами.
Прикладной уровень содержит реализацию прикладных сервисов. Прикладной уровень также может содержать дополнительные реализации доменных интерфейсов, если реализации, предоставляемые инфраструктурным уровнем, недостаточны.
Хотя эта перспектива может не совпадать с общим определением служб DDD, она отделяет домен от приложения и позволяет совместно использовать сборку домена (и инфраструктуры) между несколькими приложениями.
Лучший ресурс, который помог мне понять разницу между службой приложений и службой домена, - это Java-реализация примера груза Эрика Эванса, найденного здесь. Если вы загрузите его, вы можете проверить внутренние компоненты RoutingService (доменная служба) и BookingService, CargoInspectionService (которые являются службами приложений).
Мой "ага" момент был вызван двумя вещами:
- Читая описание Услуг по ссылке выше, точнее это предложение:
Доменные службы выражаются в терминах вездесущего языка и типов доменов, то есть аргументы метода и возвращаемые значения являются собственными классами домена.
- Читая этот пост, особенно эту часть:
То, что я нахожу большую помощь в отделении яблок от апельсинов, это мышление с точки зрения рабочего процесса приложения. Вся логика, относящаяся к рабочему процессу приложения, обычно заканчивается тем, что службы приложений включаются в уровень приложений, в то время как концепции из домена, которые, как представляется, не соответствуют объектам модели, в конечном итоге образуют одну или несколько служб домена.
Из Красной книги (Внедрение доменного дизайна, Вон Вернон), вот как я понимаю концепции:
Объекты домена (сущности и объекты значений) инкапсулируют поведение, требуемое (суб) доменом, делая его естественным, выразительным и понятным.
Доменные службы инкапсулируют такое поведение, которое не вписывается в один объект домена. Например, книжная библиотека, предоставляющая Book
к Client
(с соответствующими Inventory
изменения) может сделать это из службы домена.
Службы приложений обрабатывают поток сценариев использования, включая любые дополнительные проблемы, необходимые в дополнение к домену. Он часто предоставляет такие методы через свой API для использования внешними клиентами. Чтобы опираться на наш предыдущий пример, наша служба приложений может предоставить метод LendBookToClient(Guid bookGuid, Guid clientGuid)
тот:
- Получает
Client
, - Подтверждает свои разрешения. (Обратите внимание, как мы избавили нашу модель предметной области от проблем безопасности / управления пользователями. Такое загрязнение может привести ко многим проблемам. Вместо этого мы выполняем это техническое требование здесь, в нашей службе приложений.)
- Получает
Book
, - Вызывает службу домена (передает
Client
а такжеBook
) обрабатывать реальную предметную логику предоставления книги клиенту. Например, я полагаю, что подтверждение доступности книги определенно является частью логики предметной области.
Служба приложений, как правило, должна иметь очень простой поток. Сложные потоки сервисов приложений часто указывают на то, что логика домена просочилась из домена.
Как можно надеяться, вы можете видеть, что модель предметной области остается очень чистой, и ее легко понять и обсудить с экспертами в предметной области, поскольку она содержит только свои собственные, актуальные для бизнеса проблемы. Поток приложений, с другой стороны, также намного проще в управлении, поскольку он освобождается от проблем в области, становится лаконичным и простым.
Доменная служба является расширением домена. Это следует рассматривать только в контексте домена. Это не какое-то действие пользователя, например, закрытие аккаунта или что-то в этом роде. Доменная служба подходит там, где нет государства. В противном случае это был бы объект домена. Доменная служба делает что-то, что имеет смысл только тогда, когда выполняется с другими участниками (объектами домена или другими службами). И это имеет смысл - ответственность другого слоя.
Прикладной сервис - это тот уровень, который инициализирует и контролирует взаимодействие между объектами домена и сервисами. Процесс обычно такой: получить объект (или объекты) домена из хранилища, выполнить действие и поместить его (их) туда (или нет). Он может делать больше - например, он может проверять, существует ли объект домена или нет, и генерировать исключения соответственно. Таким образом, он позволяет пользователю взаимодействовать с приложением (и, вероятно, именно отсюда и происходит его имя) - путем манипулирования объектами и службами домена. Сервисы приложений должны, как правило, представлять все возможные варианты использования. Вероятно, лучшее, что вы можете сделать, прежде чем думать о домене, - это создать интерфейсы сервисов приложений, которые позволят вам лучше понять, что вы на самом деле пытаетесь сделать. Наличие таких знаний позволяет вам сосредоточиться на домене.
Репозитории, вообще говоря, могут быть внедрены в доменные сервисы, но это довольно редкий сценарий. Это прикладной уровень, который делает это большую часть времени, хотя.
Подумайте о доменной службе как об объекте, который реализует бизнес-логику или логику, связанную с бизнес-правилами, для объектов домена, и эту логику трудно вписать в те же объекты домена, а также не вызывает изменение состояния службы домена (служба домена - это объект без "состояние" или лучше без состояния, имеющего коммерческое значение), но в конечном итоге изменяет состояние только тех объектов домена, на которых работает.
В то время как служба приложений реализует логику прикладного уровня, такую как взаимодействие с пользователем, проверка ввода, логика, не связанная с бизнесом, а с другими проблемами: аутентификация, безопасность, электронная почта и т. Д., Ограничивая себя простым использованием служб, предоставляемых объектами домена.
Примером этого может быть следующий сценарий, задуманный только для объяснения цели: мы должны реализовать очень маленькое домашнее служебное приложение, которое выполняет простую операцию, то есть "включать свет, когда кто-то открывает дверь комнаты дома, чтобы войти. в и выключить свет при закрытии двери при выходе из комнаты ".
Очень упрощая, мы рассматриваем только 2 предметных объекта: Door
а также Lamp
, каждый из них имеет 2 состояния соответственно open/closed
а также on/off
, и конкретные методы для управления изменениями состояния на них.
В этом случае нам нужна служба домена, которая выполняет определенную операцию включения света, когда кто-то открывает дверь снаружи, чтобы войти в комнату, потому что объекты двери и лампы не могут реализовать эту логику способом, который мы считаем подходящим. их природе.
Мы можем назвать нашу доменную службу как DomoticDomainService
и реализовать 2 метода: OpenTheDoorAndTurnOnTheLight
а также CloseTheDoorAndTurnOffTheLight
, эти 2 метода соответственно изменяют состояние обоих объектов Door
а также Lamp
к open/on
а также closed/off
.
Состояние входа или выхода из комнаты, которого нет ни в объекте службы домена, ни в объектах домена, но будет реализовано как простое взаимодействие с пользователем службой приложения, которую мы можем вызвать HouseService
, который реализует некоторые обработчики событий как onOpenRoom1DoorToEnter
а также onCloseRoom1DoorToExit
и т. д. для каждой комнаты (это только пример для объяснения цели..), которые, соответственно, будут касаться методов обслуживания домена вызова для выполнения контролируемого поведения (мы не рассматривали сущностьRoom
потому что это только пример).
Этот пример далек от того, чтобы быть хорошо спроектированным приложением из реального мира, но имеет единственную цель (как уже было сказано), чтобы объяснить, что такое доменная служба и ее отличие от службы приложений, надеюсь, что она ясна и полезна.
Доменные службы. Методы, которые на самом деле не подходят для одного объекта или требуют доступа к хранилищу, содержатся в доменных службах. Уровень обслуживания домена также может содержать собственную логику домена и является такой же частью модели домена, как сущности и объекты значений.
Службы приложений. Служба приложений - это тонкий слой, который находится над моделью домена и координирует действия приложения. Не содержит бизнес-логики и не содержит состояния каких-либо объектов; однако он может хранить состояние транзакции бизнес-процесса. Служба приложения используется для предоставления API в модель домена с использованием шаблона обмена сообщениями "запрос-ответ".
Millett, C (2010). Профессиональные шаблоны проектирования ASP.NET. Wiley Publishing. 92.
Доменные службы: служба, выражающая бизнес-логику, которая не является частью какого-либо совокупного корня.
У вас есть 2 агрегата:
Product
который содержит имя и цену.Purchase
который содержит дату покупки, список продуктов, заказанных с количеством и ценой продукта на тот момент, и способ оплаты.
Checkout
не входит ни в одну из этих двух моделей и является концепцией вашего бизнеса.Checkout
может быть создан как доменная служба, которая выбирает весь продукт и вычисляет общую стоимость, оплачивая сумму, позвонив в другую доменную службуPaymentService
с частью реализации инфраструктуры, и преобразовать его вPurchase
,
Службы приложений. Служба, которая "координирует" или использует методы домена. Это может быть так просто, как просто ваш контроллер.
Это место, где вы обычно делаете:
public String createProduct(...some attributes) {
if (productRepo.getByName(name) != null) {
throw new Exception();
}
productId = productRepository.nextIdentity();
product = new Product(productId, ...some attributes);
productRepository.save(product);
return productId.value();
// or Product itself
// or just void if you dont care about result
}
public void renameProduct(productId, newName) {
product = productRepo.getById(productId);
product.rename(newName);
productRepo.save(product);
}
Вы можете сделать проверки здесь, такие как проверка, если Product
уникален Если только Product
уникальность - это инвариант, который должен быть частью доменной службы, которую можно назвать UniqueProductChecker
потому что это не может быть частью Product
класс, и он взаимодействует с несколькими агрегатами.
Вот полный пример проекта DDD: https://github.com/VaughnVernon/IDDD_Samples
Вы можете найти множество примеров службы приложений и пару доменных служб