Дизайн, керований доменом - зовнішні залежності в проблемі Entity


23

Я хотів би запустити дизайн, керований доменом, але є кілька проблем, які я хотів би вирішити перед початком :)

Давайте уявимо, що у мене є групи та користувачі, і коли користувач хоче приєднатися до групи, я викликаю groupsService.AddUserToGroup(group, user)метод. У DDD я повинен зробити group.JoinUser(user), що виглядає досить непогано.

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

Прикладом може бути - обмеження, що користувач може брати участь лише у максимум 3 групах. Для цього знадобиться DB-дзвінки зсередини group.JoinUser для перевірки цього.

Але той факт, що суб'єкт господарювання залежить від деяких зовнішніх служб / класів, не здається мені таким хорошим і "природним".

Який правильний спосіб впоратися з цим у DDD?

Відповіді:


15

Давайте уявимо, що у мене є групи та користувачі, і коли користувач хоче приєднатись до групи, я викликаю метод groupsService.AddUserToGroup (group, user). У DDD я повинен робити group.JoinUser (користувач), який виглядає досить непогано.

Однак DDD також рекомендує використовувати сервіси (без громадянства) для виконання завдань, якщо завдання під рукою занадто складне або не вписується в модель сутності. Добре мати послуги в доменному шарі. Але послуги в доменному рівні повинні включати лише логіку бізнесу. Зовнішні завдання та логіка додатків (наприклад, надсилання електронного листа), з іншого боку, повинні використовувати службу домену на рівні додатків, у якій, наприклад, можна мати окрему службу (додаток).

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

Правила перевірки дійсно належать доменної моделі! Вони повинні бути капсульовані всередині об'єктів домену (сутностей тощо).

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

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

Але той факт, що суб'єкт господарювання залежить від деяких зовнішніх служб / класів, не здається мені таким хорошим і "природним".

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

Який правильний спосіб впоратися з цим у DDD?

У вашому випадку відносини, ймовірно, повинні бути двосторонніми. Чи приєднається до групи користувач, або група приймає користувача, залежить від вашого домену. Користувач приєднується до групи? Або користувач додається до групи? Як це працює у вашому домені?

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

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

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


Але якщо ми рухаємо стільки логіки поза сутністю, що слід зберігати всередині?
SiberianGuy

Прямі обов'язки суб'єкта господарювання! Якщо ви можете сказати, наприклад, "користувач може приєднатися до групи", це відповідальність суб'єкта користувача. Іноді доводиться приймати компромісні рішення з технічних причин. Я також не є великим прихильником двосторонніх відносин, але іноді це найкраще підходить до моделі. Тому уважно слухайте, коли говорите про домен. "Суб'єкт робить ..." "Суб'єкт може ..." Коли ви чуєте такі пропозиції, то ці операції, швидше за все, належать сутності.
Сокіл

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

1
Дякую за вашу відповідь, Сокіле! До речі, я завжди намагався використовувати послуги без громадянства, тому я на крок ближче до DDD :) Скажімо, що в домені ця операція UserJoinsToGroup належить до групи. Проблема полягає в тому, що для перевірки цієї операції мені потрібно знати, у скільки груп учасник вже бере участь (відмовити в операції, якщо це вже> 3). Щоб знати, що мені потрібно запитувати базу даних. Як я можу це зробити з юридичної особи Групи? У мене є ще кілька прикладів, коли мені потрібно торкнутися БД в операціях, які природно повинні належати сутності (я опублікую їх, якщо потрібно :))
Shaddix

2
Що ж, якщо я подумаю над цим: а що з організацією GroupMembership? Вона може бути сконструйована фабрикою, і ця фабрика може отримати доступ до сховищ. Це було б добре DDD і інкапсулює створення членства. Фабрика може отримувати доступ до сховищ, створює членство та замість цього додавати його користувачеві та групі відповідно. Ця нова організація також може інкапсулювати привілеї. Можливо, це гарна ідея.
Сокіл

3

Я б підійшов до проблеми перевірки таким чином: Створіть службу домену під назвою MembershipService:

class MembershipService : IMembershipService
{
   public MembershipService(IGroupRepository groupRepository)
   { 
     _groupRepository = groupRepository;
   }
   public int NumberOfGroupsAssignedTo(UserId userId)
   {
        return _groupsRepository.NumberOfGroupsAssignedTo(userId);
   }
}

Суб'єкту Групи потрібно вводити IMemberShipService. Це можна зробити на рівні класу або на рівні методу. Припустимо, ми робимо це на рівні методу.

class Group{

   public void JoinUser(User user, IMembershipService membershipService)
   {
       if(membershipService.NumberOfGroupsAssignedTo(user.UserId) >= 3)
         throw new BusinessException("User assigned to more than 3 groups. Cannot proceed");

       // do some more stuff
   }
}

Служба додатків: GroupServiceможе бути введена за IMemberShipServiceдопомогою інжектора Constructor, який потім може перейти до JoinUserметоду Groupкласу.


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