
Jak rozpoznać aplikację legacy?
Nie ma sztywnych zasad, dzięki którym aplikację należy traktować jako taką – wszystko zależy od jej stanu i subiektywnego spojrzenia na nią.
Ile razy zdarzyło Ci się otworzyć jakiś Event Listener (czy Subscriber) a tam 250 linijek kodu w 6 metodach? A teraz odpowiedz sobie na pytanie – ile razy sam zrobiłeś taki listener? Często mylimy listenery z innymi warstwami naszej aplikacji, i wrzucamy do nich logikę, która nie powinna się tam znajdować. Powoduje to ich przerośnięcie, problemy z testowaniem a w ostateczności problemy w utrzymaniu kodu.
Jak pisać listenery by były przyjazne i nie nastręczały nam problemów? Najpierw trzeba zrozumieć czym one tak na prawdę są i jaką mają odpowiedzialność.
Info: W tym artykule używam słowa „listener”, ale mam również na myśli „subscribery”.
Jest to klasa, która odpowiada za wywołanie jakiejś akcji, gdy system rzuci zdarzeniem pod które jest ona podpięta.
Na przykład: system zarejestrował użytkownika i wysłał UserRegisteredEvent
. Listener łapie ten event, i wysyła powiadomienie za pomocą SwiftMailera. Użytkownik zostaje przywitany na stronie.
Proste, prawda? No nie do końca…
Odpowiedzialnością listenera jest tylko i wyłącznie załapanie danego Eventu i wykonanie jakiejś konkretnej akcji. I tutaj następuje pewien problem, ponieważ co to znaczy, że „listener ma wykonać jakąś akcję”?
W większości przypadków wygląda to tak, że zwyczajnie bierzemy SwiftMailera, budujemy sobie treść maila i wysyłamy go bezpośrednio w listenerze. Teoretycznie jest to wykonanie akcji, jednak nie o to tutaj chodzi.
Listener jest tylko i wyłącznie bramą, pomiędzy rzucanymi Eventami, a serwisami, które wykonują akcje. W lwiej części przypadków nie powinien on robić nic, oprócz wywołania jakiegoś serwisu lub Commanda (CQRS). Czasami może mieć jakieś warunki które spełnione pozwolą na wykonanie akcji (jednak te warunki i tak siedzą poza listenerem, on tylko pyta zewnętrzny serwis, czy może wykonać dana akcję).
I tutaj jest właśnie ta „akcja” o której wspominałem wcześniej. Listener wykonuje akcję, ale nie jest to akcja sam w sobie. Pamiętasz zapewne nagonkę na to, aby Kontroler był jak najbardziej odchudzony? Chodziło o to, by całą logikę z kontrolera wyciągnąć do osobnych klas. W przypadku listenerów jest dokładnie tak samo. Im będzie on mniejszy tym lepiej.
Przykładowy listener wysyłający powiadomienie o rejestracji użytkownika mógłby wyglądać tak.
class UserCreatedListener { private $greeter; public function __construct(GreetingsService $greeter) { $this->greeter = $greeter; } public function __invoke(UserRegisteredEvent $event) { $this->greeter->greetNewUser($event->getUserId()); } }
Polub stronę PHPCenter.pl by zyskać dostęp do najnowszych wiadomości ze świata PHP!
Gdy listener zawiera raptem jedną metodę z jedną linijką kodu, która odpowiada za wywołanie metody z jakiegoś serwisu, możesz nawet pominąć testy tego listenera. Nie ma tutaj logiki! Jest tylko delegowanie wykonywania akcji do innego serwisu – to tam powinny być testy.
Co jeśli wiadomość powitalną będziesz chciał wysłać również samodzielnie przez CLI, w razie gdyby do kogoś nie doszła? W tedy oprócz dodania Commanda musisz przenieść logikę listenera do osobnego serwisu. A razem z nią zrefaktoryzować testy. Ale czy nie lepiej było od razu odchudzić listener i logikę wysyłania powiadomień przenieść do serwisu?
Jesteśmy nygusami, My – programiści, nie lubimy tworzyć niepotrzebnie zbędnych plików/klas. Jeśli mamy wysłać maila na dany event, to łatwiej nam jest stworzyć jeden listener i tam zrobić wszystko co potrzebujemy, niż rozmieniać się na drobne i tworzyć kilka powiązanych ze sobą, wyspecjalizowanych klas. Jednak prowadzi nas to czasami do złych decyzji wdrożeniowych które odbijają się czkawką. Niektóre warstwy aplikacji wymagają szczególnego traktowania by miały sens. Tak własnie jest w przypadku Event Listenerów.
Zapamiętaj: EventListener === KISS
Im mniej kodu tym lepiej.
Polub naszą stronę aby dostawać powiadomienia o nowych, niesamowitych treściach!