Internal konstruktor a testowanie

1

Czytałem niedawno: https://enterprisecraftsmanship.com/posts/domain-model-purity-completeness/
tldr: model domenowy ma trzy cechy: kompletność, wydajność, czystość. Jednocześnie możemy mieć co najwyżej 2 z tych 3 atrybutów.

Myślałem, że trochę oszukam i osiągnę wszystkie 3 cechy przez serwisy domenowe: warstwa aplikacji nie miałaby dostępu bezpośrednio do encji, ale do serwisów domenowych i cała logika biznesowa zostałaby zamknięta w jednym projekcie z jakimś publicznym API.

2
nobody01 napisał(a):

No mam regułę biznesową, że w systemie nie może istnieć 2 klientów z tym samym NIPem. Weryfikację tej reguły chciałbym mieć w warstwie logiki biznesowej, a nie w warstwie aplikacji, dlatego zrobiłem CustomerFactory a konstruktor Customera ustawiłem na internal, żeby warstwa aplikacja nie mogła sobie od tak tworzyć tych klientów jak jej się podoba.

Mam wrażenie, że sama logika biznesowa nie jest w stanie tego zrobić, ale mniejsza.
Dlaczego CustomerFactory? Dlaczego nie NipUniquenessValidator? Albo dlaczego konstruktor Customer nie przyjmuje obiektu klasy UniqueNip pochodzącego z jakiegoś tam serwisu aplikacyjnego odpowiedzialnego za dostarczanie prawidłowych NIPów?

Tworzenie kontrahentów to niezbyt jaskrawy przykład, więc weźmy np. tworzenie zamówień. W pierwszym lepszym ERP jest z 10 ścieżek tworzenia zamówienia. Chciałbym mieć logikę związaną z tworzeniem zamówień w projekcie domenowym, żeby mi było łatwiej weryfikować reguły biznesowe i dodawać nowe przypadki użycia. Nie chciałbym wypuszczać logiki biznesowej do warstwy aplikacji.

Dobrze, a co to znaczy "logika związana z tworzeniem zamówień"? Pobranie jakichś danych ze źródła, aby takie zamówienie utworzyć, to jest "logika związana" czy już nie?

nobody01 napisał(a):

Eh, to ja już naprawdę nie wiem. Kiedy mam robić te serwisy domenowe?

Przez ostatnie 8 lat w pracy nie zrobiłem żadnego, więc obstawiam, że to się dzieje w weekendy. ;)

Unikalność NIPu to warunek techniczny, OK. No to powiedzmy, że mam wymaganie, żeby się nie dało stworzyć zamówienia dla kontrahenta, jeśli kontrahent ma już zamówienia będące w trakcie na kwotę łączną większą niż 100 000. To też sprawdzać w warstwie aplikacji?

A w encji kontrahenta nie da rady?

0

@somekind:

somekind napisał(a):

Dlaczego CustomerFactory? Dlaczego nie NipUniquenessValidator? Albo dlaczego konstruktor Customer nie przyjmuje obiektu klasy UniqueNip pochodzącego z jakiegoś tam serwisu aplikacyjnego odpowiedzialnego za dostarczanie prawidłowych NIPów?

To ciekawe, na to bym nie wpadł.

Dobrze, a co to znaczy "logika związana z tworzeniem zamówień"? Pobranie jakichś danych ze źródła, aby takie zamówienie utworzyć, to jest "logika związana" czy już nie?

Miałem na myśli jakąś "domenową logikę" typu generowanie numerów zamówień, wyliczanie cen, rabatów itd.

Przez ostatnie 8 lat w pracy nie zrobiłem żadnego, więc obstawiam, że to się dzieje w weekendy. ;)

Akurat ten wątek założyłem pod wpływem kodu napisanego w weekend :D

A w encji kontrahenta nie da rady?

No OK, Customer może mieć jakieś pole OrderInProgressTotalPrice aktualizowane przy każdym złożeniu zamówienia. Albo ewentualnie mogę w CustomerRepository pobrać zamówienia danego kontrahenta i ustawić to pole.

0

@somekind: Jeszcze odnoście testowania API systemu. Co, jak mamy system rozbity na moduły i te moduły się ze sobą komunikują? Załóżmy, że integracja jest poprzez direct call.

W przypadku testów opartych na implementacjach in memory wiadomo, podmieniamy jakieś adaptery, które pobierają dane z innych modułów, i możemy testować nasz moduł w izolacji. Integrację modułów możemy zostawić testom integracyjnych.

A co w przypadku, gdy testy operują wyłącznie na API systemu? Wtedy chyba nie można przetestować pojedynczego modułu w izolacji. Żeby sprawdzić w teście działanie endpointu z modułu X, trzeba wcześniej wysłać N requestów, żeby przygotować dane w innych modułach. To na pewno dobre?

1

@nobody01 - ale co chcesz przetestować?

Jeśli to, czy Twój kod prawidłowo reaguje na odpowiedzi z wywoływanych przez siebie API, to możesz napisać zwykłe unit testy i użyć mocków. To ma sens, pod warunkiem, że masz pewność, że używane przez Ciebie API się nie zmienia i nikt nagle nie złamie kontraktu.

Jeśli to, czy przypadek użycia z punktu widzenia użytkownika działa, to wymaga to wywołania wszystkich API, których ten użytkownik potrzebuje. Inaczej się nie da.

1 użytkowników online, w tym zalogowanych: 0, gości: 1