I want to understand basic, abstract and correct architectural approach for networking applications in iOS
: tidak ada pendekatan "yang terbaik", atau "yang paling benar" untuk membangun arsitektur aplikasi. Ini adalah pekerjaan yang sangat kreatif. Anda harus selalu memilih arsitektur yang paling mudah dan dapat dikembangkan, yang akan jelas bagi pengembang mana pun, yang mulai mengerjakan proyek Anda atau pengembang lain di tim Anda, tetapi saya setuju, bahwa mungkin ada "baik" dan "buruk" " Arsitektur.
Anda berkata: collect the most interesting approaches from experienced iOS developers
Saya tidak berpikir bahwa pendekatan saya adalah yang paling menarik atau benar, tetapi saya telah menggunakannya dalam beberapa proyek dan puas dengan itu. Ini adalah pendekatan hybrid dari yang telah Anda sebutkan di atas, dan juga dengan peningkatan dari upaya penelitian saya sendiri. Saya menarik dalam masalah membangun pendekatan, yang menggabungkan beberapa pola dan idiom terkenal. Saya pikir banyak pola perusahaan Fowler dapat berhasil diterapkan ke aplikasi seluler. Berikut adalah daftar yang paling menarik, yang dapat kami terapkan untuk membuat arsitektur aplikasi iOS ( menurut saya ): Lapisan Layanan , Unit Kerja , Fasad Jarak Jauh , Objek Transfer Data ,Gateway , Layer Supertype , Kasus Khusus , Model Domain . Anda harus selalu merancang lapisan model dengan benar dan jangan lupa tentang kegigihan (ini dapat secara signifikan meningkatkan kinerja aplikasi Anda). Anda bisa menggunakannya Core Data
untuk ini. Tapi Anda tidak boleh lupa, itu Core Data
bukan ORM atau database, tetapi manajer grafik objek dengan ketekunan sebagai pilihan yang baik. Jadi, sangat sering Core Data
bisa terlalu berat untuk kebutuhan Anda dan Anda dapat melihat solusi baru seperti Realm dan Couchbase Lite , atau membangun pemetaan objek / lapisan kegigihan ringan Anda sendiri, berdasarkan pada SQLite mentah atau LevelDB. Saya juga menyarankan Anda untuk membiasakan diri dengan Desain Berbasis Domain dan CQRS .
Pada awalnya, saya pikir, kita harus membuat layer lain untuk jaringan, karena kita tidak ingin pengendali gemuk atau model yang berat dan kewalahan. Saya tidak percaya pada fat model, skinny controller
hal - hal itu. Tetapi saya benar- benar percaya pada skinny everything
pendekatan, karena tidak ada kelas yang harus gemuk, tidak pernah. Semua jaringan secara umum dapat diabstraksi sebagai logika bisnis, akibatnya kita harus memiliki lapisan lain, di mana kita dapat meletakkannya. Layer Layanan adalah yang kami butuhkan:
It encapsulates the application's business logic, controlling transactions
and coordinating responses in the implementation of its operations.
Di MVC
ranah kami Service Layer
adalah sesuatu seperti mediator antara model domain dan pengontrol. Ada variasi yang agak mirip dari pendekatan ini yang disebut MVCS di mana a Store
sebenarnya adalah Service
layer kita . Store
contoh vends model dan menangani jaringan, caching dll. Saya ingin menyebutkan bahwa Anda tidak harus menulis semua jaringan dan logika bisnis di lapisan layanan Anda. Ini juga dapat dianggap sebagai desain yang buruk. Untuk info lebih lanjut, lihat model domain Anemic and Rich . Beberapa metode layanan dan logika bisnis dapat ditangani dalam model, sehingga akan menjadi model "kaya" (dengan perilaku).
Saya selalu menggunakan dua pustaka: AFNetworking 2.0 dan ReactiveCocoa . Saya pikir itu harus dimiliki untuk aplikasi modern yang berinteraksi dengan jaringan dan layanan web atau mengandung logika UI yang kompleks.
ARSITEKTUR
Pada awalnya saya membuat APIClient
kelas umum , yang merupakan subclass dari AFHTTPSessionManager . Ini adalah pekerja keras dari semua jaringan dalam aplikasi: semua kelas layanan mendelegasikan permintaan REST yang sebenarnya. Ini berisi semua penyesuaian klien HTTP, yang saya butuhkan dalam aplikasi tertentu: SSL menyematkan, memproses kesalahan dan membuat NSError
objek langsung dengan alasan kegagalan terperinci dan deskripsi semua API
dan kesalahan koneksi (dalam hal ini pengontrol akan dapat menampilkan pesan yang benar untuk pengguna), mengatur permintaan dan tanggapan serializer, header http dan hal-hal lain yang berhubungan dengan jaringan. Lalu aku logis membagi semua permintaan API ke subservices atau, lebih tepatnya, microservices : UserSerivces
, CommonServices
, SecurityServices
,FriendsServices
dan seterusnya, sesuai dengan logika bisnis yang mereka terapkan. Masing-masing layanan microser ini adalah kelas yang terpisah. Mereka, bersama-sama, membentuk a Service Layer
. Kelas-kelas ini berisi metode untuk setiap permintaan API, memproses model domain dan selalu mengembalikan a RACSignal
dengan model respons yang diurai atau NSError
ke pemanggil.
Saya ingin menyebutkan bahwa jika Anda memiliki logika serialisasi model yang kompleks - kemudian buat layer lain untuk itu: sesuatu seperti Data Mapper tetapi lebih umum misalnya JSON / XML -> Model mapper. Jika Anda memiliki cache: maka buatlah sebagai layer / layanan terpisah juga (Anda seharusnya tidak mencampur logika bisnis dengan caching). Mengapa? Karena lapisan caching yang benar bisa sangat kompleks dengan gotcha sendiri. Orang-orang menerapkan logika kompleks untuk mendapatkan caching yang valid dan dapat diprediksi seperti caching monoid dengan proyeksi berdasarkan para profesional. Anda dapat membaca tentang perpustakaan yang indah ini bernama Carlos untuk lebih mengerti. Dan jangan lupa bahwa Data Inti benar-benar dapat membantu Anda dengan semua masalah caching dan akan memungkinkan Anda untuk menulis lebih sedikit logika. Juga, jika Anda memiliki beberapa logika antara NSManagedObjectContext
dan model permintaan server, Anda dapat menggunakanPola repositori , yang memisahkan logika yang mengambil data dan memetakannya ke model entitas dari logika bisnis yang bertindak pada model. Jadi, saya menyarankan untuk menggunakan pola Repositori bahkan ketika Anda memiliki arsitektur berbasis Core Data. Hal-hal abstrak Repository kaleng, seperti NSFetchRequest
, NSEntityDescription
, NSPredicate
dan sebagainya untuk metode biasa seperti get
atau put
.
Setelah semua tindakan ini di lapisan Layanan, pemanggil (view controller) dapat melakukan beberapa hal asinkron yang kompleks dengan respons: manipulasi sinyal, rantai, pemetaan, dll. Dengan bantuan ReactiveCocoa
primitif, atau hanya berlangganan dan menunjukkan hasilnya dalam tampilan . Saya menyuntikkan dengan Dependency Injection di semua kelas layanan ini saya APIClient
, yang akan menerjemahkan panggilan layanan tertentu ke yang sesuai GET
, POST
, PUT
, DELETE
, dll permintaan ke titik akhir REST. Dalam hal APIClient
ini diteruskan secara implisit ke semua pengontrol, Anda dapat membuat ini eksplisit dengan parametris atas APIClient
kelas layanan. Ini bisa masuk akal jika Anda ingin menggunakan berbagai penyesuaianAPIClient
untuk kelas layanan tertentu, tetapi jika Anda, untuk beberapa alasan, tidak ingin salinan tambahan atau Anda yakin bahwa Anda akan selalu menggunakan satu contoh khusus (tanpa penyesuaian) dari APIClient
- membuat singleton, tapi JANGAN, tolong JANGAN Akan membuat kelas layanan sebagai lajang.
Kemudian setiap view controller lagi dengan DI menyuntikkan kelas layanan yang dibutuhkan, memanggil metode layanan yang sesuai dan menyusun hasilnya dengan logika UI. Untuk injeksi ketergantungan saya suka menggunakan BloodMagic atau kerangka kerja Topan yang lebih kuat . Saya tidak pernah menggunakan lajang, APIManagerWhatever
kelas dewa atau hal-hal salah lainnya. Karena jika Anda memanggil kelas Anda WhateverManager
, ini menunjukkan bahwa Anda tidak tahu tujuannya dan itu adalah pilihan desain yang buruk . Lajang juga merupakan anti-pola, dan dalam kebanyakan kasus (kecuali yang langka) adalah solusi yang salah . Singleton harus dipertimbangkan hanya jika ketiga kriteria berikut dipenuhi:
- Kepemilikan instance tunggal tidak dapat ditetapkan secara wajar;
- Inisialisasi malas diinginkan;
- Akses global tidak disediakan.
Dalam kasus kami, kepemilikan satu instance tidak menjadi masalah dan kami juga tidak memerlukan akses global setelah kami membagi manajer god kami menjadi layanan, karena sekarang hanya satu atau beberapa pengontrol khusus yang memerlukan layanan tertentu (mis. UserProfile
Kebutuhan pengontrol UserServices
dan sebagainya) .
Kami harus selalu menghormati S
prinsip dalam SOLID dan menggunakan pemisahan kekhawatiran , jadi jangan letakkan semua metode layanan dan panggilan jaringan Anda dalam satu kelas, karena ini gila, terutama jika Anda mengembangkan aplikasi perusahaan besar. Itu sebabnya kita harus mempertimbangkan injeksi ketergantungan dan pendekatan layanan. Saya menganggap pendekatan ini sebagai modern dan pasca-OO . Dalam hal ini kami membagi aplikasi kami menjadi dua bagian: kontrol logika (pengontrol dan peristiwa) dan parameter.
Satu jenis parameter adalah parameter "data" biasa. Itulah yang kami sampaikan fungsi, memanipulasi, memodifikasi, bertahan, dll. Ini adalah entitas, agregat, koleksi, kelas kasus. Jenis lain akan menjadi parameter "layanan". Ini adalah kelas yang merangkum logika bisnis, memungkinkan berkomunikasi dengan sistem eksternal, menyediakan akses data.
Berikut ini adalah alur kerja umum arsitektur saya. Misalkan kita memiliki FriendsViewController
, yang menampilkan daftar teman pengguna dan kita memiliki opsi untuk menghapus dari teman. Saya membuat metode di FriendsServices
kelas saya yang disebut:
- (RACSignal *)removeFriend:(Friend * const)friend
di mana Friend
model / objek domain (atau bisa juga hanya User
objek jika mereka memiliki atribut yang sama). Underhood metode ini mem-parsing Friend
untuk NSDictionary
parameter JSON friend_id
, name
, surname
, friend_request_id
dan sebagainya. Saya selalu menggunakan pustaka Mantle untuk jenis boilerplate dan untuk lapisan model saya (parsing maju dan mundur, mengelola hierarki objek bersarang di JSON dan sebagainya). Setelah parsing itu panggilan APIClient
DELETE
metode untuk membuat permintaan SISA aktual dan kembali Response
dalam RACSignal
ke pemanggil ( FriendsViewController
dalam kasus kami) untuk menampilkan pesan yang sesuai untuk pengguna atau apa pun.
Jika aplikasi kita sangat besar, kita harus memisahkan logika kita lebih jelas. Misalnya tidak selalu baik untuk mencampur Repository
atau memodelkan logika dengan Service
satu. Ketika saya menjelaskan pendekatan saya, saya telah mengatakan bahwa removeFriend
metode harus di Service
lapisan, tetapi jika kita akan lebih bertele-tele kita dapat melihat bahwa itu lebih baik milik Repository
. Mari kita ingat apa itu Repositori. Eric Evans memberikan deskripsi yang tepat dalam bukunya [DDD]:
Repositori merepresentasikan semua objek dari tipe tertentu sebagai set konseptual. Kerjanya seperti koleksi, kecuali dengan kemampuan kueri yang lebih rumit.
Jadi, pada Repository
dasarnya adalah fasad yang menggunakan semantik gaya Koleksi (Tambah, Perbarui, Hapus) untuk menyediakan akses ke data / objek. Itu sebabnya ketika Anda memiliki sesuatu seperti: getFriendsList
, getUserGroups
, removeFriend
Anda dapat menempatkannya di Repository
, karena koleksi-seperti semantik yang cukup jelas di sini. Dan kode seperti:
- (RACSignal *)approveFriendRequest:(FriendRequest * const)request;
jelas merupakan logika bisnis, karena itu di luar CRUD
operasi dasar dan menghubungkan dua objek domain ( Friend
dan Request
), itu sebabnya ia harus ditempatkan di Service
lapisan. Saya juga ingin memperhatikan: jangan membuat abstraksi yang tidak perlu . Gunakan semua pendekatan ini dengan bijak. Karena jika Anda akan membanjiri aplikasi Anda dengan abstraksi, ini akan meningkatkan kompleksitas aksidentalnya, dan kompleksitas menyebabkan lebih banyak masalah dalam sistem perangkat lunak daripada yang lainnya
Saya menggambarkan Anda contoh Objective-C "lama" tetapi pendekatan ini bisa sangat mudah diadaptasi untuk bahasa Swift dengan lebih banyak perbaikan, karena ia memiliki fitur yang lebih berguna dan gula fungsional. Saya sangat merekomendasikan untuk menggunakan perpustakaan ini: Moya . Hal ini memungkinkan Anda untuk membuat APIClient
layer yang lebih elegan (pekerja keras kami seperti yang Anda ingat). Sekarang APIClient
penyedia kami akan menjadi tipe nilai (enum) dengan ekstensi yang sesuai dengan protokol dan meningkatkan pencocokan pola perusakan. Pencocokan enum + pola Swift memungkinkan kita untuk membuat tipe data aljabar seperti pada pemrograman fungsional klasik. Layanan microser kami akan menggunakan APIClient
penyedia yang ditingkatkan ini seperti pada pendekatan Objective-C yang biasa. Untuk lapisan model alih-alih Mantle
Anda bisa menggunakan perpustakaan ObjectMapperatau saya suka menggunakan perpustakaan Argo yang lebih elegan dan fungsional .
Jadi, saya menggambarkan pendekatan arsitektur umum saya, yang dapat disesuaikan untuk aplikasi apa pun, saya pikir. Tentu saja bisa ada banyak perbaikan. Saya menyarankan Anda untuk belajar pemrograman fungsional, karena Anda bisa mendapatkan banyak manfaat dari itu, tetapi jangan terlalu jauh dengan itu. Menghilangkan keadaan yang bisa berubah secara global, dibagikan, dapat diubah, membuat model domain yang tidak dapat diubah atau membuat fungsi murni tanpa efek samping eksternal, umumnya, merupakan praktik yang baik, dan Swift
bahasa baru mendorong hal ini. Tetapi selalu ingat, bahwa membebani kode Anda dengan pola fungsional murni yang murni, pendekatan kategori-teoretis adalah ide yang buruk , karena pengembang lain akan membaca dan mendukung kode Anda, dan mereka dapat frustrasi atau menakutkan dariprismatic profunctors
dan hal-hal semacam itu dalam model abadi Anda. Hal yang sama dengan ReactiveCocoa
: jangan RACify
kode Anda terlalu banyak , karena dapat menjadi sangat cepat dibaca, terutama untuk pemula. Gunakan ketika itu benar-benar dapat menyederhanakan tujuan dan logika Anda.
Jadi, read a lot, mix, experiment, and try to pick up the best from different architectural approaches
. Itu adalah saran terbaik yang bisa saya berikan kepada Anda.