Чому така погана ідея ділитися інтерфейсом між сервером і клієнтом?


12

Я читав документацію Spring Cloud Netflix, коли дізнався про спосіб спільного використання інтерфейсу між HTTP-сервером та його клієнтом. Вони використовують цей приклад для мікросервісів, хоча немає причини, чому він не може поширюватися на загальний 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 {
}

Це визначає інтерфейс, який використовується як сервер (весна @RestControllerперетворює його на сервер HTTP), так і клієнт (Feign @FeignClientвстановлює його для використання HTTP-клієнта). Реалізації серверного та клієнтського класів можуть використовуватися в окремих проектах, але все ж використовувати один і той же інтерфейс для забезпечення відповідності типів.

Однак під прикладом вони містять наступне застереження:

Примітка. Зазвичай не доцільно ділитися інтерфейсом між сервером і клієнтом. Він вводить жорстке з'єднання, а також фактично не працює з Spring MVC в його поточному вигляді (відображення параметрів методу не успадковується).

Гаразд, тому це не дуже інтегровано зараз ... але ця частина з’являється після попередження про код спільного використання та введення зв'язку між сервером і клієнтом, що, на їхню думку, є більш важливим. Чому вони вважають, що подібна інтерфейс таким чином погана ідея?

Без цього ви втрачаєте можливість гарантувати, що сервер і клієнт надсилають один одному дані, які вони можуть обидва зрозуміти. Ви можете додати поле до одного, але не до іншого, і виявити невідповідність лише до часу виконання. На мій погляд, це не введення з’єднання, а просто виявлення зв'язку, яка вже існує. Чи є необхідність зробити сервери абсолютно незалежними більшою, ніж необхідність повідомляти їм, які типи даних вони отримуватимуть?


1
Зв'язок, що існує, з точки зору даних / форматування, якими обробляються клієнти / сервери, визначається протоколом - частиною документації, яка може бути використана як умова . З'єднання, що вводиться при спільному використанні інтерфейсу, є з'єднанням часу компіляції - врахуйте, що відбувається при зміні інтерфейсу (наприклад, несумісний з тим, як назад), але код клієнта / сервера, що використовує цей інтерфейс, розгорнуто в різний час. Цим зв'язком час розгортання може бути важче керувати, особливо за шкалою Netflix.
Касталья

1
Я впевнений, що я не працюю за шкалою Netflix :), але якщо інтерфейси змінені зворотно несумісним способом, чи це не просто пересуває помилку з того, що було знайдено під час компіляції, на те, щоб знайти його під час виконання? Чи вважають вони нормальним відмову від декількох викликів функцій, коли вони повільно оновлюють усі сервери?
Бен С

1
Можливо; залежить від коду клієнта. Розглянемо також інший випадок: спочатку сервери оновлюються, і клієнтам зараз доводиться мати справу з (несподівано) невдалими дзвінками ...
Castaglia

1
Цікаво, поділяючи цей інтерфейс, чи обмежує він, на яких мовах / стеках ви можете побудувати клієнта?
JeffO

Так - це файл Java, тому вам доведеться використовувати Java. Ви можете бути в змозі використати іншу мову віртуальної машини Java , але я не пробував.
Ben S

Відповіді:


6

Причина, зазначена в коментарях, полягає в тому, що це призводить до тісного з'єднання вашої клієнтської платформи з вашою платформою сервера. Тут це означає, що ваш клієнт повинен використовувати мову / платформу, яку ви використовуєте на сервері, щоб зрозуміти очікуваний контракт вашого сервера. Зауважте, що існує різниця між спільним використанням одного і того ж коду (артефактом певної мови / платформи) та узгодженням конкретного контракту.

Натомість багато проектів використовують документацію для своїх контрактів. Приклади запитів та відповідей у ​​нейтральному форматі (наприклад, JSON) над стандартними протоколами (наприклад, REST). ( Наприклад, див. Документи API Stripe API ). Тому що недоцільно писати контракт на основі коду для кожної можливої ​​клієнтської платформи, яку ви можете використовувати або дозволити. Інші використовують інструменти управління API для визначення нейтральних контрактів .

Ваш приклад додавання поля викликає окрему проблему - приклад того, чому важливо укладати контракти API версії. Нехай клієнти використовують версію, для якої вони розроблені. Поряд зі старою існує нова, несумісна з новою версією API. Клієнт для старої версії продовжує працювати до тих пір, поки її команда не зможе її оновити або поки ви не залишите стару версію (після періоду припинення / міграції). Див. Паралельну зміну .

Дотримуючись (неявна порада в) попередження допомагає клієнту та серверу еволюціонувати способами та кроками, які мають сенс для кожного. Якщо ви можете гарантовано гарантувати, що ваш сервер і клієнт завжди будуть використовувати однакову мову / платформу І розвиватися з однаковим темпом, то, використовуючи артефакт коду для мови та платформи, як ваш контракт, ймовірно, буде добре. Однак це, мабуть, не є розумним очікуванням, особливо для проектів, орієнтованих на Netflix OSS (щось конкретно орієнтоване на масштабованість та ефективність хмари, з усією необхідною складністю).


2
Очікується, що клієнт дійсно використовує інтерфейс? Я завжди бачив такі конструкції як спосіб полегшити написання клієнтів. Зрештою, ви все ще можете написати REST-клієнта іншою мовою.
Джиммі Т.

1
Точно, api все ще буде існувати, з його визначеннями маршрутів, ніщо не заважає створити клієнта іншою мовою, але поки ви використовуєте java, можна використовувати інтерфейс
Леонардо Вілла
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.