Bagaimana kami menerjemahkan proyek lawas ke GraphQL

Hai, Habr. Nama saya Anton Potapov, saya adalah pengembang iOS di FINCH . Hari ini saya ingin berbicara secara rinci tentang bagaimana menerjemahkan proyek seluler ke GraphQL, menjelaskan pro dan kontra dari pendekatan ini. Mari kita mulai.

Pengantar singkat


"Warisan." Saya pikir semua orang mendengar kata mengerikan ini, dan sebagian besar bertemu muka dengannya. Karenanya, Anda tidak perlu memberi tahu betapa sulitnya mengintegrasikan fitur baru dan hebat ke dalam proyek Anda.

gambar

Salah satu proyek kami adalah aplikasi untuk perusahaan lotre besar (saya tidak bisa memberi nama sejak NDA). Baru-baru ini, saya perlu menerjemahkannya ke GraphQL tanpa kehilangan akal.

Proyek ini sangat besar - pada pandangan pertama, perlu menghabiskan setidaknya 200-300 jam, tetapi ini melampaui semua kemungkinan sprint dua minggu. Kami juga tidak dapat mengalokasikan seluruh sprint hanya untuk satu tugas, karena ada juga fitur samping, yang tidak kalah pentingnya dari GraphQL.
Kami sudah lama berpikir apa yang harus dilakukan dan memutuskan untuk menerjemahkan proyek langkah demi langkah, model demi model. Dengan pendekatan ini, transisi akan mulus, dan 200-300 jam akan didistribusikan dalam beberapa sprint.

Poin teknis


Untuk mengerjakan proyek, saya menggunakan perpustakaan Apollo. Sudah ada artikel tentang Habré yang menjelaskan semua seluk-beluk bekerja dengannya, jadi saya tidak akan mengulanginya lagi. Saya memperingatkan Anda sebelumnya - bekerja dengan model yang dihasilkan kode tidak terlalu nyaman dan lebih baik menyimpannya sebagai "jaringan". Setiap entitas berisi __typename: bidang String, yang, anehnya, mengembalikan nama jenis.

Dekomposisi tugas


Hal pertama yang harus dilakukan adalah menentukan model Legacy mana yang akan kami terjemahkan ke dalam GraphQL. Dalam kasus saya, logis untuk menerjemahkan salah satu model GameInfo besar, dan DrawInfo tertanam di dalamnya.

  • GameInfo - berisi informasi tentang permainan lotere dan undian.
  • DrawInfo - berisi data tentang sirkulasi

Setelah menyelesaikan pilihan, saya memutuskan untuk menginisialisasi model lama dengan yang baru diperoleh dengan GraphQL. Dengan pendekatan ini, tidak perlu mengganti bagian-bagian aplikasi di mana model lama digunakan. Menyelesaikan model sebelumnya sepenuhnya berisiko - bukan fakta bahwa proyek akan bekerja seperti sebelumnya, dan itu akan memakan waktu terlalu banyak.

Secara total, ada tiga tahap implementasi GraphQL:

  • Ciptaan pelanggan;
  • Membuat penginisialisasi untuk model Legacy;
  • Mengganti permintaan API dengan GraphQL.

GraphQLClient


Seperti halnya klien mana pun, GraphQLClient harus memiliki metode pengambilan, setelah itu akan memuat data yang kami butuhkan.

Menurut pendapat saya, metode mengambil untuk GraphQLClient harus mengambil enum, berdasarkan permintaan yang sesuai akan dieksekusi. Pendekatan ini akan memungkinkan kita untuk dengan mudah menerima data untuk entitas yang diinginkan atau bahkan layar. Jika permintaan menerima parameter, maka mereka akan diteruskan sebagai nilai terkait. Dengan pendekatan ini, lebih mudah menggunakan GraphQL dan membuat kueri yang fleksibel.

enum GraphQLRequest { case image(ImageId?) } 

Klien GraphQL kami yang disesuaikan harus mengandung ApolloClient yang dikustomisasi untuk fitur yang ada, serta metode pengambilan pemuatan yang disebutkan di atas.

 func fetch<Response>(requestType: GraphQLRequest, completion: @escaping (QLRequestResult<Response>) -> Void) 

Apa itu QLRequestResult? Itu biasa

  typealias QLRequestResult<Response> = Result<Response, APIError> 

Metode pengambilan memungkinkan Anda untuk mengakses klien melalui protokol dan melakukan pergantian pada requestType. Bergantung pada requestType, Anda dapat menggunakan metode pemuatan pribadi terkait, tempat Anda dapat mengonversi model yang dihasilkan ke yang lama. Sebagai contoh:

  private func fetchGameInfo<Response>(gameId: String? = "", completion: @escaping (QLRequestResult<Response>) -> Void) { //   Apollo  let query = GetLotteriesQuery(input: gameId) //       apolloClient.fetch(query: query, cachePolicy: .returnCacheDataAndFetch, queue: .global()) { result in switch result { case .success(let response): guard let gamesQL = response.data?.games, let info = gamesQL.info else { completion(.failure(.decodingError)) return } //    let infos: [Games.Info] = info.compactMap({ gameInfo -> Games.Info? in guard let gameIdRaw = gameInfo?.gameId, let gameId = GameId(rawValue: gameIdRaw), let bonusMultiplier = gameInfo?.bonusMultiplier, let maxTicketCost = gameInfo?.maxTicketCost, let currentDraws = gameInfo?.currentDraws else { return nil } let currentDrawsInfo = Games.Info.CurrentDrawsInfo(currntDraw: currentDraws) let gameInfo = Games.Info(gameId: gameId, bonusMultiplier: bonusMultiplier, maxTicketCost: maxTicketCost, currentDraws: currentDrawsInfo) return gameInfo }) //    let games = Games(info: infos) guard let response = games as? Response else { completion(.failure(.decodingError)) return } completion(.success(response)) case .failure(let error): … } } } 

Hasilnya, kami mendapatkan model lama yang sudah jadi yang diperoleh dari GraphQL.

Jenis skalar


Dalam dokumentasi GraphQL, tipe skalar dijelaskan sebagai berikut: "Tipe skalar adalah pengidentifikasi unik yang sering digunakan untuk memilih ulang objek atau sebagai kunci untuk cache." Untuk cepat, tipe skalar dapat dengan mudah dikaitkan dengan typealias.

Saat menulis klien, saya mengalami masalah bahwa dalam skema saya ada jenis skalar Long, yang pada dasarnya

 typealias Long = Int64 

Semuanya akan baik-baik saja, tetapi Apollo secara otomatis melemparkan semua skalar pengguna ke String, yang menyebabkan crash. Saya memecahkan masalah seperti ini:

  • Saya menambahkan skrip codegen –passthroughCustomScalars
  • Membuat file terpisah untuk typealias
  • Ditambahkan ke file
     public typealias Long = Int64 

Di masa depan, untuk setiap jenis skalar, Anda perlu menambahkan typealias baru. Dengan Apollo versi 14.0, mereka menambahkan "dukungan untuk skal khusus Int." Jika Anda ingin menghindari menggunakan pendekatan ini dan bendera di codegen, maka periksa solusi untuk masalah ini di Apollo git .

Pro dan kontra


Mengapa pendekatan ini bagus? Transisi yang cukup cepat untuk menggunakan GraphQL, mengenai pendekatan dengan menghapus semua model lama.

Di masa depan, ketika kita perlu mendapatkan data untuk layar / model apa pun, kita dapat beralih ke GraphQLClient dan mendapatkan data yang diperlukan.

Dari minus:

  • Mengkonversi jenis model di klien, seseorang dapat mentransfer ke entitas lain sejak itu bekerja dengan data bukan tanggung jawab klien.
  • Grafik LuasQLClient. Setelah menambahkan kueri baru, kelas kami akan tumbuh semakin besar. Salah satu solusinya adalah ekstensi di mana metode pemuatan akan dijelaskan, yang saya bicarakan di bab GraphQLClient.

Kesimpulan


Secara umum, beralih ke GraphQL bisa cepat dan tidak menyakitkan dengan klien yang ditulis dengan baik. Butuh waktu sekitar tiga hari = 24 jam kerja. Selama waktu ini, saya berhasil membuat klien, menerjemahkan model dan membuat kembali model GameInfo. Pendekatan yang diuraikan bukanlah obat mujarab, tetapi murni keputusan saya, dilaksanakan dalam waktu singkat.

Jika Anda memiliki guru GraphQL di antara Anda, saya sarankan berbagi pengalaman Anda dan mengatakan betapa nyamannya menggunakan GraphQL + Apollo dalam proyek-proyek besar. Atau apakah gamenya tidak bernilai lilin?

Terima kasih atas perhatian anda!

Source: https://habr.com/ru/post/id470529/


All Articles