Po co wydzielać dostęp do danych?
Wydaje mi się, że na podstawie dwóch pierwszych stron rozdziału Model Driven Design znajduje się uzasadnienie, dlaczego chcesz wyodrębnić szczegóły techniczne implementacji z modelu domeny.
- Chcesz zachować ścisłe połączenie między modelem domeny a kodem
- Oddzielenie problemów technicznych pomaga udowodnić, że model jest praktyczny do wdrożenia
- Chcesz, aby wszechobecny język przeniknął do projektu systemu
Wszystko to wydaje się mieć na celu uniknięcie osobnego „modelu analizy”, który zostaje oddzielony od faktycznego wdrożenia systemu.
Z tego, co rozumiem z tej książki, wynika, że ten „model analizy” może ostatecznie zostać zaprojektowany bez uwzględnienia implementacji oprogramowania. Gdy programiści próbują wdrożyć model rozumiany przez stronę biznesową, tworzą własne abstrakcje z konieczności, powodując ściankę w komunikacji i zrozumieniu.
Z drugiej strony programiści, którzy wprowadzają zbyt wiele problemów technicznych do modelu domeny, mogą również powodować ten podział.
Można więc wziąć pod uwagę, że praktykowanie oddzielenia problemów, takich jak wytrwałość, może pomóc zabezpieczyć się przed zaprojektowaniem rozbieżnych modeli analizy. Jeśli wprowadzenie do modelu czegoś takiego jak wytrwałość wydaje się konieczne, oznacza to czerwoną flagę. Być może model nie jest praktyczny do wdrożenia.
Cytowanie:
„Pojedynczy model zmniejsza ryzyko błędu, ponieważ projekt jest teraz bezpośrednim skutkiem starannie przemyślanego modelu. Projekt, a nawet sam kod, ma komunikatywność modelu”.
Sposób, w jaki to interpretuję, jeśli skończyłeś z większą liczbą linii kodu zajmujących się takimi kwestiami jak dostęp do bazy danych, tracisz komunikatywność.
Jeśli potrzeba dostępu do bazy danych dotyczy między innymi sprawdzania unikatowości, spójrz na:
Udi Dahan: największe błędy popełniane przez zespoły przy stosowaniu DDD
http://gojko.net/2010/06/11/udi-dahan-the-biggest-mistakes-teams-make-when-applying-ddd/
w sekcji „Wszystkie reguły nie są sobie równe”
i
Wykorzystanie wzorca modelu domeny
http://msdn.microsoft.com/en-us/magazine/ee236415.aspx#id0400119
w części „Scenariusze nieużywania modelu domeny”, która dotyczy tego samego tematu.
Jak oddzielić dostęp do danych
Ładowanie danych przez interfejs
„Warstwa dostępu do danych” została wydzielona przez interfejs, który wywołujesz w celu pobrania wymaganych danych:
var orderLines = OrderRepository.GetOrderLines(orderId);
foreach (var line in orderLines)
{
total += line.Price;
}
Plusy: Interfejs oddziela kod hydrauliczny „dostęp do danych”, pozwalając na pisanie testów. Dostęp do danych może być obsługiwany indywidualnie dla każdego przypadku, co zapewnia lepszą wydajność niż ogólna strategia.
Wady: kod wywołujący musi zakładać, co zostało załadowane, a co nie.
Powiedzmy, że GetOrderLines zwraca obiekty OrderLine z zerową właściwością ProductInfo ze względu na wydajność. Deweloper musi mieć dogłębną znajomość kodu kryjącego się za interfejsem.
Wypróbowałem tę metodę na prawdziwych systemach. Ostatecznie zmieniasz zakres tego, co jest ładowane przez cały czas, próbując naprawić problemy z wydajnością. W końcu zaglądasz za interfejs, aby zobaczyć kod dostępu do danych i zobaczyć, co jest ładowane, a co nie.
Teraz rozdzielenie problemów powinno pozwolić deweloperowi skupić się na jednym aspekcie kodu naraz, o ile to możliwe. Technika interfejsu usuwa W JAKI sposób ładowane są te dane, ale NIE W JAKI SPOSÓB ładowane są DUŻE dane, KIEDY są ładowane i GDZIE są ładowane.
Wniosek: dość niska separacja!
Powolne ładowanie
Dane są ładowane na żądanie. Wywołania w celu załadowania danych są ukryte w samym grafie obiektowym, gdzie dostęp do właściwości może spowodować wykonanie zapytania SQL przed zwróceniem wyniku.
foreach (var line in order.OrderLines)
{
total += line.Price;
}
Plusy: „KIEDY, GDZIE I JAK” dostęp do danych jest ukryty przed deweloperem skupiającym się na logice domeny. W agregacie nie ma kodu zajmującego się ładowaniem danych. Ilość załadowanych danych może być dokładną ilością wymaganą przez kod.
Wady: gdy pojawia się problem z wydajnością, trudno jest go naprawić, gdy masz ogólne rozwiązanie „jeden rozmiar dla wszystkich”. Leniwe ładowanie może ogólnie pogorszyć wydajność, a wdrożenie leniwego ładowania może być trudne.
Interfejs ról / chętne pobieranie
Każdy przypadek użycia jest jawnie wyrażony za pomocą interfejsu roli zaimplementowanego przez klasę zagregowaną, umożliwiając obsługę strategii ładowania danych dla każdego przypadku użycia.
Strategia pobierania może wyglądać następująco:
public class BillOrderFetchingStrategy : ILoadDataFor<IBillOrder, Order>
{
Order Load(string aggregateId)
{
var order = new Order();
order.Data = GetOrderLinesWithPrice(aggregateId);
return order;
}
}
Wówczas Twój agregat może wyglądać następująco:
public class Order : IBillOrder
{
void BillOrder(BillOrderCommand command)
{
foreach (var line in this.Data.OrderLines)
{
total += line.Price;
}
etc...
}
}
BillOrderFetchingStrategy służy do budowania agregatu, a następnie agregat wykonuje swoją pracę.
Zalety: Pozwala na niestandardowy kod dla każdego przypadku użycia, pozwalając na optymalną wydajność. Jest zgodny z zasadą segregacji interfejsu . Brak wymagań dotyczących kodu złożonego. Testy jednostkowe agregatów nie muszą naśladować strategii ładowania. W większości przypadków można zastosować ogólną strategię ładowania (np. Strategię „wczytaj wszystko”), a w razie potrzeby można wdrożyć specjalne strategie ładowania.
Minusy: programista wciąż musi modyfikować / weryfikować strategię pobierania po zmianie kodu domeny.
Dzięki strategii pobierania możesz wciąż zmieniać niestandardowy kod pobierania w celu zmiany reguł biznesowych. Nie jest to idealna separacja problemów, ale będzie łatwiejsza w utrzymaniu i jest lepsza niż pierwsza opcja. Strategia pobierania zawiera dane HOW, WHEN i WHERE wczytywane. Ma lepszą separację problemów, bez utraty elastyczności, tak jak jeden rozmiar pasuje do wszystkich leniwych metod ładowania.