TCA vs Clean Architecture: Refleksje z przepisywania produkcyjnej aplikacji
Wprowadzenie
Po ponad 8 latach programowania w iOS nauczyłem się doceniać architekturę, która służy zespołowi i projektowi, a nie odwrotnie. Jakiś czas temu dołączyłem do projektu, gdzie aplikacja była napisana w The Composable Architecture (TCA). Po kilku miesiącach zmagań podjęliśmy decyzję o przepisaniu jej do Clean Architecture. Chcę podzielić się tym, czego nas to nauczyło.
Wyzwania, z którymi się zmierzyliśmy
-
Akumulacja problemów technicznych
Aplikacja borykała się z problemami związanymi z zarządzaniem stanem: race conditions przy współbieżnych akcjach, nieoczekiwane mutacje stanu, problemy z czasem zycia subskrypcji. TCA obiecuje przewidywalność przez strict unidirectional data flow. W praktyce jednak:
- Każda akcja może wywołać effect,
- Effects mogą emitować kolejne akcje,
- Reducery modyfikują współdzielony stan,
- Wszystko dzieje się w asynchronicznym środowisku.
Dla zespołu, który nie miał 100% komfortu z każdym aspektem biblioteki, stało się to źródłem frustracji i błędów.
-
Debugowanie wymagało detektywistycznej pracy
Przykład: użytkownik zgłosił problem z wyświetlaniem danych po ponownym zalogowaniu. W tradycyjnej architekturze sprawdzilibyśmy przepływ: Repository → Use Case → ViewModel. W TCA musieliśmy przejść przez kilka warstw reducerów, effects, zarządzania współdzielonym stanem i environment dependencies. Znalezienie źródła zajęło znacznie więcej czasu niż zwykle.
Problem? Jeden z reducerów nie czyścił poprawnie stanu przy wylogowaniu - rzecz, którą w prostszej architekturze zauważylibyśmy w kilka minut.
-
Game-changer: wsparcie cross-platformowe
Ten punkt przeważył w naszej decyzji. Aplikacja na Androida była napisana w Clean Architecture. W pewnym momencie nacisk na dostarczanie features stał się tak duży, że zarząd przesunął kilku developerów z Android team do pracy nad iOS.
I tutaj ujawnił się prawdziwy problem. Ci developerzy - doświadczeni seniorzy znający Kotlina, Jetpack Compose i Clean Architecture stanęli przed ścianą TCA. Swift i SwiftUI opanowali szybko. Ale specyfika TCA (Reducers, Actions, Effects, Store) była dla nich obca i wymagała tygodni nauki.
Co więcej, różnice architektoniczne przełożyły się na rozbieżności w logice biznesowej. Android team operował na jasno zdefiniowanych Use Case'ach, my na Actions i State mutations. Obie aplikacje, mimo korzystania z tego samego API, czasami prezentowały dane w odmienny sposób.
Po przepisaniu do Clean Architecture sytuacja wyglądała zupełnie inaczej. Gdy ponownie potrzebowaliśmy wsparcia od Android team, otrzymywaliśmy je prktycznie natychmiastowo:
- Rozumieli Use Cases - dokładnie ten sam pattern,
- Rozumieli Repository pattern - identyczny jak w ich kodzie,
- Rozumieli SOLID principles - te same zasady stosowali codziennie,
Jeden z kolegów powiedział mi wtedy: "To jest dokładnie to samo co piszemy u nas, tylko w Swift."
Dlaczego zdecydowaliśmy się na refactor?
Decyzja nie była pochopna. Rozważaliśmy ją przez kilka tygodni. Przeważyły trzy czynniki:
- Synchronizacja z Android team - wspólny język architektoniczny eliminował rozbieżności w logice biznesowej,
- Redukcja friction - prostszy debugging, szybszy development, krótszy onboarding,
- Długoterminowa perspektywa - Clean Architecture i SOLID to standardy branżowe, które przetrwają dekadę. TCA, choć świetnie rozwijany przez Point-Free, pozostaje biblioteką jednego vendora.
Efekty
Refactor zajął 3 miesiące. Rezultaty:
- Liczba bugów związanych ze stanem spadła o ~60%,
- Onboarding nowych developerów: z 3-4 tygodni zmniejszył się do kilku dni,
- Czas debugowania typowych problemów skrócił się o połowę,
- Rotacja między zespołami iOS i Android stała się bezproblemowa.
Podsumowanie
Czy TCA to zły wybór? Nie. To dobrze przemyślany framework, który sprawdza się w wielu kontekstach - szczególnie w mniejszych zespołach, gdzie wszyscy są w niego wdrożeni. Ale w naszym przypadku - duży, długoterminowy projekt, wymóg synchronizacji między platformami, rotacja developerów, jak najszybsze dowożenie nowych funckjonalności - niezależność od konkretnej biblioteki przeważyła nad korzyściami płynącymi z TCA.
Kluczowa lekcja: nie ma złych architektur, są tylko niewłaściwe wybory dla danego kontekstu. Wybieraj świadomie, uwzględniając:
- Rozmiar i doświadczenie zespołu,
- Przewidywaną długość życia projektu,
- Koordynację z innymi platformami,
- Dostępność deweloperów na rynku.
Architektura powinna służyć projektowi i zespołowi. Nie odwrotnie.

Rafał Dubiel
Senior iOS Developer z ponad 8-letnim doświadczeniem w tworzeniu aplikacji mobilnych. Pasjonat czystej architektury, Swift i dzielenia się wiedzą ze społecznością.
Udostępnij artykuł: