Wyjątki w DDD

12

Uczę się DDD i myślę o rzucaniu wyjątków w określonych sytuacjach. Rozumiem, że obiekt nie może wejść w zły stan, więc tutaj wyjątki są w porządku, ale w wielu przykładach wyjątki są zgłaszane na przykład, jeśli próbujemy dodać nowego użytkownika z istniejącą pocztą e-mail w bazie danych.

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()
        )
    );
}

Jeśli więc użytkownik z tym adresem e-mail istnieje, możemy w usłudze aplikacji wykryć wyjątek, ale nie powinniśmy kontrolować działania aplikacji za pomocą bloku try-catch.

Jak jest na to najlepszy sposób?

yadiv
źródło
1
Posiadanie usług domenowych (procedur obsługi) do zgłaszania wyjątków jest w porządku.
Andy

Odpowiedzi:

21

Zacznijmy od krótkiego przeglądu przestrzeni problemowej: jednym z podstawowych zasad DDD jest umieszczenie reguł biznesowych jak najbliżej miejsc, w których należy je egzekwować. Jest to niezwykle ważna koncepcja, ponieważ sprawia, że ​​twój system jest bardziej „spójny”. Przesunięcie reguł „w górę” jest zazwyczaj oznaką modelu anemicznego; gdzie obiekty są tylko workami danych, a reguły są wprowadzane z tymi danymi, które mają być egzekwowane.

Anemiczny model może mieć wiele sensu dla programistów rozpoczynających pracę z DDD. Tworzysz Usermodel i EmailMustBeUnqiueRuledostaje się do niego niezbędne informacje, aby sprawdzić poprawność wiadomości e-mail. Prosty. Elegancki. Problem polega na tym, że ten „sposób myślenia” ma zasadniczo charakter proceduralny. Nie DDD. Ostatecznie dzieje się tak, że masz moduł z dziesiątkami Rulesstarannie zapakowanych i zamkniętych, ale są one całkowicie pozbawione kontekstu do tego stopnia, że ​​nie można ich już zmienić, ponieważ nie jest jasne, kiedy / gdzie są egzekwowane. Czy to ma sens? To może być oczywiste, że EmailMustBeUnqiueRulezostaną zastosowane na utworzenie grupy roboczej User, ale co UserIsInGoodStandingRule?. Powoli, ale pewnie ziarnistość ekstrakcjiRulesz ich kontekstu pozostawia cię system, który jest trudny do zrozumienia (i dlatego nie można go zmienić). Reguły powinny być enkapsulowane tylko wtedy, gdy faktyczne chrupanie / wykonywanie jest tak szczegółowe, że Twój model zaczyna tracić koncentrację.

Teraz do konkretnego pytania: Problem z posiadające Service/ CommandHandlerrzucić Exceptionto, że logika biznesowa zaczyna przeciekać ( „w górę”) z danej domeny. Dlaczego Twój Service/ CommandHandlermusisz znać adres e-mail musi być unikalny? Warstwa usługi aplikacji jest zwykle używana raczej do koordynacji niż do implementacji. Przyczynę tego można zilustrować po prostu, jeśli dodamy ChangeEmailmetodę / polecenie do twojego systemu. ZARÓWNO metody / moduły obsługi poleceń będą musiały zawierać unikatowy test. To tutaj deweloper może ulec pokusie „wyodrębnienia” an EmailMustBeUniqueRule. Jak wyjaśniono powyżej, nie chcemy iść tą drogą.

Pewne dodatkowe trudności w zdobywaniu wiedzy mogą prowadzić do odpowiedzi na więcej DDD. Wyjątkowość wiadomości e-mail jest niezmiennikiem, który należy wymusić w całej kolekcji Userobiektów. Czy w Twojej domenie istnieje koncepcja reprezentująca „kolekcję Userobiektów”? Myślę, że prawdopodobnie widzisz, dokąd idę.

W tym konkretnym przypadku (i wielu innych, obejmujących egzekwowanie niezmienników w różnych kolekcjach), najlepsze miejsce na wdrożenie tej logiki będzie w twoim Repository. Jest to szczególnie wygodne, ponieważ Repository„wiesz” dodatkową infrastrukturę niezbędną do przeprowadzenia tego rodzaju weryfikacji (magazyn danych). W twoim przypadku umieszczę to sprawdzenie w addmetodzie. To ma sens, prawda? Koncepcyjnie jest to ta metoda, która naprawdę dodaje Userdo twojego systemu. Magazyn danych jest szczegółem implementacji.

zjeżdżalnia królewska
źródło
Czy mogę zapytać, co masz na myśli Moving rules "upwards" is generally a sign of an anemic model? W górę oznacza warstwę aplikacji? lub do modelu domeny?
Anyname Donotcare
1
„W górę” oznacza w kierunku warstwy aplikacji (pomyśl o architekturze warstwowej). Dotknąłem tego powyżej, ale powodem, dla którego przesuwanie reguł w górę jest oznaką modelu anemicznego, jest to, że reprezentuje on oddzielenie danych i zachowania w sposób, który nie spełnia całego celu posiadania podstawowego modelu domeny. Jeśli sprawdzanie poprawności wiadomości e-mail znajduje się w osobnym module niż wysyłanie wiadomości e-mail, Emailmodel musi stanowić worek danych, który przed wysłaniem jest sprawdzany pod kątem niezmienników. Zobacz: softwareengineering.stackexchange.com/questions/372338/...
king-side-slide
2
Przeniesienie logiki domeny do repozytorium jest nieprawidłowe. powinieneś wymusić regułę domeny poprzez zagregowanie katalogu głównego w warstwie domeny.
Arash,
2
Sprawdzanie poprawności zestawu @Arash jest trudne i często nie działa dobrze z DDD. Pozostaje Ci możliwość sprawdzenia, czy jakiś proces będzie działał przed przystąpieniem do tego procesu (dodanie a User), lub zaakceptowanie faktu, że ten konkretny niezmiennik nie zostanie starannie zamknięty w domenie. Pierwsza z nich reprezentuje rozdzielenie danych i zachowania, co skutkuje paradygmatem proceduralnym. To mniej niż idealne. Walidacja powinna istnieć z danymi, a nie wokół danych. Co więcej, jaką korzyść daje utworzenie usługi domeny, która służy tylko do sprawdzania tej jednej reguły? Pozornie ta usługa ...
zjeżdżalnia królewska
2
@Arash łączy twoje Repository, Useri to niezmienne i dlatego nie osiąga purystycznej wizji, do której i tak dążysz . Implementacje repozytoriów są częścią domeny, dlatego można im powierzyć pewne obowiązki.
king-side-slide
0

Możesz narzucić weryfikację na każdej warstwie aplikacji. Gdzie narzucić, które reguły zależą od twojej aplikacji.

Na przykład jednostki implementują metody reprezentujące reguły biznesowe, podczas gdy przypadki użycia implementują reguły specyficzne dla aplikacji.

Jeśli budujesz usługę e-mail, taką jak Gmail, można argumentować, że reguła „użytkownicy powinni mieć unikalny adres e-mail” to reguła biznesowa.

Jeśli budujesz proces rejestracji dla nowego systemu CRM, ta reguła prawdopodobnie najlepiej będzie zaimplementowana w przypadku użycia, ponieważ reguła ta prawdopodobnie również będzie częścią przypadku użycia. Ponadto testowanie może być łatwiejsze, ponieważ w testach przypadków użycia można łatwo zlikwidować wywołania repozytorium.

Dla dodatkowego bezpieczeństwa możesz również wymusić tę regułę w swoim repozytorium.

psujący rozgrywkę
źródło