Винятки в DDD


12

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

public function doIt(UserData $userData)
{
    $user = $this->userRepository->byEmail($userData->email());
    if ($user) {
        throw new UserAlreadyExistsException();
    }

    $this->userRepository->add(
        new User(
            $userData->email(),
            $userData->password()
        )
    );
}

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

Як найкращий спосіб для цього?


1
Маючи винятковий сервіс домену (обробників), викидайте винятки, це чудово.
Енді

Відповіді:


21

Почнемо це з короткого огляду проблемного простору: Однією з основних принципів DDD є розміщення правил бізнесу якомога ближче до місць, де їх потрібно виконувати. Це надзвичайно важлива концепція, оскільки вона робить вашу систему "більш згуртованою". Переміщення правил «вгору», як правило, є ознакою анемічної моделі; де об'єкти - це лише пакети даних, і правила вводяться в ці дані для виконання.

Анемічна модель може створити багато сенсу розробникам, тільки починаючи з DDD. Ви створюєте Userмодель та модель, EmailMustBeUnqiueRuleяка отримує необхідну інформацію для перевірки електронної пошти. Простий. Елегантний. Питання в тому, що такий "вид" мислення має принципово процедурний характер. Не DDD. Що в кінцевому підсумку відбувається, це те, що вам залишається модуль з десятками Rulesакуратно загорнутих і інкапсульованих, але вони повністю позбавлені контексту до того, що їх більше не можна змінити, оскільки не ясно, коли / де вони застосовуються. Чи має це сенс? Це може бути само собою зрозумілим , що EmailMustBeUnqiueRuleбуде застосовуватися на створення User, але як UserIsInGoodStandingRule?. Повільно, але впевнено відбувається грануляризація вилученняRulesпоза їх контексту залишає вас систему, яку важко зрозуміти (і тому її неможливо змінити). Правила слід інкапсулювати лише тоді, коли фактична хрускіт / виконання настільки багатослівна, що ваша модель починає втрачати фокус.

Тепер перейдемо до вашого конкретного питання: Проблема з Service/ CommandHandlerкиданням Exceptionполягає в тому, що ваша логіка бізнесу починає просочуватися ("вгору") з вашого домену. Чому ваш Service/ CommandHandlerпотрібно знати електронний лист повинен бути унікальним? Службовий рівень додатків зазвичай використовується для координації, а не для реалізації. Причину цього можна проілюструвати просто, якщо ми додамо ChangeEmailметод / команду до вашої системи. Тепер методи BOTH / обробники команд повинні включати ваш унікальний чек. Ось де розробник може спокуситись "витягти" EmailMustBeUniqueRule. Як пояснено вище, ми не хочемо йти цим маршрутом.

Деякі додаткові скорочення знань можуть призвести до більш відповіді DDD. Унікальність електронної пошти - це інваріант, який потрібно застосовувати через колекцію Userоб'єктів. Чи є у вашому домені концепція, яка представляє "колекцію Userоб'єктів"? Я думаю, ти, певно, можеш побачити, куди я сюди їду.

У цьому конкретному випадку (і багато інших із залученням інваріантів у колекціях) найкраще місце для реалізації цієї логіки буде у вашому Repository. Це особливо зручно, оскільки ви Repositoryтакож "знаєте" додатковий фрагмент інфраструктури, необхідний для виконання такого виду перевірки (сховище даних). У вашому випадку я би помістив цю перевірку в addметод. Це має сенс правильно? Концептуально саме цей метод справді додає Userвашій системі. Зберігання даних - це деталь реалізації.


Чи можу я запитати, що ти маєш на увазі Moving rules "upwards" is generally a sign of an anemic model? Вгору означає до рівня додатків? або до доменної моделі?
Anyname Donotcare

1
"Вгору" означає ваш рівень програми (подумайте про шарувату архітектуру). Я торкнувся цього вище, але причина переміщення правил вгору є ознакою анемічної моделі в тому, що вона представляє поділ даних і поведінки таким чином, що перемагає всю мету створення основної доменної моделі. Якщо перевірка електронної пошти знаходиться в окремому модулі, ніж надсилання електронної пошти, ваша Emailмодель повинна бути пакетом даних, який перевіряється на інваріанти перед відправкою. Дивіться: softwareengineering.stackexchange.com/questions/372338/…
king-side-slide

2
Переміщення логіки домену до сховища неправильно. слід застосувати правило домену за допомогою сукупного кореня в доменному шарі.
Араш

2
@Arash Набір перевірки складний і часто не грає добре з DDD. Вам залишається або вирішити перевірити, що якийсь процес буде працювати, перш ніж спробувати цей процес (додавши а User), або визнаєте, що цей специфічний вид інваріанта не буде чітко інкапсульований у вашому домені. Перший являє собою розділення даних та поведінки, що призводить до процесуальної парадигми. Це менше, ніж ідеально. Перевірка повинна існувати з даними, а не навколо даних. Крім того, яка користь у створенні служби домену, яка служить лише для перевірки цього одного правила? Нібито, ця послуга ...
ковзання в сторону

2
@Arash з'єднує ваше Repository, Userі це інваріантне, і тому не досягає пуристського бачення, на яке ви все-таки наполягаєте. Реалізація сховища є частиною домену, і тому їм може бути надано певні обов'язки.
король-бік-слайд

0

Ви можете встановити перевірку на кожному шарі програми. Де накласти, які правила залежать від вашої заявки.

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

Якщо ви будуєте службу електронної пошти, як-от Gmail, можна стверджувати, що правило "користувачі повинні мати унікальну електронну пошту" - це бізнес-правило.

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

З метою додаткової безпеки ви також можете застосувати це правило у своєму сховищі.

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