サーバーとクライアント間でインターフェースを共有するのはなぜそんなに悪い考えなのですか?


12

HTTPサーバーとそのクライアントの間でインターフェースを共有する方法を見つけたとき、Spring Cloud Netflixのドキュメントを読んでいました。彼らはこの例をマイクロサービスに使用していますが、一般的なHTTP通信に拡張できない理由はありません:

// The shared interface, in a common library
public interface UserService {
    @RequestMapping(method = GET, value = "/users/{id}")
    User getUser(@PathVariable long id);
}

// The controller, on the server
@RestController
public class UserResource implements UserService {
}

// The same interface used for the client
@FeignClient("users")
public interface UserClient extends UserService {
}

これは、サーバー(Spring @RestControllerがHTTPサーバーに変換する)とクライアント(Feign @FeignClientがHTTPクライアント用にセットアップする)の両方として使用されるインターフェースを定義します。サーバークラスとクライアントクラスの実装は、別々のプロジェクトで使用できますが、同じインターフェイスを使用して、型が一致することを確認できます。

ただし、この例の下には次の警告があります。

注:通常、サーバーとクライアント間でインターフェースを共有することはお勧めできません。密結合を導入し、実際には現在の形式のSpring MVCでは機能しません(メソッドパラメーターマッピングは継承されません)。

さて、今はうまく統合されていません...しかし、その部分はコードを共有し、サーバーとクライアント間のカップリングを導入することに対する警告の後にあります。このようにインターフェースを共有するのは、どうしてそんなに悪い考えだと思うのでしょうか?

それがないと、サーバーとクライアントがお互いに理解できるデータを送信することを保証することができなくなります。一方にフィー​​ルドを追加し、他方にはフィールドを追加せず、実行時までのみ不一致を検出できます。私の考えでは、カップリングを導入するのではなく、すでに存在するカップリングを明らかにするだけです。サーバーが受信するデータの種類を知らせる必要性よりも、サーバーを完全に独立させる必要性は大きいのでしょうか?


1
クライアント/サーバーによって処理されるデータ/フォーマットに関して存在するカップリングは、プロトコルによって決まります。これは、慣例として使用できるドキュメントです。インターフェースを共有することで導入されるカップリングはコンパイル時のカップリングです-インターフェースが変更された場合(たとえば、後方互換性のない方法で)に何が起こるかを考えますが、そのインターフェースを使用するクライアント/サーバーコードは異なるタイミングでデプロイされます。その展開時のカップリングは、特にNetflixの規模では管理が難しい場合があります。
カスタリア

1
私はNetflixのスケールで動作していないことはかなり確信しています:)しかし、インターフェースが後方互換性のない方法で変更された場合、これは単にエラーをコンパイル時の検出から実行時の検出にシフトしませんか?彼らはすべてのサーバーをゆっくりとアップグレードしながら、いくつかの関数呼び出しを失敗させてもよいと考えていますか?
ベンS

1
おそらく。クライアントコードに依存します。他のケースも考慮してください。サーバーが最初にアップグレードされ、クライアントは(予期せず)失敗した呼び出しに対処する必要があります
...-Castaglia

1
このインターフェイスを共有することにより、クライアントを構築できる言語/スタックが制限されますか?
ジェフ

はい— Javaファイルなので、Javaを使用する必要があります。あなたはあり、他のJVM言語を使用することができるが、私はそれを試していません。
ベンS

回答:


6

コメントに記載されている理由は、クライアントプラットフォームとサーバープラットフォームが緊密に結合されるためです。ここでは、サーバーの予想される契約を理解するために、サーバーで使用している言語/プラットフォームをクライアントが使用する必要があることを意味します。同じコード(特定の言語/プラットフォームの成果物)を共有することと、特定の契約に同意することには違いがあることに注意してください。

多くのプロジェクトでは、代わりに契約書にドキュメントを使用しています。標準プロトコル(RESTなど)を介した中立形式(JSONなど)での要求と応答の例。(たとえば、Stripe APIのドキュメントを参照してください)。使用または許可する可能性のあるすべてのクライアントプラットフォームに対してコードベースのコントラクトを作成することは実用的ではないためです。さらに、中立的な契約を定義するためにAPI管理ツールを使用するものもあります。

フィールドを追加する例は、別の懸念事項です。APIコントラクトをバージョン管理することが重要な理由の例です。クライアントが設計されたバージョンを使用できるようにします。下位互換性のない新しいAPIバージョンが古いバージョンとともに存在します。古いバージョンのクライアントは、そのチームが更新に取りかかるまで、または古いバージョンを廃止するまで(非推奨/移行期間後)動作し続けます。見るParallel Changeを

(内の暗黙のアドバイス)警告に従うことは、クライアントとサーバーがそれぞれに意味のある方法とペースで進化するのに役立ちます。サーバーとクライアントが常に同じ言語/プラットフォームを共有し、同じペースで進化することを合理的に保証できる場合は、おそらく言語とプラットフォーム固有のコード成果物を使用して、契約は大丈夫でしょう。ただし、これはおそらくNetflix OSSを対象とするプロジェクト(クラウドのスケーラビリティとパフォーマンスに特化したもので、そのすべての必要な複雑さを伴うもの)にとっては、おそらく合理的な期待ではありません。


2
クライアントはインターフェースを使用することが本当に期待されていますか?私はいつもクライアントを書くことをより簡単にする方法としてそのような構造を見てきた。結局のところ、RESTクライアントは別の言語で作成できます。
ジミーT.

1
正確には、APIはまだ別の言語でそのルート定義、何も防止して、クライアントの作成が存在しますが、限り、あなたが使用してJavaはインタフェース使用することができます
レオナルドVillela
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.