Mobil proqramlaşdırma dünyasında SRP prinsipi daha çox UI, Biznes və Data təbəqələrinin ayrılmasına əsaslanır. Ümumilikdə desək, bu bölünmə normaldır. Amma bir nümunə üzərindən getməyə çalışıb, daha çox detallarına baxaq.
Qeyd edim ki, burda istifadə olunan şəkil və nümunələr, https://www.petrosefthymiou.com/product-page/clean-mobile-architecture bu kitabdan götürülüb.
Şəkildə ən sadə bölünmə təsvir olunub. Burdakı təbəqələri anlamağa çalışaq:
- Executor - bu tiplər daha çox hər hansı işi yerinə yetirən təbəqələrə aiddir. Məsələn, UI istifadəçinin əmrlərinin yerini yetirir, Service backend-dən dataları alır və s.
- Orchestrator - bu tiplər isə executorlar arasında əlaqəni təmin edən təbəqələrdir. ViewModel (Bloc, Riverpod və s) orkestorlardır.
Bu ən sadə nümunə idi. İndi düşünək ki, bu arxitekturaya JSON-ları parse etmək üçün bir class və bir dənə də HTTP Client əlavə etməliyik. Bu zaman arxitektura bu şəkildə olacaq:
Gördüyümüz kimi, yeni executorlar əlavə olundu. Burda diqqətlə baxsaq, SRP qaydasını pozmamaq üçün, JSON parse etmək mexanizmini ayrı classa, HTTP Clienti isə ayrı classda yazmağı seçmişik. Hər bir classın özünə xas işi var, HTTP Client parse etməməlidir.
İndi isə düşünək ki, bizə yeni bir tələb gəldi ki, keşləmə mexanizmi olmalı, əgər lokalda data varsa ordan oxunmalıdır, yoxdursa, apidan data çəkilməlidir. Ən sadə halda, aşağıdakı arxitekturanı hazırladığımızı düşünək:
Gördüyümüz kimi, ayrı bir class yaratdıq və orda Database işlərini həll etdik. Amma bu zaman bizim service artıq executor oldu, çünki api və ya database ilə ViewModel-in əlaqəsini bu təbəqə təyin etməli oldu. Amma bu yanaşma artıq SRP prinsipini pozmuş oldu, çünki, Service-in 2 müxtəlif məsuliyyəti yaranmış oldu. Arxitekturanı yaxşılaşdırmaq üçün, aşağıdakı şəkilə diqqət edək:
Yeni bir class - DAO (Data Access Object) yaratdıq, bu classın məqsədi Database ilə əlaqəni abstraklaşdırmaq, interfeys yaratmaqdır. Və artıq, Service classın içindəki lokal və api idarəetmə məntiqini ViewModelə yönəltdik. Bu bizim Service təbəqisinin SRP problemini həll etsə də, ViewModel üçün problem yaratmış oldu, çünki indi servisin edəcəyini ViewModel etməli olacaq. `Bu halı da yaxşılaşdırmağa çalışaq:
Yeni bir təbəqə - Repository (ürəyimiz) əlavə olundu. Artıq bütün məsuliyyət Repository üzərinə düşür, lokaldan və ya apidan oxunması lazım olduğuna Repository qərar verəcəkdir. Amma bircə dəqiqə, burda da problemimiz var. Axı ViewModelin bir başa Repositoryə əl çatan olması normal deyil? Çünki zamanla Biznesin tələbi ilə kodda dəyişiklik olduqca, Repository dəyişdikcə, bu bir başa ViewModelə təsir edə bilər. Buna görə də biz, Repository-ləri ViewModellərlə Əlaqələndirmək üçün, Interactorlar istifadə edə bilərik. Bunlar bir növ Data Sourcelar kimidir, bir neçə Repository-dən olan dataları cəmləyib, ViewModelə ötürə bilər.
Domain Mapper və UI Mapper kimi classlar daha çox köməkçi (helper) classlardır, UI ilə bağlı və ya Domain ilə bağlı olan hər hansı məntiqləri ayrı köməkçi hissələrə çıxararaq, daha səliqəli kod almış olarıq.
Bunu da qeyd edəki ki, kodun daha səliqəli olması baxımından, hər Repository-nin sadəcə bir Entity üçün çalışması daha doğru yol olacaqdır. Yəni onun içində olan metodlar ancaq bir entity üzərində əməliyyat aparacaqdır.
Ən sonda onu qeyd edim ki, belə bir məcburiyyət yoxdur ki, hər zaman bu qədər təbəqə olsun. Əgər sizin rastlaşdığınız hal daha rahatdırsa, UI təbəqəsinin heç bir concerni yoxdursa, zamanla çox dəyişikliyə uğramayacaqsa, Service çox sadədirsə, o halda bu qədər təbəqələşdirməyə ehtiyac qalmır.
Ümid edirəm ki, sizlər üçün yararlı məqalə oldu. Əgər bəyəndinizsə, kofe alaraq dəstək ola bilərsiniz :)