Walidacje projektu w warstwie modelu domeny

Napiwek

Ta zawartość jest fragmentem książki eBook, architektury mikrousług platformy .NET dla konteneryzowanych aplikacji platformy .NET dostępnych na platformie .NET Docs lub jako bezpłatnego pliku PDF, który można odczytać w trybie offline.

.NET Microservices Architecture for Containerized .NET Applications eBook cover thumbnail.

W DDD reguły walidacji można traktować jako niezmienne. Główną obowiązkiem agregacji jest wymuszenie 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 powinny być zawsze 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 niezmienności jest obowiązkiem jednostek domeny (zwłaszcza zagregowanego katalogu głównego), a obiekt jednostki nie powinien być w stanie istnieć bez sprawdzania poprawności. Niezmienne reguły są po prostu wyrażane jako kontrakty, a wyjątki lub powiadomienia są zgłaszane, gdy zostaną naruszone.

Przyczyną 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 sprawdzimy to ponownie? Lub bardziej prawdopodobne ... po prostu nie przeszkadza ci sprawdzenie i "nadzieja na najlepsze" — masz nadzieję, że ktoś przeszkadza, aby go zweryfikować przed wysłaniem go do Ciebie. Oczywiście przy użyciu TDD jednym z pierwszych testów powinniśmy napisać jest to, że jeśli wyślem klienta z nazwą null, że powinien zgłosić błąd. Ale kiedy zaczniemy pisać tego rodzaju testy w kóło, zdajemy sobie sprawę ... "Co zrobić, jeśli nigdy nie zezwoliliśmy na wartość null nazwy? nie mielibyśmy wszystkich tych testów!".

Implementowanie walidacji w warstwie modelu domeny

Walidacje są zwykle implementowane w konstruktorach jednostek 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 powiadamiania w celu zwrócenia kolekcji błędów zamiast zwracania wyjątku 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 jest nieprawidłowy.

Podobne podejście można użyć w konstruktorze jednostki, podnosząc 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 atrybuty, 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 weryfikacji jednostek w programie EF Core (ani IValidatableObject.Validate metody), ponieważ zostały wykonane od ef 4.x w programie .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 i jest to element MVC lub interfejs API nie dotyczy modelu domeny.

Po wyjaśnieniu różnicy koncepcyjnej można nadal używać adnotacji danych i IValidatableObject w klasie jednostki do weryfikacji, jeśli akcje otrzymują parametr obiektu klasy jednostki, co nie jest zalecane. W takim przypadku walidacja nastąpi 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 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 weryfikacji IValidatableObject jednostek jest widoczna w tym komentarzu w witrynie 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 pochylić 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 wykonywać tej czynności 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 użycie wzorca powiadomień do stosu i zwrócenie 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 projektowania opartego na domenie Vaughn Vernon omawia je w sekcji dotyczącej walidacji.

Weryfikacja dwuetapowa

Rozważ również weryfikację dwuetapową. Użyj walidacji na poziomie pola polecenia Obiekty transferu danych (DTO) i walidacji 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 się 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