API версій


9

Припустимо, у вас є великий проект, підтримуваний базою API. Проект також постачає публічний API, який можуть використовувати кінцеві користувачі.

Іноді потрібно внести зміни до бази API, яка підтримує ваш проект. Наприклад, вам потрібно додати функцію, яка потребує зміни API, новий метод або потребує зміни одного з об'єктів або формату одного з цих об'єктів, переданого в API або з нього.

Якщо припустити, що ви також використовуєте ці об’єкти у своєму загальнодоступному API, публічні об’єкти також змінюватимуться щоразу, коли ви це зробите, що небажано, оскільки ваші клієнти можуть покластися на те, що об’єкти API залишаються ідентичними для їхнього коду розбору. (кашель С ++ клієнтів WSDL ...)

Отже, одне потенційне рішення - це версія API. Але коли ми кажемо API "версія", це здається, що це також повинно означати версію об'єктів API, а також надання дублюючих викликів методів для кожної зміненої підпису методу. Тож у мене тоді з'явиться звичайний старий об’єкт clr для кожної версії мого api, що знову здається небажаним. І навіть якщо я це роблю, я, безумовно, не буду будувати кожен об'єкт з нуля, оскільки це в кінцевому підсумку має велику кількість дублюваного коду. Швидше за все, API, швидше за все, поширить приватні об'єкти, які ми використовуємо для базового API, але тоді ми зіткнемося з тією ж проблемою, оскільки додані властивості також будуть доступні в загальнодоступному API, коли вони не повинні бути.

Отже, яка певна розумність, яка зазвичай застосовується в цій ситуації? Я знаю, що багато публічних служб, таких як Git for Windows, підтримує API, що працює на версії, але у мене виникають проблеми уявити архітектуру, яка підтримує це без великої кількості дублікатів коду, що покриває різні способи та об'єкти введення / виведення.

Мені відомо, що такі процеси, як семантична версія, намагаються поставити певний розум, коли має відбуватися розрив публічного API. Проблема полягає в тому, що здається, що багато або більшість змін вимагають зламати відкритий API, якщо об'єкти не розділені більше, але я не бачу хорошого способу зробити це без дублювання коду.


1
I don't see a good way to do that without duplicating code- Ваш новий API завжди може викликати методи у вашому старому API, або навпаки.
Роберт Харві

2
AutoMapper на допомогу, на жаль, так - вам потрібні чіткі версії кожного контракту, не забувайте, що всі об'єкти, на які посилається ваш контракт, є частиною цього контракту. Результат - ваша реальна реалізація повинна мати власну єдину версію моделей, і вам потрібно перетворити одну версію в різні версії контракту. AutoMapper може допомогти вам тут, а також зробити внутрішні моделі розумнішими, ніж контрактні моделі. За відсутності AutoMapper я використовував методи розширення для створення простих перекладів між внутрішніми моделями та контрактними моделями.
Джиммі Хоффа

Чи є для цього певна платформа / контекст? (тобто DLL, API REST тощо)
GrandmasterB

.NET, з інтерфейсом MVC та Webforms, dll-класами. У нас є і API відпочинку, і мила.
Справа

Відповіді:


6

Підтримуючи API, який використовується третіми сторонами, неминуче потрібно буде внести зміни. Рівень складності буде залежати від типу змін, що відбуваються. Це основні сценарії, які з'являються:

  1. До існуючих API додана нова функціональність
  2. Стара функціональність застаріла від API
  3. Існуюча функціональність в API, що змінюється певним чином

Додавання нової функціональності до існуючого API

Це найпростіший сценарій для підтримки. Додавання нових методів до API не повинно вимагати змін до існуючих клієнтів. Це безпечно для розгортання для клієнтів, яким потрібна нова функціональність, оскільки вона не має оновлень для жодного існуючого клієнта.

Стара функціональність застаріла від API

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

Існуюча функціональність в API, що змінюється певним чином

Цей сценарій буде залежати від типу змін. Якщо вхідні параметри більше не потрібно використовувати, ви можете просто оновити службу / бібліотеку, щоб ігнорувати тепер додаткові дані. У бібліотеці було б, щоб перевантажений метод внутрішньо викликав новий метод, який вимагає меншої кількості параметрів. У розміщеній службі у вас кінцева точка ігнорує додаткові дані, і вона може обслуговувати обидва типи клієнтів та виконувати ту саму логіку.

Якщо до існуючої функціональності потрібно додати нові необхідні елементи, тоді у вас повинні бути дві кінцеві точки / методи для вашої служби / бібліотеки. Поки клієнти не оновлюються, вам потрібно підтримати обидві версії.

Інші думки

Rather, the API is likely to extend the private objects we are using for our base API, but then we run into the same problem because added properties would also be available in the public API when they are not supposed to be.

Не відкривайте внутрішні приватні об’єкти через вашу бібліотеку / службу. Створіть власні типи та складіть карту внутрішньої реалізації. Це дозволить внести внутрішні зміни та звести до мінімуму кількість оновлень, які потребують зовнішні клієнти.

The problem is more that it seems like many or most changes require breaking the public API if the objects aren't more separated, but I don't see a good way to do that without duplicating code.

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

Не розглядайте цей крок як "дублюючий код". Незважаючи на схожість, вони є окремими сутностями, яких варто створити трохи часу. Хоча зміни зовнішнього API майже завжди вимагають відповідних змін у внутрішній реалізації, зміни внутрішнього впровадження не завжди повинні змінювати зовнішній API.

Приклад

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


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

1
Я не знаю жодних хороших прикладів у верхній частині голови. Якщо у мене є певний час у ці вихідні, я можу створити тестовий додаток, щоб кинути на GitHub, щоб показати, як деякі з цих речей можна було б реалізувати.
Філ Паттерсон

1
Складна частина полягає в тому, що з високого рівня існує стільки різних способів спробувати мінімізувати зв'язок між клієнтами та серверами. WCF має інтерфейс під назвою IExtensibleDataObject, який дозволяє передавати від клієнта дані, які не містять контракту, та надсилати їх по проводу на сервер. Google створив Protobuf для зв'язку між системами (є реалізовані програми з відкритим кодом для .NET, Java тощо). Крім того, існує багато систем на основі повідомлень, які також можуть працювати (якщо припустити, що ваш процес може виконуватися асинхронно).
Філ Паттерсон

Можливо, не буде поганою ідеєю показати конкретний приклад дублювання коду, який ви намагаєтесь видалити (як нове запитання про переповнення стека) і подивитися, які рішення пропонує спільнота. Важко відповісти на питання в загальних рисах; тому конкретний сценарій може бути кращим.
Філ Паттерсон
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.