DDD - агрегований корінь з великою кількістю дітей


10

Я виступлю з цим питанням, кажу, що я відносно новий в DDD, тому я можу тут робити деякі принципові помилки!

Я працюю над проектом, який включає поняття Рахунки та операції (у фінансовому розумінні). В обліковому записі може бути введено багато транзакцій.

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

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

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

Як люди вирішують подібну ситуацію? Ви просто приймаєте наслідки пам'яті та продуктивності завантаження потенційно величезної кількості дітей для сукупного кореня?

Відповіді:


9

Я рекомендую бути обережними з правилом "не можу існувати без" . Це говорить про концепцію складу в дизайні UML / OO і, можливо, був одним із встановлених підходів до проектування агрегатів у оригінальній синій книзі DDD (не впевнений у цьому), але з цього часу значною мірою був переглянутий. Можливо, буде кращою ідеєю побачити ваші агрегати з точки зору межі транзакційної консистенції .

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

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


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

7

tl; dr - порушуйте правила, якщо вам потрібно. DDD не може вирішити всі проблеми; насправді об'єктні ідеї, які вона дає, - це добрі поради та хороший старт, але справді поганий вибір для деяких бізнес-проблем. Вважайте підказкою, як робити речі.


Що стосується завантаження всіх дітей (транзакції) з батьком (акаунт) - Схоже, ви натрапили на проблему n + 1 (щось для google), яку вирішили багато ORM.

Ви можете вирішити це за допомогою ледачого завантаження дітей (транзакції) - лише за потреби.

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

Щоб "приховати" ці дані, щоб користуватися ними міг тільки Обліковий запис, вам не потрібно було б навіть зберігати їх там, де хто-небудь інший ні як не десаріалізувати їх, як публічна реляційна таблиця. Ви можете зберігати його з "документом" рахунку в БД документа. У будь-якому випадку, якщо хтось досить старався, вони все ще могли побачити дані. І «працюй» з цим. А коли ти не дивишся, вони будуть!

Таким чином, ви можете налаштувати дозволи, але тоді ви повинні запустити "рахунок" як окремий процес.

Тут ви дійсно розумієте, що DDD та чисте використання об’єктної моделі іноді повернуть вас у кут. Правду кажучи, звичайно, вам не доведеться використовувати "склад" / агрегатний корінь, щоб скористатися принципами дизайну DDD. Це лише одне, що ви можете використовувати, коли у вас є ситуація, яка відповідає її обмеженням.

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

Справжня відповідь - почати вставати SOA. На моєму робочому місці ми переглянули відеоролики Уді Дахана "Розподілені обчислення" та придбали nServiceBus (тільки наш вибір). Створіть службу для облікових записів - з власним процесом, чергами повідомлень, доступом до бази даних відносин, яку вона бачить, і ... віола, ви можете жорстко кодувати операції SQL у програмі і навіть закидати пару скриптів транзакцій Cobol (жартує) звичайно), але серйозно є більше проблем, ніж найрозумніший сноб OO / Java, про який могли мріяти.

Я б рекомендував добре моделювати його; Ви можете просто отримати переваги сукупного кореня тут без проблем, розглядаючи послугу як міні-обмежений контраст.

Це, звичайно, має і недолік. Ви не можете просто RPC (веб-сервіс, SOAP або REST) ​​в сервісах і поза ними, а також між ними, або ви отримуєте SOA антипатерн під назвою "вузол" через тимчасове з'єднання. Ви повинні використовувати схему інверсії комунікацій, яка називається "Pub-Sub", яка подібно до цього обробляє події та рейдери подій, але (1) між процесами (які ви можете поставити на окремих машинах, якщо вони перевантажуються на одній).

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

Книга "Шаблони SOA" Арнона Ротема-Галь-Оза відповідає на багато питань з цього приводу. Включаючи використання "активної схеми обслуговування", щоб періодично копіювати дані із сторонніх служб на власні, коли виникає потреба (багато RPC було б потрібно, або посилання ненадійне / немає в екосистемі публікації / підписки).

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

Зрештою, не всі речі можна належним чином класифікувати в єдину службу. Світ не працює за кодом равіолі! Тож вам доведеться порушувати правила. Навіть якщо вам ніколи не доведеться, нові розробники проекту будуть робити це, коли ви його залишите. Але не хвилюйтеся, якщо ви зробите все, що можете, 85%, які дотримуються правил, зроблять програму набагато більш досяжною.

Ого, це було довго.


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