Było wiele dyskusji, czy fasady w #Laravelu implementują Wzorzec Fasady Gangu Czterech, ale myślę, że w tym momencie nie ma to znaczenia – zespół Laravela i tak nie zmieni konwencji nazewnictwa. Nazewnictwo nie stanowi problemu, widzę natomiast inne problemy z fasadami — przyjrzyjmy się im!

To nie jest krytyka

Przede wszystkim, zanim powiem cokolwiek o fasadach Laravela, chciałbym podkreślić, że nie jestem przeciwko obu tym kwestiom: frameworkowi i wzorcowi projektowemu. Celem tego artykułu jest rzucenie światła na fasady, zapewnienie innej perspektywy. Mam nadzieję, że wywoła to dyskusję, skłoni deweloperów do zastanowienia się nad tym tematem.

Nie mówię, że musisz całkowicie porzucić fasady i używać tylko Dependency Injection — po prostu wybierz mądrze 😉. Chcesz być programistą PHP czy Laravel Developerem? Ponieważ jeśli będziesz przestrzegać ogólnych zasad programowania, nie będziesz miał problemów po przejściu na inny framework, ale jeśli będziesz wszystko robił “jak Laravel przykazał”, możesz napotkać wiele problemów poza tym frameworkiem.

To rodzaj magii

Niektórzy mogą to postrzegać jako zaletę, ale ja uważam to za wadę. Używając fasad, nie wykonujesz dokładnie tego kodu, który wywołujesz, ale Twoje wywołanie jest przekazywane do jakiejś bliżej nieokreślonej usługi skrytej pod spodem. Ściśle łączy to Twój kod z frameworkiem, który obsługuje tę operację. API fasady musi być dodane jako komentarze w PHPDoc (poprzez adnotację @method), co jest podatne na błędy, ponieważ łatwo zapomnieć o aktualizacji phpDoc, gdy podstawowa usługa (tzw. accessor) zostanie zmieniona. Nawet jeśli autouzupełnianie w IDE działa, nie możesz po prostu przejść do kodu metody.

Nawigacja po fasadach Laravela w natywnym PHPStorm

Istnieją oczywiście narzędzia, takie jak barryvdh/laravel-ide-helper, które mogą rozwiązać niektóre (lub nawet wszystkie) z tych problemów, ale dla mnie to, że potrzebujesz zewnętrznego narzędzia, żeby pracować z kodem w rozsądny sposób, nie jest najlepszym doświadczeniem dla programistów.

Na marginesie: ten sam problem dotyczy funkcji pomocniczych Laravela. Oczywiście upraszczają kod (choć to subiektywne), ale kosztem bardzo ścisłego powiązania z frameworkiem. Głównym problemem, który w nich widzę, jest to, że ludzie uczą się używać Laravela, a nie pisać kod w PHP.

Łamią zasadę pojedynczej odpowiedzialności

Fasady Laravela działają jak statyczne proxy do lokalizatorów usług, ale jednocześnie… udostępniają API do testowania. Teoretycznie kończysz na produkcji z kodem, który można mockować.

Ponieważ fasady są używane statycznie, nie można ich zastąpić inną implementacją podczas inicjalizacji obiektu. Dlatego podstawowa klasa Facade używa Mockery i udostępnia kilka metod, które umożliwiają testowanie. To niepotrzebnie wzbogaca interfejs API: Twoje IDE sugeruje metody, których nie potrzebujesz do regularnego programowania, ponieważ są one przeznaczone tylko do testowania.

Fasady Laravela zaciemniają autouzupełnianie w IDE

Możesz pomyśleć: „Ale to działa! Mogę przetestować swój kod” i w pewnym stopniu masz rację. Ponieważ Facade używa Mockery, a Laravel ma go tylko w require-dev, możesz skończyć z “błędnie działającą” fasadą w przypadku instalacji z opcją --no-dev (np. na produkcji). Moim zdaniem to śmierdzi.

Z drugiej strony, jeśli budujesz/wdrażasz niewłaściwie (composer install bez --no-dev), możesz skończyć na produkcji z fasadami, które można mockować — kod testowy nie powinien osiągnąć tego etapu. Dodaj do tego podejście „debugowanie na produkcji” i kłopoty gotowe 😅

Fasady w czasie rzeczywistym

Przed przygotowaniem wątku na Twitterze nie wiedziałem nawet o takiej koncepcji w Laravelu, ale odrobiłem pracę domową przed opublikowaniem i przeczytałem dokumentację. Uderzyła mnie ta jedna konkretna sekcja:

możesz traktować dowolną klasę w swojej aplikacji jak fasadę

Jestem po prostu zdumiony, że ktoś wpadł na ten pomysł i że Laravel oficjalnie sugeruje, aby dowolną usługę owinąć magiczną fasadą 🤯 To nawet nie magia, to czarnoksięstwo! 😅

Szczerze, ten pomysł w ogóle mi się nie podoba. Jeśli chcesz użyć wbudowanych fasad frameworka — OK. Jeśli chcesz zapewnić własną fasadę — OK. Jednak magiczne opakowanie klasy zewnętrznej nieistniejącą przestrzenią nazw tylko po to, aby statycznie używać API klasy? Nie. Znowu niektórzy mogą powiedzieć: „To jest niesamowite! To bardzo upraszcza!” i znowu będą mieli w pewnym stopniu rację. Natomiast ponownie: to łamie zasady SOLID, zakrywa jawne zależności magią, ściśle łączy Twój kod z Laravelem, uczy niewłaściwych (moim zdaniem) praktyk.

Niestandardowe automatyczne ładowanie

Nie było tego w wątku na Twitterze, ale jest jeszcze jeden mały szczegół związany z fasadami — możesz pominąć importowanie FQCN fasady i potraktować je tak, jakby były w globalnej przestrzeni nazw, a nadal będą działać 😩

Fasady Laravela są automatycznie ładowane z globalnej przestrzeni nazw

Jak można zauważyć na powyższym obrazku, IDE sugeruje brak klasy, ale wykonanie takiego kodu działa.

Dzieje się tak z powodu innego magicznego podejścia — konfigurowalnych aliasów (z domyślnymi aliasami). Zasadniczo konfigurujesz, które aliasy (traktuj je jako nieistniejące klasy z globalnej przestrzeni nazw PHP) powinny być traktowane jako fasada i rozwiązywane jako rzeczywista klasa.

To magia kryjąca się za inną magią. Statyczne proxy z aliasem zdefiniowanym w konfiguracji. Dla mnie to za dużo 😅

Alternatywa

Zamiast używać fasad, możesz wykorzystać Dependency Injection i bezpośrednio korzystać z usług kryjących się za nimi. Są one wymienione w dokumentacji Laravela, większość z nich ma powiązane usługi zdefiniowane w kontenerze, które można wykorzystać do wstrzykiwania ich jako zależność.

Zapewniam, że pomoże Ci to gdy będziesz musiał pracować z innymi frameworkami. Przejście z Laravela na inny framework jest moim zdaniem trudniejsze niż zmiana w przeciwną stronę, więc jeśli jest szansa, że będziesz pracować z innymi frameworkami, to podejście z użyciem DI lepiej Cię do tego przygotuje. Kurczowe trzymanie się konwencji Laravela może prowadzić do nawyków, które nie mają zastosowania gdzie indziej.

Oczywiście jest to zawsze Twój wybór (lub Twojego zespołu) 🙂