[GraphQLの何が問題なのか] ...そしてその対処方法

前回の記事では、GraphQL型システムの不便な点を調べました。

そして今、それらのいくつかを打ち負かそうとします。 猫の下で興味を持ってください。







セクションの番号は、私が何とか対処できた問題に対応しています。







1.2 NON_NULL INPUT



この時点で、GraphQLでNULL可能実装機能を生成するあいまいさを調べました。







そして問題は、部分更新の概念をゼロから実装できないことです。これは、RESTアーキテクチャのHTTP PATCH



メソッドに類似しています。 過去の資料に関するコメントの中で、私は「REST」の考え方について強く批判されました。 私はCRUDアーキテクチャが私にこれを義務付けていると言うだけです。 そして、「これをしないで」という理由だけで、RESTの利点を放棄する準備ができていませんでした。 そして、この問題の解決策が見つかりました。







そして、問題に戻りましょう。 ご存じのとおり、エントリを更新するときのCRUDスクリプトは次のようになります。







  1. 後ろからレコードを取得しました。
  2. 編集されたレコードフィールド。
  3. 奥にレコードを送りました。


この場合、部分更新の概念により、変更されたフィールドのみを送り返すことができます。

したがって、この方法で入力モデルを定義すると







 input ExampleInput { foo: String! bar: String }
      
      





次に、この値でExampleInput



型の変数をマッピングするとき







 { "foo": "bla-bla-bla" }
      
      





この構造を持つDTOで:







 ExampleDTO { foo: String #   bar: ?String #   }
      
      





この値を持つDTOオブジェクトを取得します。







 { foo: "bla-bla-bla", bar: null }
      
      





そして、この値を持つ変数をマッピングするとき







 { "foo": "bla-bla-bla", "bar": null }
      
      





前回と同じ値を持つDTOオブジェクトを取得します。







 { foo: "bla-bla-bla", bar: null }
      
      





つまり、エントロピーが発生します。フィールドがクライアントから送信されたかどうかに関する情報が失われます。

この場合、最終オブジェクトのフィールドで何を行う必要があるのか​​は明確ではありません。クライアントがフィールドを渡さなかったために触れたり、クライアントがnull



渡したためにnull



設定したりしないでください。







厳密に言えば、GraphQLはRPCプロトコルです。 そして、私は背中でそのようなことをどのように行うか、そして私が望む方法を正確に行うためにどの手順を呼び出すべきかについて考え始めました。 バックエンドでは、次のようにフィールドを部分的に更新します。







 $repository->find(42)->setFoo('bla-bla-lba');
      
      





つまり、このプロパティの値を変更する必要がない限り、文字通りエンティティプロパティのセッターに触れません。 これをGraphQLスキーマにシフトすると、次の結果が得られます。







 type Mutation { entityRepository: EntityManager! } type EntityManager { update(id: ID!): PersitedEntity } type PersitedEntity { setFoo(foo: String!): String! setBar(foo: String): String }
      
      





必要に応じて、 setBar



メソッドを呼び出してその値をnullに設定するか、このメソッドに触れないようにすると、値は変更されません。 したがって、 partial update



素晴らしい実装が出てきます。 悪名高いRESTのPATCH



ほど悪くはありません。







過去の資料に関するコメントの中で、 summerwindは次のように尋ねました。 私は答えます:非常に大きなフィールドがあります。


3.ポリモーフィズム



多くの場合、「まったく同じ」ではあるが完全ではない入力エンティティに送信する必要があることがあります。 過去の素材からアカウントを作成する例を使用します。







 #   AccountInput { login: "Acme", password: "***", subject: OrganiationInput { title: "Acme Inc" } }
      
      





 #    AccountInput { login: "Acme", password: "***", subject: PersonInput { firstName: "Vasya", lastName: "Pupkin", } }
      
      





明らかに、1つの引数にこのような構造のデータを送信することはできません。GraphQLでは、これを実行できません。 だから、どういうわけかこの問題を解決する必要があります。







方法0-額







最初に思い浮かぶのは、入力の可変部分の分離です。







 input AccountInput { login: String! password: Password! subjectOrganization: OrganiationInput subjectPerson: PersonInput }
      
      





うーん...そのようなコードを見たとき、私はよくジョセフィン・パブロフナを思い出します。 私には似合わない。







方法1-額ではなく額に

その後、エンティティを識別するためにUUIDを使用します(一般的にはすべての人にお勧めします-何度も役立ちます)。 そしてこれは、クライアント上で有効なエンティティを直接作成し、それらを識別子でバインドし、それらを個別にバックエンドに送信できることを意味します。







それから、私たちは次の精神で何かをすることができます。







 input AccountInput { login: String! password: Password! subject: SubjectSelectInput! } input SubjectSelectInput { id: ID! } type Mutation { createAccount( organization: OrganizationInput, person: PersonInput, account: AccountInput! ): Account! }
      
      





または、さらに便利であることが判明しました(より便利な理由は、ユーザーインターフェイスの生成が可能になったときに説明します)。







 type Mutation { createAccount(account: AccountInput!): Account! createOrganization(organization: OrganizationInput!): Organization! createPerson(person: PersonInput!) : Person! }
      
      





次に、createAccountおよびcreateOrganization / createPersonにリクエストを送信する必要があります。

1つのバッチ。 バッチの処理はトランザクションでラップする必要があることに注意してください。







方法2-魔法のスカラー

秘Theは、GraphQLのスカラーがInt



Sting



Float



などだけではないことです。 これは一般的には何でもです(もちろん、JSONはこれを処理できます)。

その後、単純にスカラーを宣言できます。







 scalar SubjectInput
      
      





次に、ハンドラーを記述します。蒸していません。 次に、変数フィールドを入力に簡単にスリップできます。







どちらを選ぶか? 私は両方を使用し、自分用のルールを作成しました。

親エンティティが子の集約ルートである場合、2番目の方法を選択します。それ以外の場合は最初の方法を選択します。







4.ジェネリック。



ここではすべてが平凡であり、コード生成よりも優れたものは思いつきませんでした。 Rails(railt / sdlパッケージ)がなければ、私は対処できませんでした(より正確には、松葉杖を使って同じことをしていました)。 秘Theは、Railsではドキュメントレベルでディレクティブを定義できることです(仕様にはディレクティブのような位置はありません)。







 directive @example on DOCUMENT
      
      





つまり、ディレクティブは、それらが呼び出されるドキュメント以外には付加されません。







次のディレクティブを導入しました。







 directive @defineMacro(name: String!, template: String!) on DOCUMENT directive @macro(name: String!, arguments: [String]) on DOCUMENT
      
      





誰もマクロの本質を説明する必要はないと思います...







今のところすべてです。 この素材が過去ほど多くのノイズを引き起こすとは思いません。 それでも、タイトルはかなり「黄色」でした)







前の資料へのコメントで、ハブロフスクの住民はアクセスを共有するためにdr死しました...次の資料は認可についてです。








All Articles