Geriausi architektūriniai iOS tinklo programų kūrimo metodai (REST klientai)

Aš esu iOS kūrėjas, turintis tam tikrą patirtį, ir šis klausimas man labai įdomus. Aš mačiau daug skirtingų išteklių ir medžiagų šia tema, tačiau vis dėlto aš vis dar painu. Kas yra geriausia „iOS“ tinklo programos architektūra? Turiu omenyje pagrindinę abstrakčią struktūrą, modelius, kurie atitiks kiekvieną tinklo programą, ar tai maža programa, turinti tik keletą serverio užklausų, ar sudėtingas REST klientas. „Apple“ rekomenduoja naudoti „ MVC kaip pagrindinį architektūrinį požiūrį į visas „iOS“ programas, tačiau nei MVC nei modernesni MVVM šablonai nepaaiškina, kur įdėti tinklo logikos kodą ir kaip jį organizuoti.

Man reikia sukurti kažką panašaus į MVCS ( S for Service ), ir šiuo lygiu Service įdėti visus API prašymus ir kitą tinklo logiką, kuri ateityje gali būti tikrai sudėtinga? Kai kurie tyrimai parodė du pagrindinius metodus. Čia buvo rekomenduojama kiekvienai tinklo užklausai sukurti atskirą klasę API žiniatinklio paslaugai (pvz., „ LoginRequest klasė arba „ PostCommentRequest ir kt.), Kuri perima viską nuo abstrakčios „AbstractBaseRequest“ bazinės užklausos klasės ir kartu su tam tikru pasauliniu tinklo valdytoju, kuris apjungia bendrą tinklo kodą ir kitus nustatymus (gali būti AFNetworking arba RestKit , jei turime sudėtingus objektų žemėlapius ir atkaklumą arba netgi įgyvendinant tinklo ryšį su standartine API). Tačiau šis požiūris man atrodo viršesnis. Kitas metodas yra dispečeris arba dispečeris oneton API , kaip ir pirmuoju metodu, bet ne kiekvienai užklausai sukurti klases ir vietoj kiekvieno prašymo, kaip viešojo metodo šio vadovo klasės egzemplioriui, pavyzdžiui fetchContacts , loginUser metodai ir kt. Taigi, kas yra geriausias ir teisingiausias būdas? Ar yra kokių nors kitų įdomių metodų, kurių dar nežinau?

Ar turėčiau sukurti kitą sluoksnį visai šiai tinklo medžiagai, pvz., „ Service arba „ NetworkProvider ar kažkam kitam ant mano MVC architektūros, ar šis sluoksnis turėtų būti integruotas (įtrauktas) į esamus MVC sluoksnius, pvz., Model ?

Žinau, kad egzistuoja gražūs metodai arba kaip mobilieji monstrai, pvz., „Facebook“ klientas ar „LinkedIn“ klientas, susiduria su sparčiai augančiu tinklo logikos sudėtingumu?

Žinau, kad nėra tikslaus ir oficialaus atsakymo į problemą. Šio klausimo tikslas - surinkti įdomiausius patyrusių iOS kūrėjų požiūrius . Geriausias pasiūlytas metodas bus pažymėtas kaip priimtas ir apdovanotas reputacijos apdovanojimu, kiti bus palaikomi. Tai daugiausia teorinis ir mokslinių tyrimų klausimas. Noriu suprasti pagrindinį, abstraktų ir tinkamą architektūrinį požiūrį į tinklų pritaikymą „iOS“. Tikiuosi gauti išsamų paaiškinimą iš patyrusių kūrėjų.

278
11 июня '14 в 14:42 2014-06-11 14:42 MainstreamDeveloper00 yra nustatytas birželio 11 d. 14:42 2014-06-11 14:42
@ 11 atsakymų

I want to understand basic, abstract and correct architectural approach for networking applications in iOS kaip sukurti taikomąją architektūrą, nėra „geriausio“ ar „teisingiausio“ požiūrio. Tai labai kūrybingas darbas. Visada turėtumėte pasirinkti paprasčiausią ir išplečiamą architektūrą, kurią supras bet kuris kūrėjas, kuris pradės dirbti su jūsų projektu ar kitais savo komandos kūrėjais, bet sutinku, kad gali būti „gera“ ir „bloga“ architektūra.

Jūs sakėte: collect the most interesting approaches from experienced iOS developers , nemanau, kad mano požiūris yra įdomiausias ar teisingas, bet aš jį panaudojau keliuose projektuose ir esu patenkintas. Tai hibridinis požiūris į tuos, kuriuos minėjote anksčiau, taip pat mano pačių tyrimų tobulinimas. Mane domina problemos, susijusios su metodų, jungiančių keletą gerai žinomų modelių ir idiomų, kūrimu. Manau, kad daugelis „ Fowlerkorporacinių šablonų gali būti sėkmingai pritaikyti mobiliesiems. Čia yra sąrašas įdomiausių, kuriuos galime naudoti kurdami „iOS“ programos architektūrą (mano nuomone): paslaugų lygis , darbo vienetas , nuotolinis fasadas , duomenų perdavimo objektas , šliuzai , „ Supertype“ sluoksnis , specialus atvejis , domeno modelis . Jūs visada turite tinkamai suprojektuoti modelio sluoksnį ir visada nepamirškite išsaugoti (tai gali žymiai padidinti jūsų paraiškos našumą). Tam galite naudoti Core Data . Tačiau nepamirškite, kad „ Core Data nėra ORM arba duomenų bazė, o objekto grafiko valdytojas, taupantis kaip geras pasirinkimas. Todėl labai dažnai „ Core Data gali būti pernelyg sunkūs jūsų poreikiams ir galite ieškoti naujų sprendimų, pvz., „ Realm“ ir „ Couchbase Lite“, arba sukurti savo sluoksnį, skirtą suderinti / išsaugoti nešiojamus objektus, pagrįstus „SQLite“ arba „ LevelDB“ . Taip pat patariu susipažinti su „ Domain Driven Design“ ir „ CQRS“ .

Iš pradžių manau, kad reikia sukurti kitą tinklų sluoksnį, nes mums nereikia riebalų reguliatorių ar sunkių, perkrautų modelių. Aš netikiu šiais dalykais. Bet aš tikiu , kad skinny everything yra skinny everything , nes nė viena klasė niekada neturėtų būti riebalų. Visas tinklas gali būti suskirstytas į verslo logiką, todėl turėtume turėti kitą sluoksnį, kuriame galėtume ją išreikšti. Paslaugų lygis yra tas, ko mums reikia:

 It encapsulates the application business logic, controlling transactions and coordinating responses in the implementation of its operations. 

Mūsų srityje „ MVC Service Layer yra kažkas panašaus tarp domeno modelio ir valdiklių. Yra gana panašus šio požiūrio variantas, vadinamas MVCS , kur Store iš tikrųjų yra mūsų paslaugų sluoksnis. Store rodomi modelių pavyzdžiai ir apdorojimo tinklai, talpyklos ir pan. Norėčiau paminėti, kad jums nereikia parašyti viso tinklo ir verslo logikos paslaugų lygmeniu. Tai taip pat gali būti laikoma bloga. Norėdami gauti daugiau informacijos, žr. Kai kurie paslaugų metodai ir verslo logika gali būti apdorojami modelyje, todėl jis bus „turtingas“ (su elgesiu) modelis.

Visada plačiai naudojasi dviem bibliotekomis: AFNetworking 2.0 ir ReactiveCocoa . Manau, kad tai reikalinga bet kuriai šiuolaikinei programai, kuri sąveikauja su tinklu ir interneto paslaugomis arba kurioje yra sudėtinga vartotojo sąsajos logika.

ARCHITEKTŪRA

Pirma, sukuriu bendrąją APIClient klasę, kuri yra AFHTTPSessionManager poklasis. Tai yra viso tinklo darbo programa programoje: visos paslaugos klasės perduoda jai galiojančius REST prašymus. Jame yra visi HTTP kliento nustatymai, kurie reikalingi konkrečiai programai: SSL įpareigojimas, klaidų tvarkymas ir paprastų NSError objektų kūrimas, išsamios priežastys, dėl kurių NSError gedimai ir visų API ir ryšio klaidų aprašymai (šiuo atveju valdytojas galės rodyti teisingus naudotojo pranešimus), užklausų ir atsakymų, HTTP antraštių ir kitų su tinklu susijusių medžiagų serializatorių diegimas. Tada aš logiškai UserSerivces visus API paklausimus, susijusius su sub-paslaugomis, arba UserSerivces : UserSerivces , CommonServices , SecurityServices , FriendsServices ir pan. Kiekviena iš šių mikroservų yra atskira klasė. Kartu jie sudaro Service Layer . Šiose klasėse yra metodai kiekvienam API prašymui, proceso domeno modeliui ir visada grąžinami RACSignal su analizės modeliu arba NSError .

Norėčiau paminėti, kad jei turite sudėtingą modeliavimo serijos logiką, tada sukurkite kitą sluoksnį: kažką panašaus į duomenų žemėlapį , bet, pavyzdžiui, bendresnį. JSON / XML → Modelio keitiklis. Jei turite talpyklą: taip pat sukurkite jį kaip atskirą sluoksnį / paslaugą (verslo logiką neturėtumėte maišyti su talpykla). Kodėl? Kadangi teisingas talpyklos sluoksnis gali būti gana sudėtingas su savo klaidomis. Žmonės įgyvendina sudėtingą logiką, kad gautų patikimą, nuspėjamą talpyklą, pavyzdžiui, monoidinę talpyklą su finansinėmis prognozėmis. Jūs galite perskaityti šią gražią biblioteką, pavadintą Carlos, kad suprastumėte daugiau. Ir nepamirškite, kad pagrindiniai duomenys gali tikrai padėti jums visomis spartinimo problemomis ir leis jums parašyti mažiau logikos. Be to, jei turite logiką tarp „ NSManagedObjectContext ir serverio užklausų modelių, galite naudoti „ Repository“ modelį, kuris atskiria logiką, ištraukiančią duomenis, ir suderina jį su įmonės modeliu pagal modelį veikiančią verslo logiką. Todėl rekomenduoju naudoti saugyklos šabloną, net jei turite pagrindinę duomenų bazę. Duomenų saugykla gali NSFetchRequest tokius dalykus kaip NSFetchRequest , NSEntityDescription , NSPredicate ir kt. Padarykite paprastus metodus, pvz., get arba put .

Po visų šių veiksmų, paslaugų lygmeniu, skambintojas (peržiūros kontrolierius) gali atlikti sudėtingą asinchroninę atsako medžiagą: manipuliacijas su signalais, įrišimo, suderinimo ir kt. naudojant „ ReactiveCocoa primityvius arba tiesiog užsiregistruokite ir rodykite rezultatus rodinyje. Visose šiose APIClient paslaugos klasėse APIClient priklausomybės įpurškimą , kuris verčia konkretų paslaugos skambutį į atitinkamą get , POST , put , DELETE ir kt. užklausos pabaigos taškas REST. Tokiu atveju APIClient yra netiesiogiai perduodamas visiems valdikliams, galite tai aiškiai su APIClient priskirtomis naudingumo klasėmis. Tai gali būti prasminga, jei norite naudoti skirtingas APIClient nuostatas tam tikroms paslaugų rūšims, tačiau jei dėl kokių nors priežasčių nenorite papildomų kopijų arba esate tikri, kad visada naudosite vieną konkretų APIClient egzempliorių (be nustatymų), - padaryti jį vienu elementu, bet nereikia, nesinaudokite paslaugoms, pvz., vienišiams.

Tuomet kiekvienas peržiūros valdiklis vėl su DI įveda reikiamą paslaugų klasę, skambina atitinkamais aptarnavimo metodais ir sukuria rezultatus su vartotojo sąsajos logika. Dėl priklausomybės injekcijos norėčiau naudoti galingesnę „BloodMagic“ arba „ Typhoon“ struktūrą. Niekada nenaudoju vieno taško, APIManagerWhatever Dievo APIManagerWhatever ar kitų neteisingų dalykų. Kadangi jei iškviesite savo „ WhateverManager klasę, tai reiškia, kad jūs nežinote jo tikslo ir tai yra blogas dizaino pasirinkimas . Vienkartiniai žodžiai taip pat yra anti-modeliai, ir daugeliu atvejų (išskyrus retus) yra neteisingas sprendimas. „Singleton“ turėtų būti svarstomas tik tuo atveju, jei tenkinami visi trys kriterijai:

  • Vienos kopijos nuosavybė negali būti pagrįstai priskirta;
  • Pageidaujamas tingus inicijavimas;
  • Visuotinė prieiga nenumato kitaip.

Mūsų atveju, vieno egzemplioriaus nuosavybė nėra problema, ir mums nereikia visuotinės prieigos po to, kai mes padalinsime savo dievų valdytoją į paslaugas, nes dabar tik vienam ar keliems specialiems valdikliams reikia tam tikros paslaugos (pvz., Reikalingas „ UserProfile “ valdiklis UserServices ir tt).

SOLID sistemoje visada turėtume laikytis „ S principo ir naudoti problemos pasidalijimą , todėl nedėkite visų savo paslaugų metodų ir tinklų vienoje klasėje, nes jis išprotėja, ypač jei kuriate didelę įmonės programą. Štai kodėl turime atsižvelgti į injekcijų ir paslaugų įvedimo metodą. Manau, kad šis požiūris yra modernus ir po OO . Tokiu atveju mes padalijome savo paraišką į dvi dalis: valdymo logiką (valdiklius ir įvykius) ir parametrus.

Vienas iš parametrų yra įprastiniai duomenų parametrai. Tai mes perduodame funkcijoms, manipuliuojame, keičiame, išsaugome ir tt Tai yra subjektai, agregatai, kolekcijos, atvejų klasės. Kitas vaizdas bus „paslaugų“ parametrai. Tai klasės, apimančios verslo logiką, leidžia bendrauti su išorinėmis sistemomis ir suteikti prieigą prie duomenų.

Toliau pateikiamas bendras mano architektūros darbo pavyzdys. Tarkime, kad turime „ FriendsViewController , kuriame rodomas vartotojo draugų sąrašas, ir mes turime galimybę pašalinti iš draugų. FriendsServices metodą savo FriendsServices klasėje:

 - (RACSignal *)removeFriend:(Friend * const)friend 

kur Friend yra modelio / domeno objektas (arba jis gali būti tik User objektas, jei jie turi panašius požymius). Šis metodas analizuoja Friend į NSDictionary apie JSON parametrus friend_id , name , surname , friend_request_id ir pan. Visada naudoju „ Mantle“ biblioteką šiam tipui ir mano modelio sluoksniui (analizuojant pirmyn ir atgal, tvarkant įdėtų objektų hierarchijas JSON ir tt). Išnagrinėjęs, jis kviečia APIClient DELETE metodą atlikti tikrą REST užklausą ir grąžina RACSignal Response skambinančiam abonentui (mūsų atveju „ FriendsViewController “), kad būtų rodomas tinkamas pranešimas vartotojui ar kitam.

Jei mūsų paraiška yra labai didelė, mes vis dar turime pasidalinti savo logika. Pavyzdžiui. Ne visada patogu maišyti Repository ar modelio logiką su „ Service . Kai apibūdinau savo požiūrį, sakiau, kad „ removeFriend metodas turėtų būti „ Service sluoksnyje, bet jei mes esame labiau pedantiški, galime pastebėti, kad jis geriau susijęs su Repository . Leiskite jam prisiminti, kas yra saugykla. Ericas Evansas davė jam tikslią aprašymą [DDD] knygoje:

Saugykla pateikia visus konkretaus tipo objektus kaip konceptualų rinkinį. Jis veikia kaip kolekcija, išskyrus sudėtingesnes užklausas.

Taigi, Repository iš tikrųjų yra fasadas, kuris naudoja kolekcijos stiliaus semantiką (Pridėti, Atnaujinti, Pašalinti), kad suteiktų prieigą prie duomenų / objektų. Todėl, kai jūs turite kažką panašaus: getFriendsList , getUserGroups , removeFriend , galite jį įdėti į Repository , nes ši kolekcija yra labai suprantama čia. Ir toks kodas, kaip:

 - (RACSignal *)approveFriendRequest:(FriendRequest * const)request; 

- tai, be abejo, yra verslo logika, nes ji užima pagrindines CRUD operacijas ir jungia du domeno objektus ( Friend ir Request ), todėl ji turi būti patalpinta į Service sluoksnį. Taip pat noriu atkreipti dėmesį: nesukurkite nereikalingų abstrakcijų . Naudokite visus šiuos metodus protingai. Nes jei jūs nuslopinsite savo taikymą abstrakcijomis, tai padidins atsitiktinį sudėtingumą, o sudėtingumas sukelia daugiau problemų programinės įrangos sistemose nei bet kas kitas.

Aš jums apibūdinsiu „senojo“ tikslo C pavyzdį, tačiau šis požiūris gali būti labai lengvai pritaikomas prie „Swift“ kalbos su daug daugiau patobulinimų, nes jis turi daugiau naudingų funkcijų ir funkcionalaus cukraus. Labai rekomenduoju naudoti šią biblioteką: Moya . Tai leidžia jums sukurti elegantiškesnį APIClient sluoksnį (mūsų APIClient , kaip prisimenate). Dabar mūsų APIClient teikėjas bus vertės tipas (enum) su plėtiniais, atitinkamais protokolais ir naudojant sunaikinimo modelio žemėlapius. Swift enums + modelio suderinimas leidžia mums sukurti algebrinius duomenų tipus , kaip ir klasikiniame funkciniame programavime. Mūsų „mikroservisas“ naudos šį patobulintą „ APIClient teikėją, kaip ir įprastame „C- APIClient metode. Modelio sluoksnyje vietoj „ Mantle galite naudoti „ObjectMapper“ biblioteką arba norėčiau naudoti elegantiškesnį ir funkcionalesnį „ Argo“ .

Taigi, apibūdinau savo bendrą architektūrinį požiūrį, kuris, manau, gali būti pritaikytas bet kuriai programai. Žinoma, gali būti daug daugiau patobulinimų. Aš patariu studijuoti funkcinį programavimą, nes jūs galite daug naudos iš to, bet ne per daug. Paprastai gera praktika yra nereikalingos, bendros pasaulinės kintančios būklės pašalinimas, nekintamo domeno modelio kūrimas arba grynų funkcijų kūrimas be išorinių šalutinių poveikių, o nauja kalba „ Swift skatina. Bet visada atminkite, kad kodų perkrovimas su sunkiais švariais funkciniais šablonais, teoriniai teoriniai metodai yra bloga idėja, nes kiti kūrėjai skaitys ir prižiūrės jūsų kodą, ir jie gali būti nusiminę ar baisūs apie prismatic profunctors ir tokius dalykus, kurių nepakeista modelius. Tas pats su „ ReactiveCocoa : nereikia RACify savo kodo per daug , nes jis gali tapti neįskaitomas labai greitai, ypač pradedantiesiems, naudokite jį, kai jis tikrai gali supaprastinti jūsų tikslus ir logiką.

Taigi, read a lot, mix, experiment, and try to pick up the best from different architectural approaches . Tai yra geriausias patarimas, kurį galiu jums suteikti.

289
11 июня '14 в 20:19 2014-06-11 20:19 atsakymą pateikė Aleksandras Karaberovas birželio 14 d. 14 val. 20:19 2014-06-11 20:19

Pagal šio klausimo tikslą norėčiau apibūdinti mūsų požiūrį į architektūrą.

Architektūrinis požiūris

Mūsų bendra „iOS“ programų architektūra grindžiama šiais modeliais: paslaugų lygiai , MVVM , naudotojo sąsajos duomenų rišimas , priklausomybės įpurškimas ; ir funkcinis reaktyvus programavimas .

Mes galime sumažinti įprastą vartotojo susidūrimą su šiais loginiais sluoksniais:

  • Asamblėja
  • Modelis
  • Paslaugos
  • Saugojimas
  • Vadybininkai
  • Koordinatoriai
  • sąsaja
  • Infrastruktūra

Surinkimo sluoksnis yra mūsų taikymo pradžios taškas. Jame yra talpykla, skirta priklausomybei nuo injekcijos ir paraiškos objektų deklaracijoms bei jų priklausomybėms. Šiame sluoksnyje taip pat gali būti programų konfigūracijos (URL, trečiosios šalies paslaugų raktai ir tt). Tam mes naudojame Typhoon biblioteką.

Modelio lygmenyje yra domenų modelių klasės, patvirtinimas, atitikimas. Mantle biblioteką naudojame mūsų modelių žemėlapiams: jis palaiko serializaciją / deserializaciją JSON ir NSManagedObject formatuose. Mes naudojame FXForms ir FXModelValidation bibliotekas, kad patikrintume ir pateiktume savo modelių formą.

Paslaugų lygis deklaruoja paslaugas, kuriomis mes naudojame, kad galėtume bendrauti su išorinėmis sistemomis, kad galėtume siųsti ar gauti duomenis, pateiktus mūsų domeno modelyje. Paprastai teikiame paslaugas, skirtas bendrauti su serverio API (kiekvienam objektui), pranešimų paslaugoms (pvz., PubNub ), saugojimo paslaugoms (pvz., „Amazon S3“) ir kt. Iš esmės, paslaugos apgaubia SDK pateiktus objektus (pvz., PubNub SDK) arba įgyvendina savo komunikacijos logiką. Для общих сетей мы используем библиотеку AFNetworking .

Уровень хранилища предназначен для организации локального хранения данных на устройстве. Мы используем Core Data или Realm для этого (оба имеют плюсы и минусы, решение о том, что использовать, основано на конкретных спецификациях). Для настройки Core Data мы используем MDMCoreData библиотеку и набор классов - хранилищ - (похожих на службы), которые обеспечивают доступ к локальному хранилищу для каждого объекта. Для Realm мы просто используем аналогичные хранилища для доступа к локальному хранилищу.

Уровень менеджеров - это место, где живут наши абстракции/обертки.