Clean Architecture

Artur Szymański | Rozwój oprogramowania / Zarządzanie projektami | 17.12.2019

Stałe i spójne podejście w pisaniu kodu jest ważnym elementem realizacji projektów. Będzie ono miało wpływ na całość prowadzonych prac oraz czas realizacji. W dzisiejszym artykule chcę się przyjrzeć podejściu Clean Architecture. Jaka jest główna idea tworzenia architektury typu Clean Architecture i w jakich sytuacjach się ona sprawdzi? Postaram się również omówić i przedstawić jej główne zalety.

Wybór architektury

Rozpoczynając nowy projekt, developerzy chcą bardzo szybko zakończyć pierwsze zadanie, pomijając wagę przygotowania projektu. To błąd! Przecież to właśnie początkowy etap tworzenia nowej aplikacji jest najważniejszy. Pozwala on wypracować spójne, stałe podejście dla pozostałych developerów biorących udział w projekcie.

Czytaj także: Neo4j – zaproszenie do grafowych baz danych

Architektura a dług technologiczny

Kolejnym błędem popełnianym przez developerów jest doprowadzenie do długu technologicznego. Prawdą jest, że często tworzymy coś szybko, wydawać by się mogło sensownie… Jednak ze względu na różne czynniki zaciągamy coraz większy dług technologiczny. W pracy projektowej nie warto iść na skróty. Pośpiech i zaniedbanie to najczęstsze przyczyny nawarstwiania się długu technologicznego w projekcie.

Pół biedy, jeśli zaciągamy dług, który potrafimy później „spłacić”, czyli w odpowiednim czasie dokonać poprawek. Gorzej, jeśli zaciągamy dług przekraczający nasze umiejętności lub wychodzący poza ramy czasowe projektu. Niemożliwy do spłacenia dług technologiczny doprowadzić może do czegoś, co wszyscy dobrze znamy. Idealnie obrazuje to grafika poniżej.

Clean Archotecture - dług technologiczny na przykładzie długu publicznego Polski

Aby uniknąć tego problemu, warto użyć jednego z wielu dostępnych wzorców tworzenia architektury projektowej. Moim faworytem, a zarazem najczęściej używanym przeze mnie podejściem, jest Clean Architecture.

Czym jest Clean Architecture

Clean Architecture to podejście do programowania aplikacji, które zakłada wydzielenie czterech głównych modułów funkcjonalnych w projekcie. Moduły te to:

  • Application,
  • Presentation,
  • Domain,
  • Data.

W dalszej części omówię szczegółowo każdą z nich. Na początek kilka słów o głównej idei podejścia Clean Architecture.

Idea Clean Architecture

Robert C. Martin (Uncle Bob) w książce „Clean Architecture” definiuje cel architektury jako wspieranie cyklu życia systemu. Dalej za autorem: dobra architektura sprawia, że ​​system jest łatwy do zrozumienia, łatwy w rozwoju, łatwy w utrzymaniu i łatwy do wdrożenia. Ostatecznym celem jest zminimalizowanie kosztów eksploatacji systemu i maksymalizacja produktywności programistów.

Tworząc podejście Clean Architecture Robert C. Martin, dążył do stworzenia struktury projektu z wyraźnie oddzieloną odpowiedzialnością poszczególnych warstw. Założenie było następujące: każda z warstw ma wykonywać jedno zadanie i ma być łatwa do wyizolowania od reszty. Inne założenie to możliwość współdzielenia jednego kodu między wieloma środowiskami. Miałoby się to odbywać poprzez wyciągnięcie dwóch głównych modułów niezależnych od technologii użytej do tworzenia aplikacji: Presentation oraz Domain i współdzielenie w innych środowiskach, dopisując jedynie warstwy Application oraz Data. Warstwy te są ściśle powiązane ze środowiskiem, na którym uruchamiamy projekt. Dobrym przykładem może być napisanie logiki biznesowej raz i współdzielenie jej przez aplikacje mobilne na iOS i Android, ale również przez aplikacje webowe i desktopowe.

Clean Architecture – wzorce projektowe

Moduł prezentacji może zostać zaimplementowany za pomocą wielu popularnych wzorców projektowych: Model View ViewModel (MVVM), Model View Presenter (MVP), Model View Intent (MVI), Model View Controller (MVC) czy też wiele innych. Każdy programista ma własne preferencje, jeśli chodzi o wybór wzorca. Mimo iż każdy z nich ma wady i zalety, na potrzeby opisu tej architektury skupimy się na podejściu MVP. Jest to podejście, które osobiście preferuję.

Application

Warstwa aplikacji jest najmocniej połączona z technologią, w której używamy podejścia Clean Architecture. Zapewnia ona całą wizualną część projektu oraz reaguje na zadania powierzane jej przez warstwę prezentacji. Warstwa ta decyduje, w jaki sposób wyświetlane będą dane dostarczane przez część prezentacyjną. Zawiera ona logikę wyświetlania oraz informuje warstwę prezentacji o wszelkich akcjach wykonanych przez użytkownika. Obsługuje ona też mechanizm utrzymywania stanu Presentera oraz przygotowuje wszelkie niezbędne zależności do jego poprawnego działania.

Presentation

Presenter steruje całą aplikacją, jak i poszczególnymi ekranami, reaguje on również na poszczególne akcje wykonane przez użytkownika w części wizualnej. To również tu zapada decyzja, w jaki sposób obsłużyć dane zadania czy też jaką część aplikacji kolejno pokazać użytkownikowi. UseCase’y, opisane dalej, są jedynym sposobem komunikacji tego modułu z pozostałymi. Służy np. do wykonania logiki biznesowej lub też pobrania określonych danych. W podejściu MVP każdy Presenter ma interfejs do komunikacji z widokiem, komunikacja ta pozwala jedynie na zlecanie zadań widokowi, ale nie umożliwia ona żądania czegoś w zamian. Mówiąc prościej, Presenter wykonuje funkcje na interfejsie widoku, ale funkcje te nie mogą mieć typu zwracanego.

Domain

Domain to moduł zawierający całą logikę biznesową. Składa się on z wielu składowych:

UseCase

Najważniejsza, widoczna na zewnątrz składowa, wykorzystywana przez warstwę prezentacji. UseCase’y są odwzorowaniem realnych wymagań biznesowych i zapewniają logice prezentacji dostęp do operacji na danych (pobierania, zmiany, nasłuchiwania zmian). Umożliwiają również wykonywanie logiki biznesowej: obliczeń czy manipulacji na danych. Warto wspomnieć, iż są one jedyną możliwością komunikacji między warstwą prezentacji a warstwami domeny. Dane transportowane są przez modele przechowywane również w tej warstwie. UseCase’y dostęp do danych otrzymują za pośrednictwem DataSource’ów poprzez udostępniony przez nie interfejs. Innym rodzajem wykorzystywanych przez nie zależności są klasy z warstwy Logic opisane dalej.

Model

To zbiór w pewien logiczny sposób zebranych danych, wykorzystywanych przez logikę biznesową. Model ten nie musi być identyczny jak model z warstwy data. Może on np. łączyć kilka modeli data w zależności od potrzeb i zastosowania.

Logic

Miejsce w strukturze, które służy do przechowywania wydzielonych klas z rozbudowaną logiką biznesową. Miejsce te umożliwia unikanie rozbudowanych UseCase’ów.

DataSource

Najważniejszym zadaniem DataSource’ów jest decydowanie, skąd będą pobierane lub gdzie zapisywane będą dane. Właśnie one za pomocą interfejsów mają dostęp do wszystkich źródeł danych, które wystawia moduł Data. Oprócz danych w środku znajdziemy również Mappery umożliwiające tłumaczenie danych z warstwy Data na modele biznesowe wykorzystywane później. DataSource’y są jedynym połączeniem między modułem Data a resztą logiki biznesowej i to one wystawiają dalej interfejsy dające dostęp do dostosowanych danych dla UseCase’ów.

Mappery

Mappery w architekturze Clean Architecture dbają o spójność danych dostarczanych przez warstwę data. To właśnie tu odbywa się sprawdzenie, czy wszystkie niezbędne dane zostały dostarczone, tak aby logika biznesowa mogła w pełni działać. Mappery często łączą kilka obiektów z warstwy data z różnych źródeł, tworząc jeden, który ma wszystkie potrzebne dla logiki biznesowej dane.

Data

Moduł, który dostarcza dostęp do wszelkiego typu źródeł danych oraz modeli, które te dane odwzorowują. Dostęp do metod udostępnianych przez ten moduł możliwy jest jedynie dzięki wykorzystaniu interfejsów, które ten moduł udostępnia. Jest on również drugim modułem mającym największą zależność od platformy, na której wykorzystywana jest Clean Architecture.

Data transfer object

Data transfer object (w skrócie DTO) jest to model służący do dwustronnego przesyłania danych. Modele takie wykorzystywane są tylko w warstwie Data i w zależności od tego, czy dane te wysyłamy, czy też pobieramy, są one tłumaczone na modele biznesowe lub z nich za pomocą Mapperów.

Data

Data to miejsce pobierania i zapisywania danych wykorzystywanych w pozostałych częściach aplikacji. Przykładowe źródła danych to: Bluetooth, Internet, Shared Preferences, bazy danych, pliki, GPS, kamera, żyroskop czy system. Dostęp do takich danych odbywa się przez interfejs. Pozwala to na łatwe podmienianie danego źródła. Zamiana takiego źródła odbywa się poprzez podanie różnych obiektów implementujących ten interfejs. Zastępowanie źródeł danych najczęściej wykorzystywane jest np. do obsługi różnych typów serwerów czy też podpięcia mockowych danych.

Wady Clean Architecture

  • Czasochłonność. Zaimplementowanie tak złożonej architektury wymaga dodatkowego czasu. Z mojego doświadczenia wynika, że pisanie kodu w podejściu Clean Architecture wymaga około 10% więcej czasu niż tworzenie aplikacji bez żadnej specjalnej architektury.
  • Potrzebna jest wiedza. W ten typ podejścia wpisany jest utrudniony próg wejścia dla osób, które nie miały wcześniej wiedzy na temat tej struktury.
  • Duża ilość małych klas i interfejsów. Jedni uważają to za wadę, inni z kolei za zaletę. Uczciwie będzie, gdy stwierdzimy, że w każdej opinii jest ziarno prawdy. Moja opinia jest taka, że to niewątpliwa zaleta – osoby mające inne zdanie uważają, że duża ilość plików jest trudna w zarządzaniu, a zmiany w nich poczynione – mało czytelne.

Zalety Clean Architecture

  • Łatwiejsze utrzymanie. Clean Architecture znacząco ułatwia utrzymanie projektu. Naprawianie błędów w tym podejściu jest bardzo proste, a ich lokalizowanie – szybsze.
  • Lepsze zarządzanie zmianą. Podejście przyśpiesza wprowadzanie zmian w projekcie. Dzięki małym klasom z wydzielonymi odpowiedzialnościami bardzo łatwo modyfikować kod pod kątem nowych wymagań czy też dodawać nowe funkcjonalności bez obawy o konsekwencje w pozostałych częściach projektu.
  • Duża ilość małych klas i interfejsów. Jak już pisałem, tu zdania są podzielone. Ja uważam, że jest to zaleta z tego względu, że dzięki wielu małym klasom z wydzielonymi funkcjonalnościami kod daje się łatwo pokryć testami.

W jakich projektach stosować Clean Architecture?

Moim zdaniem Clean Architecture świetnie spełnia swoje zadanie w przypadku średnich i  dużych projektów. Złożoność podejścia w przypadku małych projektów może skutkować większym nakładem dodatkowych prac. Czy to znaczy jednak, że nie powinniśmy stosować wtedy Clean Architecture? Warto pamiętać, że małe projekty w naturalny sposób szybko się rozwijają. Podjęcie decyzji o stosowaniu podejścia CA nawet w mniejszych projektach jest moim zdaniem uzasadnione. Pozwala to uniknąć sytuacji zaciągania długu technologicznego, o którym pisałem. Szczególnie, gdy narzut czasowy jest nieduży.

Podsumowanie

Nawiązując do konceptu współdzielenia dwóch modułów między wieloma technologiami, uważam to za świetny pomysł i coraz bliższy realizacji. Do tej pory było to trudne w uzyskaniu, jednak dzięki upowszechnieniu języka programowania, jakim jest Kotlin już niedługo możemy mieć pierwsze projekty w pełni wykorzystujące te założenia. Mam nadzieję, że udało mi się przybliżyć główne założenia idei Clean Architecture i zachęcić do stosowania jej w projektach. Przemyślany wybór architektury pozwoli uniknąć długu technologicznego, a ten typ architektury – choć wymaga wiedzy i przeznaczenia więcej czasu na implementację, bez wątpienia sprawdza się długofalowo zarówno w większych, jak i mniejszych projektach.