Walidacje projektu w warstwie modelu domeny
Porada
Ta zawartość jest fragmentem książki eBook, architektury mikrousług platformy .NET dla konteneryzowanych aplikacji .NET, dostępnym na platformie .NET Docs lub jako bezpłatny plik PDF do pobrania, który można odczytać w trybie offline.
W DDD reguły walidacji mogą być uważane za niezmienne. Główną obowiązkiem agregacji jest wymuszanie niezmiennych zmian stanu dla wszystkich jednostek w ramach tej agregacji.
Jednostki domeny powinny zawsze być prawidłowymi jednostkami. Istnieje pewna liczba niezmiennych obiektów, które zawsze powinny być prawdziwe. Na przykład obiekt elementu zamówienia zawsze musi mieć ilość, która musi być dodatnią liczbą całkowitą oraz nazwą artykułu i ceną. W związku z tym wymuszanie niezmiennych jest obowiązkiem jednostek domeny (zwłaszcza z zagregowanego katalogu głównego), a obiekt jednostki nie powinien być w stanie istnieć bez bycia prawidłowym. Niezmienne reguły są po prostu wyrażane jako kontrakty, a wyjątki lub powiadomienia są zgłaszane w przypadku ich naruszenia.
Uzasadnieniem jest to, że wiele usterek występuje, ponieważ obiekty są w stanie, w których nigdy nie powinny znajdować się.
Zaproponujmy teraz usługę SendUserCreationEmailService, która przyjmuje element UserProfile ... jak możemy racjonalizować w tej usłudze, że nazwa nie ma wartości null? Czy sprawdzamy to ponownie? Lub bardziej prawdopodobne ... Po prostu nie przeszkadza, aby sprawdzić i "nadzieję na najlepsze" — masz nadzieję, że ktoś przeszkadza, aby zweryfikować go przed wysłaniem go do Ciebie. Oczywiście przy użyciu TDD jeden z pierwszych testów powinniśmy napisać jest to, że jeśli wysyłam klienta z nazwą null, która powinna zgłosić błąd. Ale kiedy zaczniemy pisać tego rodzaju testy w kółka, zdajemy sobie sprawę ... "Co zrobić, jeśli nigdy nie pozwolono na nadenie nazwy wartości null? nie mielibyśmy wszystkich tych testów!".
Implementowanie walidacji w warstwie modelu domeny
Walidacje są zwykle implementowane w konstruktorach jednostki domeny lub w metodach, które mogą aktualizować jednostkę. Istnieje wiele sposobów implementowania walidacji, takich jak weryfikowanie danych i zgłaszanie wyjątków w przypadku niepowodzenia walidacji. Istnieją również bardziej zaawansowane wzorce, takie jak używanie wzorca specyfikacji na potrzeby walidacji, oraz wzorzec powiadomień, aby zwrócić kolekcję błędów zamiast zwracać wyjątek dla każdej weryfikacji w miarę jego wystąpienia.
Weryfikowanie warunków i zgłaszanie wyjątków
Poniższy przykład kodu przedstawia najprostsze podejście do weryfikacji w jednostce domeny przez zgłoszenie wyjątku. W tabeli odwołań na końcu tej sekcji można wyświetlić linki do bardziej zaawansowanych implementacji na podstawie omówionych wcześniej wzorców.
public void SetAddress(Address address)
{
_shippingAddress = address?? throw new ArgumentNullException(nameof(address));
}
Lepszym przykładem jest potrzeba zapewnienia, że stan wewnętrzny nie uległ zmianie lub że wystąpiły wszystkie mutacje metody. Na przykład następująca implementacja pozostawi obiekt w nieprawidłowym stanie:
public void SetAddress(string line1, string line2,
string city, string state, int zip)
{
_shippingAddress.line1 = line1 ?? throw new ...
_shippingAddress.line2 = line2;
_shippingAddress.city = city ?? throw new ...
_shippingAddress.state = (IsValid(state) ? state : throw new …);
}
Jeśli wartość stanu jest nieprawidłowa, pierwszy wiersz adresu i miasto zostały już zmienione. Może to spowodować, że adres będzie nieprawidłowy.
Podobne podejście może być używane w konstruktorze jednostki, co zgłasza wyjątek, aby upewnić się, że jednostka jest prawidłowa po jej utworzeniu.
Używanie atrybutów weryfikacji w modelu na podstawie adnotacji danych
Adnotacje danych, takie jak Wymagane lub MaxLength, mogą służyć do konfigurowania właściwości pól bazy danych platformy EF Core, jak wyjaśniono szczegółowo w sekcji Mapowanie tabel, ale nie działają już na potrzeby walidacji jednostki w programie EF Core (IValidatableObject.Validateani metody), jak to miało miejsce od czasu ef 4.x w .NET Framework.
Adnotacje danych i IValidatableObject interfejs mogą być nadal używane do walidacji modelu podczas tworzenia powiązania modelu, przed wywołaniem akcji kontrolera w zwykły sposób, ale ten model ma być modelem ViewModel lub DTO, a to mvC lub interfejs API nie dotyczy modelu domeny.
Po jasnej różnicy koncepcyjnej można nadal używać adnotacji danych i IValidatableObject w klasie jednostki do walidacji, jeśli akcje otrzymają parametr obiektu klasy jednostki, co nie jest zalecane. W takim przypadku walidacja zostanie przeprowadzona po powiązaniu modelu, tuż przed wywołaniem akcji i można sprawdzić właściwość ModelState.IsValid kontrolera, aby sprawdzić wynik, ale następnie ponownie, dzieje się to w kontrolerze, a nie przed utrwalaniem obiektu jednostki w obiekcie DbContext, tak jak to miało miejsce od ef 4.x.
Nadal można zaimplementować niestandardową walidację w klasie jednostki przy użyciu adnotacji danych i IValidatableObject.Validate metody, przesłaniając metodę SaveChanges obiektu DbContext.
Przykładowa implementacja sprawdzania poprawności IValidatableObject jednostek jest widoczna w tym komentarzu na temat GitHub. Ten przykład nie wykonuje weryfikacji opartych na atrybutach, ale powinien być łatwy do zaimplementowania przy użyciu odbicia w tym samym zastąpieniu.
Jednak z punktu widzenia DDD model domeny najlepiej jest chylić się przy użyciu wyjątków w metodach zachowania jednostki lub przez zaimplementowanie wzorców specyfikacji i powiadomień w celu wymuszania reguł walidacji.
Warto używać adnotacji danych w warstwie aplikacji w klasach ViewModel (zamiast jednostek domeny), które będą akceptowały dane wejściowe, aby umożliwić walidację modelu w warstwie interfejsu użytkownika. Nie należy jednak tego robić w przypadku wykluczenia walidacji w modelu domeny.
Zweryfikuj jednostki, implementując wzorzec specyfikacji i wzorzec powiadomień
Na koniec bardziej rozbudowane podejście do implementowania walidacji w modelu domeny polega na zaimplementowaniu wzorca specyfikacji w połączeniu ze wzorcem powiadomień, jak wyjaśniono w niektórych dodatkowych zasobach wymienionych w dalszej części.
Warto wspomnieć, że można również użyć tylko jednego z tych wzorców — na przykład weryfikacji ręcznie za pomocą instrukcji sterowania, ale przy użyciu wzorca powiadomień do stosu i zwrócenia listy błędów walidacji.
Używanie walidacji odroczonej w domenie
Istnieją różne podejścia do obsługi odroczonych weryfikacji w domenie. W swojej książce Implementowanie Domain-Driven Design Vaughn Vernon omawia je w sekcji dotyczącej walidacji.
Weryfikacja dwuetapowa
Należy również rozważyć weryfikację dwuetapową. Użyj walidacji na poziomie pola w poleceniu Obiekty transferu danych (DTO) i weryfikacji na poziomie domeny wewnątrz jednostek. Można to zrobić, zwracając obiekt wynikowy zamiast wyjątków, aby ułatwić radzenie sobie z błędami walidacji.
Używanie walidacji pola z adnotacjami danych, na przykład nie duplikuje definicji walidacji. Jednak wykonanie może być zarówno po stronie serwera, jak i po stronie klienta w przypadku obiektów DTO (na przykład poleceń i modelu ViewModels).
Dodatkowe zasoby
Rachel Appel. Wprowadzenie do weryfikacji modelu w ASP.NET Core MVC
https://docs.microsoft.com/aspnet/core/mvc/models/validationRick Anderson. Dodawanie walidacji
https://docs.microsoft.com/aspnet/core/tutorials/first-mvc-app/validationMartin Fowler. Zastępowanie zgłaszania wyjątków powiadomieniem w walidacji
https://martinfowler.com/articles/replaceThrowWithNotification.htmlWzorce specyfikacji i powiadomień
https://www.codeproject.com/Tips/790758/Specification-and-Notification-PatternsLev Gorodinski. Walidacja w projekcie Domain-Driven (DDD)
http://gorodinski.com/blog/2012/05/19/validation-in-domain-driven-design-ddd/Colin Jack. Walidacja modelu domeny
https://colinjack.blogspot.com/2008/03/domain-model-validation.htmlJimmy Bogard. Walidacja w świecie DDD
https://lostechies.com/jimmybogard/2009/02/15/validation-in-a-ddd-world/