Дизайн, керований доменом: сервіс домену, сервіс додатків


268

Чи може хтось пояснити різницю між доменними та прикладними службами, надавши кілька прикладів? І якщо служба є доменною службою, я б поставив фактичну реалізацію цієї послуги в рамках доменної збірки, і якщо так, чи я також вкладатимуть сховища в цю службу домену? Деяка інформація була б дуже корисною.

Відповіді:


356

Послуги пропонуються у трьох варіантах: Доменні служби , Прикладні послуги та Інфраструктура .

  • Служби доменів : інкапсулює ділову логіку, яка, природно, не входить в об’єкт домену, і НЕ є типовими операціями CRUD - вони належатимуть до репозиторію .
  • Прикладні послуги : Використовуються зовнішніми споживачами для спілкування з вашою системою (думаю, Web Services ). Якщо споживачам потрібен доступ до операцій з CRUD, вони опиняться тут.
  • Послуги з інфраструктури : використовуються для абстрактних технічних проблем (наприклад, MSMQ, постачальник електронної пошти тощо).

Обслуговування доменних служб разом із об’єктами домену є розумним - всі вони зосереджені на логіці домену. І так, ви можете вставити сховища у свої служби.

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

Сподіваюся, що це допомагає!


2
Куди б ви розмістили команди та запити CQRS? Яка служба їх генерує та яка служба обробляє їх?
inf3rno

5
Я думаю, що Служби прикладних програм повинні не залежати від технічних деталей, таких як "веб-сервіси", вони використовуються такими службами. Дивіться Служби в дизайні, керованому доменом
деамон

1
@prograhammer - Прикладом для послуги домену може бути FundsTransferService, де модель домену є рахунком BankAccount, переказ може мати деяку логіку бізнесу, яка не вписується безпосередньо в об’єкт рахунку (взято з книги DDD Еванса).
BornToCode

так, скажімо, наприклад, Loginuser () буде прикладом служби домену. де, як getUsers () була б послуга додатків ??
filthy_wizard

Обидва - це швидше службові додатки, оскільки рішення про автентифікацію, а часто і про авторизацію, не належать до основного домену.
MauganRa

114

(Якщо ви не любите читати, внизу є підсумок :-)

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

Інші ресурси

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

Стаття журналу MSDN Вступ до дизайну, керованого доменом описує прикладні послуги як спосіб перетворення та / або піддавання вашої доменної моделі зовнішнім клієнтам, наприклад, як послуга WCF. Ось як описує Vijay і сервіси прикладних програм. З цієї точки зору, сервіси додатків - це інтерфейс до вашого домену .

Статті Джефрі Палермо про архітектуру цибулі (частина перша , дві та три ) - це добре прочитане. Він розглядає послуги додатків як концепції на рівні додатків , наприклад, сеанс користувача. Хоча це ближче до мого розуміння прикладних служб, воно все ще не відповідає моїм думкам з цього приводу.

Мої думки

Я прийшов розглядати прикладні послуги як залежності, що надаються додатком . У цьому випадку програма може бути додатком для настільних ПК або послугою WCF.

Домен

Час для прикладу. Ви починаєте зі свого домену. Тут реалізовані всі об'єкти та будь-які доменні служби, які не залежать від зовнішніх ресурсів. Будь-які концепції домену, які залежать від зовнішніх ресурсів, визначаються інтерфейсом. Ось можливий макет рішення (назва проекту жирним шрифтом):

Моє рішення
- My.Product.Core (My.Product.dll)
  - Доменні сервіси
      IExchangeRateService
    Товар
    ProductFactory
    IProductRepository

ProductІ ProductFactoryкласи були реалізовані в базовій збірці. Це IProductRepositoryте, що, ймовірно, підтримується базою даних. Реалізація цього не стосується домену, тому визначається інтерфейсом.

Поки що ми зупинимося на IExchangeRateService. Логіка бізнесу для цієї послуги реалізується зовнішньою веб-службою. Однак його концепція все ще є частиною домену і представлена ​​цим інтерфейсом.

Інфраструктура

Реалізація зовнішніх залежностей є частиною інфраструктури програми:

Моє рішення
+ My.Product.Core (My.Product.dll)
- My.Product.Infrastructure (My.Product.Infrastructure.dll)
  - Доменні сервіси
      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)
  - Доменні сервіси
      IExchangeRateService
    Товар
    ProductFactory
    IProductRepository
- My.Product.Infrastructure (My.Product.Infrastructure.dll)
  - Прикладні сервіси
      ICache
  - Доменні сервіси
      CachingExchangeRateService
      XEExchangeRateService
    SqlServerProductRepository
- My.Product.WcfService (My.Product.WcfService.dll)
  - Прикладні сервіси
      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інтерфейс? Це концепція домену, тобто щось, що входить у всюдисущу мову Вашого замовника. Інші частини вашого домену можуть залежати від цієї послуги, тому її інтерфейс визначений у доменному шарі. Але оскільки його реалізація передбачає зовнішню веб-службу, клас реалізації знаходиться в інфраструктурному рівні. Таким чином доменний шар стосується лише логіки бізнесу.
Niels van der Rest

4
@Tiendq: У традиційній багатошаровій архітектурі інфраструктура зазвичай є доменною-агностичною. Але в архітектурі Onion (див. Посилання у моїй відповіді) інфраструктура реалізує зовнішні залежності домену. Але я б не сказав, що інфраструктура залежить від домену, вона лише посилається на неї. Я взяв термін "інфраструктура" з лукової архітектури, але "зовнішні" можуть бути кращою назвою.
Нільс ван дер Рест

1
@Derek: Однією з таких «речей» може бути ExchangeRateекземпляр, який містить базову валюту, зустрічну валюту та значення обмінного курсу між цими двома валютами. Ці тісно пов'язані значення представляють поняття "обмінний курс" від домену, тому вони живуть у доменному шарі. Хоча це може здатися простим DTO, в DDD він називається Value Object і може містити додаткову логіку бізнесу для порівняння або перетворення примірників.
Нільс ван дер Рест

6
Я не згоден з тією частиною, де ви не згодні з Віджаєм, і ось чому. CachingExchangeRateService є проблемою інфраструктури. Незважаючи на те, що ви загалом приймаєте ICache, реалізація цього ICache залежить від технології, що займається (наприклад, Web, Windows). Просто тому, що це загальне, це не робить його послугою додатків. Служба додатків - це API вашого домену. Що робити, якщо ви хочете розкрити свій домен іншому, хто пише додаток, що вони використовуватимуть? Служби прикладних програм, і вони можуть не потребувати кешування, тому ваше кешування impl для них марне (тобто, чому це інфраструктура)
Аарон Хокінс

38

Кращий ресурс , який допоміг мені зрозуміти різницю між службою додатків і послугами домену є реалізація Java прикладу вантажу Еріка Еванса, знайшов тут . Якщо ви скачаєте його, ви можете перевірити внутрішні положення RoutingService (служба домену) та BookingService, CargoInspectionService (що є Службами прикладних програм).

Мій «ага» момент був викликаний двома речами:

  • Читаючи опис Послуги за посиланням вище, точніше це речення:

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

  • Читання цієї публікації в блозі , особливо цієї частини:

    Те, що я знаходжу велику допомогу у відділенні яблук від апельсинів, - це думка з точки зору робочого процесу застосування. Вся логіка, що стосується документообігу додатків, як правило, перетворюється на Служби прикладних програм, які входять до рівня додатків, тоді як поняття з домену, які, здається, не входять як модельні об'єкти, в кінцевому підсумку формують одну або кілька Доменних служб.


3
Я згоден, саме так я визначаю Служби програм, і це відповідає всім ситуаціям, з якими я стикався до цього часу. Служби доменів мають справу з усім, що стосується об'єктів домену, але виходять за рамки однієї сутності. Наприклад: BookReferencesService.GetNextAvailableUniqueTrackingNumber (), в центрі уваги чітко ділові правила *. Що стосується Служби прикладних програм, то це саме те, що ви описуєте, більшу частину часу я починаю з введення цього робочого процесу в свої дії контролера, і коли це помічаю, я перетворюю цю логіку в рівень обслуговування додатків. Можна сказати, що цей шар призначений для випадків використання
tobiak777

1
* І такі інтерфейси служб домену споживаються суб'єктами домену.
tobiak777

32

Служба доменів - це розширення домену. Це слід розглядати лише в контексті домену. Це не якась дія користувача, наприклад, закриття акаунта чи щось таке. Служба домену підходить там, де немає штату. Інакше це був би доменний об’єкт. Служба доменів робить щось, що має сенс лише тоді, коли це робиться з іншими співробітниками (об’єктами домену чи іншими службами). І що сенс - це відповідальність іншого шару.

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

Загалом, сховища можуть бути введені в доменні сервіси, але це досить рідкісний сценарій. Саме цей додаток виконує більшу частину часу.


10
"Служба домену підходить там, де немає стану. Інакше це буде доменний об'єкт." зробив це натиснути для мене. Дякую.
Нік

31

З Червоної книги (Реалізація дизайну, керованого доменом, Вон Вернон), я розумію ці поняття:

Об'єкти домену ( сутності та об'єкти цінності ) інкапсулюють поведінку, необхідну для (під) домену, роблячи це природним, виразним та зрозумілим.

Доменні служби інкапсулюють таку поведінку, яка не вкладається в один об'єкт домену. Наприклад, книга бібліотека позичаючи Bookдо Client(з відповідними Inventoryзмінами) , може зробити це від служби домену.

Служби прикладних програм обробляють потік випадків використання, включаючи будь-які додаткові проблеми, необхідні поверх домену. Він часто виставляє такі методи через свій API, для споживання зовнішніми клієнтами. На основі нашого попереднього прикладу наша служба додатків може відкрити метод, LendBookToClient(Guid bookGuid, Guid clientGuid)який:

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

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

Як ви можете сподіватися, що модель домену залишається дуже чистою , і її легко зрозуміти та обговорити з експертами з домену, оскільки вона містить лише власні фактичні проблеми бізнесу. З іншого боку, потоком додатків також набагато простіше керувати, оскільки він позбавляється від проблем домену, стає лаконічним та простим.


2
Я б сказав, що служба застосунків - це також точка вирішення залежностей. Його метод - це випадок використання, єдиний потік, тому він може приймати обгрунтовані рішення щодо конкретних реалізацій для використання. Тут також підходять операції з базою даних.
Тимо

10

Послуги домену: методи, які дійсно не підходять до однієї сутності або потребують доступу до сховища, містяться в службах домену. Рівень сервісу домену також може містити власну логіку домену і є стільки ж частиною доменної моделі, скільки об'єктами та об'єктами цінності.

Служби програм: Служба додатків - це тонкий шар, який розташовується над моделлю домену та координує діяльність програми. Він не містить ділової логіки і не утримує штати жодних суб'єктів; однак, він може зберігати стан трансакції робочого процесу. Ви використовуєте службу Application для надання API в модель домену за допомогою схеми обміну повідомленнями Request-Response.

Міллетт, С (2010). Професійні шаблони дизайну ASP.NET Видавництво Wiley 92.


6

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

  • У вас є 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

Ви можете знайти безліч прикладів Служби прикладних програм та пару Служб домену


Чи обов'язково перевіряти та зберігати об'єкти лише в Службах прикладних програм? Якщо у мене є об'єкти A, B і C, і всі вони пов'язані один з одним (A -> B -> C) і операція над A повинна викликати зміни B і C, викликаючи одну службу домену від іншої, як це зробити?
MrNVK

> Чи обов'язково перевіряти та зберігати об'єкти лише в Прикладних службах? Якщо вам доведеться, то так. У більшості випадків доводиться перевіряти, чи існує ідентифікатор, тому що в іншому випадку ви працюєте над нульовою змінною.
doesnotmatter

1
> Якщо у мене є об'єкти A, B і C і всі вони пов'язані один з одним (A -> B -> C) і операція над A повинна викликати зміни B і C, викликаючи одну службу домену від іншої, як це зробити ? Я не впевнений, що ви маєте на увазі під "викликом однієї доменної служби від іншої", але для реакцій на зміни суб'єкта можна використовувати події або просто упорядкувати його службою додатків на зразок: agregateA.doOperation (), agregateB.doAgether ( ). Шукати: Оркестрація проти хореографії
нерегулярно

Дякую за відповідь! "виклик однієї служби домену від іншого" - я маю на увазі, якщо у мене є складна операція над об'єктом A, то я повинен використовувати ADomainService. Але ця операція, крім сутності A, впливає на сутність B. Операція, яку необхідно виконати на об'єкті B в ADomainService, також є складною. Тому я повинен використовувати BDomainService від ADomainService. Тепер я сумніваюся в такому підході :) Але якщо я вклав цю логіку в ApplicationService, чи не порушив би її інкапсуляцію бізнес-процесів, які повинні бути лише в доменному шарі, а не в додатковому шарі?
MrNVK

Ви можете просто випромінювати подію зі свого доменного сервісу, якщо ви думаєте, що це повинно бути в доменній службі замість служби додатків.
didnotmatter

0

Розгляньте доменну службу як об'єкт, що реалізує бізнес-логіку або правила ведення бізнесу, пов'язані з логікою доменних об'єктів, і ця логіка складно вписатись в одні й ті ж доменні об’єкти, а також не викликає зміни стану доменної служби (служба домену є об'єктом без "стан" або краще без держави, що має ділове значення), але з часом змінюється стан лише об'єктів домену, на яких функціонує.

У той час як Служба застосувань реалізує логіку прикладного рівня як взаємодію з користувачем, перевірку введення, логіку, не пов’язану з бізнесом, а з іншими проблемами: автентифікацією, безпекою, електронною поштою тощо.

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

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

У цьому випадку нам потрібна служба домену, яка виконує конкретну операцію ввімкнення світла, коли хтось відкриває двері із зовнішньої сторони, щоб увійти в приміщення, тому що двері та предмети лампи не можуть реалізувати цю логіку таким чином, який ми вважаємо підходящим. до їх натури .

Ми можемо назвати нашу службу домену , як DomoticDomainServiceі здійснити 2 способами: OpenTheDoorAndTurnOnTheLightі CloseTheDoorAndTurnOffTheLightці 2 методи respectevely зміни стану обох об'єктів Doorі Lampдо open/onі closed/off.

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

Цей приклад, далеко не добре розроблений в реальному світі додатку, має єдину мету (як уже говорилося декілька разів) пояснити, що таке Служба доменів та її відмінність від Прикладного сервісу, сподіваюся, він зрозумілий і корисний.


Чіро: Ви, наприклад, не практичні, і це дуже заплутано.
Мортеза Азізі

Привіт, Мортеса, ви могли б бути більш конкретними? Ви ризикуєте бути лише «судженням» без реальних аргументів. Дякую
Ciro Corvino
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.