Abstrakcja w kontekście duplikacji i wieloznaczności



Zastanawiałem się wiele razy co jest lepsze z perspektywy generalnej. Abstrakcja i dziedziczenie, czy kompozycja, a może duplikacja? Co jest właściwe? Oczywiście odpowiadamy jak? "To zależy".



Możemy wyróżnić kilka sytuacji. Pierwsza kiedy posiadamy wartości słownikowe czyli określony klucz użyty w UI może posiadać kilka wartości w zależności od używanego języka. Jak modelować tę relację? Czy istnieje jeden opis, jedna pozycja jako klucz, która posiada wiele możliwych wartości które są odpowiednikiem np. nagłówka w różnych językach, czy może powinniśmy każdy obszar traktować osobno i budować słownik osobny dla każdego kontekstu na osobnej tabeli. Dla mnie pierwsza opcja jest najprostsza i nie mam ochoty wdawać się w dywagacje kto i o co pyta tylko skupić się na encji jaką jest konkretna pozycja w menu, w nagłówku, w opisie itp. Słownik staje się słownikiem i tłumaczy wartość encji z jednego języka na inny, może posiadać warianty. Spotykałem się jednak z tym, że każdy miał własny kontekst przez co powstawało pytanie o to czy definiowanie takich tabel nie doprowadza do sytuacji w których to co dziś jest słownikiem nie będzie przekształcało się w niespójnie rozwijające się modele obiektów, czy nawet agregatów a funkcjonalność języka rozmyje się i rozleje na inne byty i wprowadzenie np jednej kolumny oznaczającej jeden język będzie powodowało doklejanie jej do wielu tabel. 

Ok. To był przykład prosty kiedy zapobiegliśmy powstawaniu przypadkowych struktur pod wpływem złego wyciągnięcia kontekstu, a więc pod wpływem tego, że mogliśmy koncentrować się na złej abstrakcji i jej poddać się modelując strukturę bazy danych. Idźmy w inny przykład.

Mamy zamówienie i posiada ono statusy. Czy zamówienie ze statusem wysłane i posiadające tylko podstawowe pola powinno być modelowane jako tabela ze wszystkimi możliwymi kolumnami i w procesie realizacji będziemy zmieniać zawartość i dopisywać wartości, aż do momentu kiedy logistyka dopisze numer przesyłki i zarchiwizuje zamówienie odebrane? Częsty przypadek bazy która ma sklejone osobne byty domenowe w jeden, gdzie występuje rozróżnienie na inne dialekty języka biznesowego w którym dla różnych departamentów, lub osób to samo pole będzie powinno oznaczać coś innego. Jakie ma to plusy? Plusy są takie że unikniemy problemów z wieloznacznością.

Wieloznaczność będzie dla nas zagadnieniem kiedy dojdziemy do momentu pojawienia się wielu różnych struktur wynikających z różnych relacji w systemie. Zamówienie może być związane z konkretną pozycją katalogową, ale też z konkretną polityką cenową, w odniesieniu do aktualnego stanu magazynowego. Pomyślmy o tym jak zachować powinno się zamówienie kiedy nie ma towaru w magazynie? Może powinno oczekiwać na realizację umożliwiając zwrot pieniędzy na żądanie? Może powinno zostać odrzucone? Może jak zawsze... "to zależy". Jeśli jest to zamówienie w kontekście promocji powinno zostać odrzucone bo promocja jest zakończona po ostatnim wysłanym egzemplarzu, które nie zdążyło się rozpropagować w systemie bo dział logistyki nie "ściągnął" ostatniej sztuki z magazynu i wstępna walidacja "widziała" zarezerwowaną już pozycję? Może w kontekście regularnego zamówienia powinniśmy je trzymać i zamówić u producenta specjalnie dla klienta, a klient powinien zostać poinformowany o terminie realizacji?

Te same pojęcia jak cena, czy opis może występować w różnych kontekstach i oznaczać coś zupełnie innego. Dla logistyki to cena transportu, dla zamówienia to cena z polityką cenową i rabatem. Dla działu zamówień i zakupów będzie to cena za którą kupujemy u producenta. Magazyn może mieć podejście do własnego pojęcia cena i nazywać tak koszt składowania w specjalnych warunkach konkretnej sztuki towaru. Oczywiście zazwyczaj pojęcia te są układane w długie nazwy opisowe by zapobiegać przypadkowemu mieszaniu pojęć, ale nie zawsze uda się temu zapobiegać. W jaki sposób możemy stworzyć abstrakcję dla takiego bytu? Możemy powiedzieć że zawsze będzie jakieś ID i zawsze będzie opis i zawsze będzie cena. No i tu napotykamy problem. Mamy osobne tabele w bazie ale mamy różne wersje obiektu który posiadając wspólny interfejs, lub wspólną abstrakcję w postaci klasy daje nam możliwość wykonywania operacji bez rozróżnienia na typ konkretny. Możemy łatwo procesować w jednym miejscu określone byty.

Pytanie powstaje wtedy o to, czy te procesory abstrakcji, które są jedyną motywacją dla powstawania abstrakcji kiedy zechcemy je rozwijać i na nich stworzymy jakieś funkcjonalności nie zaczną mieć własnych potrzeb i nie zaczną wymagać od nas wprowadzania własnego języka do naszych struktur danych. Może pojawić się pole z nazwą tabeli bo ono będzie umożliwiało odpowiednie przeprocesowanie pola opis :) no i tu już jesteśmy w tym co się zwykło określać jako "życie".

Każda abstrakcja ma swój ogromny koszt który ujawni się w najmniej oczekiwanym momencie. 

Dla tego ja osobiście nie wierzę w coś takiego jak dobra abstrakcja. Każda ma swój koszt i czasem możemy go akceptować, ale zawsze abstrakcja jest potencjalnym długiem technologicznym i to takim którego nie wykryje nam narzędzie do statycznej analizy kodu.

Panowanie nad abstrakcjami i ostrożne ich używanie to coś co cechuje doświadczonego programistę który nie tylko pracował nad wytworzeniem systemu ale i musiał dbać o niego i dostosowywać go do zmian organizacyjnych, zmian w strukturze i zmian w procesowaniu, zmian w profilu przedsiębiorstwa i zmian w rozumieniu pojęć podstawowych. Abstrakcja jest pierwszym miejscem w którym system pęka. Pęknięcie zaczyna się od słowa "instanceof" które pojawia się w warstwie logiki biznesowej. O ile słowo to możemy tolerować w warstwie technologicznej gdzie chcemy wysyłać różne rodzaje zdarzeń na jedną kolejkę by zachować chronologię i musimy odróżnić w "listenerze" owe zdarzenia (czasem za nas robi to framework), o tyle logika biznesowa powinna operować zawsze na konkrecie. Tu pojawia się duplikacja jako koszt. To jest dług technologiczny dla narzędzia ale dla nas jest to znak, że każdy proces może być w dowolnym momencie zmieniony nie wnikając w detale tego jak to się ma do innych procesów. Inne procesy mają własne definicje operacji. Czasem identyczne - przynajmniej rzez jakiś czas.

Oczywiście można zapobiegać temu delegując "duplikaty" poza klasy, agregaty i tam wprowadzać różne lokalne "utilsy" do których przekażemy kontekst za pomocą słówka "this". To nie jest ładne i śmierdzi ale kiedy mamy klasy agregatu który ma wiele tysięcy linii kodu i każda metoda logiki biznesowej rozrasta się bardzo wchodząc w algorytmikę, wykorzystując procesowanie obiektu przez sieci neuronowe... chyba zapomnieliśmy zwyczajnie o jakimś serwisie i implementujemy właśnie inną domenę, może nawet mikroserwis duplikując go w każdej klasie :)  

Dla tego proszę Was... tkwijmy nogami na ziemi. Nawet jeśli nasze myśli widzą wszystko jako jedną "prostą" funkcjonalność, przecież "identyczną" dla każdego przypadku. Więc czemu nie wyabstrahować? Bo kurła nie :)

Comments