Проектирование на основе домена: служба домена, служба приложения

268

Может кто-нибудь объяснить разницу между доменом и службами приложений, приведя несколько примеров? И, если служба является службой домена, я бы поместил фактическую реализацию этой службы в сборку домена, и если да, я бы также внедрил репозитории в эту службу домена? Некоторая информация была бы действительно полезна.

Крис
источник

Ответы:

358

Сервисы бывают трех видов: доменные , прикладные и инфраструктурные .

  • Доменные сервисы : инкапсулирует бизнес-логику , которая не вписывается в доменный объект и НЕ является типичной операцией CRUD - они принадлежат репозиторию .
  • Службы приложений : используются внешними потребителями для связи с вашей системой (например, веб-службы ). Если потребителям нужен доступ к операциям CRUD, они будут выставлены здесь.
  • Инфраструктурные услуги : Используются для выявления технических проблем (например, MSMQ, поставщик электронной почты и т. Д.).

Держать доменные службы вместе с вашими доменными объектами разумно - все они сосредоточены на доменной логике. И да, вы можете внедрить репозитории в свои сервисы.

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

Надеюсь, это поможет!

Виджей Патель
источник
2
Где бы вы разместили команды и запросы по CQRS? Какой сервис генерирует их и какой сервис их обрабатывает?
inf3rno
5
Я думаю, что Сервисы Приложений должны быть независимы от технических деталей, таких как «веб-сервисы», они используются такими сервисами. См. Услуги в доменно-управляемом дизайне
deamon
1
@prograhammer. Примером службы домена может быть FundsTransferService, где моделью домена является BankAccount, при передаче может иметься некоторая бизнес-логика, которая не вписывается непосредственно в объект счета (взято из книги DDD Эванса).
BornToCode
скажем, например, Loginuser () будет примером доменной службы. где как getUsers () будет служба приложений ??
filthy_wizard
Оба являются скорее прикладными услугами, потому что аутентификация, а также решения об авторизации не относятся к основному домену.
MauganRa
114

(Если вам не хочется читать, внизу есть резюме :-)

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

Другие источники

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

В статье 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, она отделяет домен от приложения и позволяет совместно использовать сборку домена (и инфраструктуры) между несколькими приложениями.

Нильс ван дер Рест
источник
2
@ dario-g: вам придется реконструировать / перенаселить модель вашего домена из модели запроса и передать модель домена в службу домена. Этот вопрос может дать вам некоторые идеи. Если нет, дайте мне знать, и я посмотрю, есть ли у меня время, чтобы добавить ответ на другой вопрос.
Нильс ван дер Рест
1
@Tiendq: Вы имеете в виду IExchangeRateServiceинтерфейс? Это концепция домена, то есть то, что включено в повсеместный язык вашего клиента. Другие части вашего домена могут зависеть от этой услуги, поэтому ее интерфейс определен на уровне домена. Но поскольку его реализация включает в себя внешний веб-сервис, реализующий класс находится на уровне инфраструктуры. Таким образом, уровень домена касается только бизнес-логики.
Нильс ван дер Рест
4
@Tiendq: В традиционной многоуровневой архитектуре инфраструктура обычно не зависит от домена. Но в Onion Architecture (см. Ссылки в моем ответе) инфраструктура реализует внешние зависимости домена. Но я бы не сказал, что инфраструктура зависит от домена, он просто ссылается на него. Я взял термин «инфраструктура» из Onion Architecture, но «внешнее» может быть лучшим названием.
Нильс ван дер Рест
1
@Derek: Одна из этих «вещей» может быть ExchangeRateэкземпляром, который содержит базовую валюту, контрвалюту и значение обменного курса между этими двумя валютами. Эти тесно связанные значения представляют концепцию «обменного курса» в домене, поэтому они находятся на уровне домена. Хотя это может показаться простым DTO, в DDD он называется объектом-значением и может содержать дополнительную бизнес-логику для сравнения или преобразования экземпляров.
Нильс ван дер Рест
6
Я не согласен с той частью, где вы не согласны с Виджаем, и вот почему. CachingExchangeRateService является проблемой инфраструктуры. Даже если вы в целом принимаете ICache, реализация этого ICache зависит от используемой технологии (т. Е. Web, Windows). Тот факт, что он универсален, не делает его сервисом приложений. Служба приложений - это API вашего домена. Что если вы захотите открыть свой домен кому-то еще, пишущему приложение, что они будут использовать? Службам приложений, и им может не понадобиться кэширование, так что ваш смысл кэширования для них бесполезен (т. Е. Почему это инфраструктура)
Аарон Хокинс
38

Лучший ресурс, который помог мне понять разницу между службой приложений и службой домена, - это Java-реализация примера груза Эрика Эванса, найденного здесь . Если вы загрузите его, вы можете проверить внутренние компоненты RoutingService (доменная служба) и BookingService, CargoInspectionService (которые являются службами приложений).

Мой 'ага' момент был вызван двумя вещами:

  • Читая описание Услуг по ссылке выше, точнее это предложение:

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

  • Читая этот блог , особенно эту часть:

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

гхола
источник
3
Я согласен, именно так я определяю службы приложений, и они подходят ко всем ситуациям, с которыми я сталкивался до сих пор. Доменные службы имеют дело со всем, что связано с объектами домена, но выходят за рамки одной сущности. Например: BookReferencesService.GetNextAvailableUniqueTrackingNumber (), основное внимание уделяется бизнес-правилам *. Что касается службы приложений, это именно то, что вы описываете, большую часть времени я начинаю с того, что включаю этот рабочий бизнес-процесс в свои действия контроллера, а когда замечаю это, я реорганизую эту логику на уровне службы приложений. Можно сказать, что этот слой предназначен для случаев использования
tobiak777
1
* И такие интерфейсы доменных служб используются объектами домена.
tobiak777
32

Доменная служба является расширением домена. Это следует рассматривать только в контексте домена. Это не какое-то действие пользователя, например закрытие аккаунта или что-то в этом роде. Доменная служба подходит там, где нет государства. В противном случае это будет объект домена. Доменная служба делает что-то, что имеет смысл только тогда, когда выполняется с другими участниками (объектами домена или другими службами). И это имеет смысл - ответственность другого слоя.

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

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

kboom
источник
10
«Служба домена подходит там, где нет состояния. В противном случае это будет объект домена». сделал это щелкнуть для меня. Спасибо.
Ник
32

Из Красной книги (Внедрение доменного дизайна, Вон Вернон), вот как я понимаю концепции:

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

Доменные службы инкапсулируют такое поведение, которое не вписывается в один объект домена. Например, книга библиотека одалживая Bookк Client(с соответствующими Inventoryизменениями) , может сделать это от службы домена.

Службы приложений обрабатывают поток сценариев использования, включая любые дополнительные проблемы, необходимые в дополнение к домену. Он часто предоставляет такие методы через свой API для использования внешними клиентами. Чтобы опираться на наш предыдущий пример, наша служба приложений может предоставить метод, LendBookToClient(Guid bookGuid, Guid clientGuid)который:

  • Получает Client.
  • Подтверждает свои разрешения. ( Обратите внимание, как мы избавили нашу модель домена от проблем безопасности / управления пользователями. Такое загрязнение может привести ко многим проблемам. Вместо этого мы выполняем это техническое требование здесь, в нашей службе приложений. )
  • Получает Book.
  • Вызывает доменную службу (передавая Clientи Book) для обработки фактической доменной логики предоставления книги клиенту. Например, я полагаю, что подтверждение доступности книги определенно является частью логики предметной области.

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

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

Timo
источник
3
Я бы сказал, что служба приложений также является точкой, где разрешаются зависимости. Его метод представляет собой вариант использования, единый поток, поэтому он может принимать обоснованные решения по конкретным реализациям для использования. Здесь также подходят транзакции с базой данных.
Тимо
10

Доменные службы. Методы, которые на самом деле не подходят для одного объекта или требуют доступа к хранилищу, содержатся в доменных службах. Уровень обслуживания домена также может содержать собственную логику домена и является такой же частью модели домена, как сущности и объекты значений.

Службы приложений. Служба приложений - это тонкий слой, который находится над моделью домена и координирует действия приложения. Не содержит бизнес-логики и не содержит состояния каких-либо объектов; однако он может хранить состояние транзакции бизнес-процесса. Служба приложений используется для предоставления API в модель домена с использованием шаблона обмена сообщениями «запрос-ответ».

Millett, C (2010). Профессиональные шаблоны проектирования ASP.NET. Wiley Publishing. 92.

GorkemHalulu
источник
7

Доменные службы . Служба, выражающая бизнес-логику, которая не является частью какого-либо совокупного корня.

  • У вас есть 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);
}

Вы можете выполнить валидацию здесь, например, проверить, является ли a Productуникальным. Если Productуникальное существо не является инвариантом, то оно должно быть частью доменной службы, которая может быть вызвана, UniqueProductCheckerпоскольку она не может быть частью Productкласса и взаимодействует с несколькими агрегатами.

Вот полный пример проекта DDD: https://github.com/VaughnVernon/IDDD_Samples

Вы можете найти множество примеров службы приложений и пару доменных служб

не важно
источник
Обязательно ли проверять и сохранять объекты только в службах приложений? Если у меня есть объекты A, B и C, и все они связаны друг с другом (A -> B -> C), и работа на A должна вызвать изменения в B и C, вызывая одну доменную службу из другой, как это сделать?
MrNVK
> Обязательно ли проверять и сохранять объекты только в службах приложений? Если нужно, то да. В большинстве случаев вам нужно проверить, существует ли идентификатор, потому что в противном случае вы будете работать с пустой переменной.
значения
1
> Если у меня есть объекты A, B и C, и все они связаны друг с другом (A -> B -> C), и работа над A должна вызвать изменения в B и C путем вызова одной доменной службы из другой, как это сделать ? Я не уверен, что вы подразумеваете под "вызовом одной доменной службы из другой", но для реакции на изменения сущности вы можете использовать события или просто организовать их с помощью службы приложений, например: aggregateA.doOperation (), aggregateB.doAnother ( ). Оркестр против хореографии
неважно
Спасибо за ответ! «вызов одной доменной службы из другой» - я имею в виду, если у меня сложная операция над объектом A, то я должен использовать ADomainService. Но эта операция, в дополнение к объекту A, влияет на объект B. Операция, которая должна выполняться на объекте B в ADomainService, также является сложной. Поэтому я должен использовать BDomainService от ADomainService. Теперь я сомневаюсь в таком подходе :) Но если я добавлю эту логику в ApplicationService, разве это не нарушит инкапсуляцию бизнес-процессов, которые должны быть только на уровне домена, а не на уровне приложения?
MrNVK
Вы можете просто отправить событие из своей доменной службы, если считаете, что оно должно быть в доменной службе, а не в службе приложений.
значения
1

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

В то время как служба приложений реализует логику аппликативного уровня как взаимодействие с пользователем, проверку ввода, логику, не связанную с бизнесом, а с другими проблемами: аутентификацией, безопасностью, отправкой по электронной почте и т. Д., Ограничивая себя простым использованием служб, предоставляемых объектами домена.

Примером этого может быть следующий сценарий, предназначенный только для объяснения цели: мы должны реализовать очень маленькое вспомогательное приложение, которое выполняет простую операцию, то есть «включает свет, когда кто-то открывает дверь комнаты дома, чтобы войти и выключите свет, когда закроете дверь, выходящую из комнаты ".

Упрощая много мы рассмотрим только две сущности домена: Doorи Lampкаждый из них имеет 2 состояния, respectevely open/closedи on/off, и методы , характерные для работающих изменений состояния на них.

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

Мы можем вызвать нашу доменную службу как DomoticDomainServiceи реализовать 2 метода: OpenTheDoorAndTurnOnTheLightи CloseTheDoorAndTurnOffTheLightэти 2 метода соответственно изменяют состояние обоих объектов, Doorа также Lampна open/onи closed/off.

Состояние входа или выхода из комнаты, которого нет в объекте службы домена и в объектах домена, но будет реализовано как простое взаимодействие пользователя с прикладной службой, которую мы можем вызвать HouseService, которая реализует некоторые обработчики событий как onOpenRoom1DoorToEnterи onCloseRoom1DoorToExit, и так далее, для каждой комнаты (это только пример для объяснения цели ..) , которая соответственно будет касаться методов обслуживания домена вызова для выполнения поведения с участием (мы не рассматривали сущность, Roomпотому что это только пример) ,

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

Сиро Корвино
источник
Сиро: Ваш пример не практичен, и это очень запутанно.
Мортеза Азизи
Привет Мортеза, не могли бы вы быть более конкретным? Ваш риск быть только «суждением» без каких-либо реальных аргументов. Спасибо
Чиро Корвино