Pada artikel sebelumnya , kami memeriksa titik-titik yang tidak nyaman dalam sistem tipe GraphQL.
Dan sekarang kita akan mencoba untuk mengalahkan beberapa dari mereka. Semua tertarik, tolong, di bawah kucing.
Penomoran partisi sesuai dengan masalah yang berhasil saya atasi.
Pada titik ini, kami memeriksa ambiguitas yang menghasilkan fitur implementasi nullable di GraphQL.
Dan masalahnya adalah bahwa hal itu tidak memungkinkan untuk menerapkan konsep pembaruan parsial dari awal - analog dari metode HTTP PATCH
dalam arsitektur REST. Dalam komentar tentang materi masa lalu, saya banyak dikritik karena pemikiran "REST". Saya hanya bisa mengatakan bahwa arsitektur CRUD mengharuskan saya untuk ini. Dan saya belum siap untuk melepaskan manfaat dari REST, hanya karena "jangan lakukan ini." Ya, dan solusi untuk masalah ini ditemukan.
Jadi, kembali ke masalahnya. Seperti yang kita semua tahu, skrip CRUD saat memperbarui catatan terlihat seperti ini:
- Punya catatan dari belakang.
- Bidang rekaman yang diedit.
- Mengirim catatan ke belakang.
Konsep pembaruan parsial, dalam hal ini, harus memungkinkan kami untuk mengirim kembali hanya bidang-bidang yang telah diubah.
Jadi, jika kita mendefinisikan model input dengan cara ini
input ExampleInput { foo: String! bar: String }
lalu ketika memetakan variabel tipe ExampleInput
dengan nilai ini
{ "foo": "bla-bla-bla" }
pada DTO dengan struktur ini:
ExampleDTO { foo: String
kami mendapatkan objek DTO dengan nilai ini:
{ foo: "bla-bla-bla", bar: null }
dan saat memetakan variabel dengan nilai ini
{ "foo": "bla-bla-bla", "bar": null }
kami mendapatkan objek DTO dengan nilai yang sama seperti terakhir kali:
{ foo: "bla-bla-bla", bar: null }
Yaitu, terjadi entropi - kami kehilangan informasi tentang apakah bidang ditransmisikan dari klien atau tidak.
Dalam kasus ini, tidak jelas apa yang perlu dilakukan dengan bidang objek akhir: jangan menyentuhnya karena klien tidak lulus bidang, atau mengaturnya ke null
karena klien lulus null
.
Sebenarnya, GraphQL adalah protokol RPC. Dan saya mulai memikirkan bagaimana saya melakukan hal-hal seperti itu di belakang dan prosedur apa yang harus saya panggil untuk melakukan persis seperti yang saya inginkan. Dan di backend, saya melakukan pembaruan parsial bidang seperti ini:
$repository->find(42)->setFoo('bla-bla-lba');
Artinya, saya benar-benar tidak menyentuh setter properti entitas, kecuali saya perlu mengubah nilai properti ini. Jika Anda menggeser ini ke skema GraphQL, Anda mendapatkan hasil berikut:
type Mutation { entityRepository: EntityManager! } type EntityManager { update(id: ID!): PersitedEntity } type PersitedEntity { setFoo(foo: String!): String! setBar(foo: String): String }
Sekarang, jika kita mau, kita bisa memanggil metode setBar
, dan mengatur nilainya ke nol, atau tidak menyentuh metode ini, dan kemudian nilainya tidak akan berubah. Dengan demikian, implementasi partial update
bagus keluar. Tidak lebih buruk dari PATCH
dari REST yang terkenal.
Dalam komentar tentang materi sebelumnya, summerwind bertanya: mengapa kita perlu partial update
? Saya menjawab: ada bidang yang SANGAT besar.
3. Polimorfisme
Sering terjadi bahwa Anda perlu tunduk pada entitas input yang agak "satu dan sama" tetapi tidak cukup. Saya akan menggunakan contoh membuat akun dari materi sebelumnya.
# AccountInput { login: "Acme", password: "***", subject: OrganiationInput { title: "Acme Inc" } }
# AccountInput { login: "Acme", password: "***", subject: PersonInput { firstName: "Vasya", lastName: "Pupkin", } }
Jelas, kami tidak dapat mengirimkan data dengan struktur seperti itu untuk satu argumen - GraphQL tidak akan mengizinkan kami melakukan ini. Jadi, Anda harus menyelesaikan masalah ini.
Metode 0 - dahi
Hal pertama yang terlintas dalam pikiran adalah pemisahan bagian variabel dari input:
input AccountInput { login: String! password: Password! subjectOrganization: OrganiationInput subjectPerson: PersonInput }
Hmm ... ketika saya melihat kode seperti itu, saya sering mengingat Josephine Pavlovna. Itu tidak cocok untukku.
Metode 1 - tidak di dahi, tetapi di dahi
Kemudian fakta datang ke bantuan saya bahwa untuk mengidentifikasi entitas, saya menggunakan saya menggunakan UUID (umumnya saya merekomendasikannya kepada semua orang - itu akan membantu lebih dari sekali). Dan ini berarti bahwa saya dapat membuat entitas yang valid secara langsung pada klien, mengikatnya bersama dengan pengidentifikasi, dan mengirimkannya ke back-end secara terpisah.
Maka kita dapat melakukan sesuatu dalam semangat:
input AccountInput { login: String! password: Password! subject: SubjectSelectInput! } input SubjectSelectInput { id: ID! } type Mutation { createAccount( organization: OrganizationInput, person: PersonInput, account: AccountInput! ): Account! }
atau, yang ternyata lebih nyaman (mengapa lebih nyaman, saya akan memberi tahu Anda ketika kita sampai pada generasi antarmuka pengguna), membaginya menjadi metode yang berbeda:
type Mutation { createAccount(account: AccountInput!): Account! createOrganization(organization: OrganizationInput!): Organization! createPerson(person: PersonInput!) : Person! }
Kemudian, kita perlu mengirim permintaan untuk membuat Akun dan membuat Organisasi / membuat Orang
satu batch. Perlu dicatat bahwa pemrosesan batch harus dibungkus dalam suatu transaksi.
Metode 2 - skalar ajaib
Caranya adalah skalar di GraphQL tidak hanya Int
, Sting
, Float
, dll. Ini umumnya apa saja (well, sementara JSON dapat menangani ini, tentu saja).
Maka kita bisa mendeklarasikan skalar:
scalar SubjectInput
Lalu, tulis pawang Anda di situ, dan itu tidak dikukus. Kemudian kita dapat dengan mudah memasukkan bidang variabel ke input.
Cara mana yang harus dipilih? Saya menggunakan keduanya, dan telah mengembangkan aturan untuk diri saya sendiri:
Jika entitas induk adalah Root Agregat untuk anak, maka saya memilih metode kedua, jika tidak, yang pertama.
4. Generik.
Semuanya sepele di sini dan saya tidak menghasilkan sesuatu yang lebih baik daripada pembuatan kode. Dan tanpa Rails (paket railt / sdl) saya tidak akan bisa (atau lebih tepatnya, saya akan melakukan hal yang sama dengan kruk). Kuncinya adalah bahwa Rail memungkinkan Anda untuk menentukan arahan tingkat dokumen (tidak ada posisi untuk arahan dalam lembar spesifikasi).
directive @example on DOCUMENT
Artinya, arahan tidak dilampirkan pada apa pun selain dokumen di mana mereka dipanggil.
Saya memperkenalkan arahan berikut:
directive @defineMacro(name: String!, template: String!) on DOCUMENT directive @macro(name: String!, arguments: [String]) on DOCUMENT
Saya pikir tidak ada yang perlu menjelaskan esensi makro ...
Itu saja untuk saat ini. Saya tidak berpikir bahwa materi ini akan menyebabkan suara sebanyak masa lalu. Semua sama, judul di sana cukup "kuning")
Dalam komentar di artikel sebelumnya, warga Khabrovsk tenggelam karena berbagi akses ... sehingga artikel selanjutnya adalah tentang otorisasi.