From df9453b3799e036410a9f306067c5c4b3e602031 Mon Sep 17 00:00:00 2001 From: Sergey Konstantinov Date: Mon, 13 Mar 2023 23:10:08 +0200 Subject: [PATCH] code generation --- .../drafts/06-Раздел V. SDK и UI-библиотеки/02.md | 6 ++++-- .../drafts/06-Раздел V. SDK и UI-библиотеки/03.md | 15 ++++++++++++++- 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/src/ru/drafts/06-Раздел V. SDK и UI-библиотеки/02.md b/src/ru/drafts/06-Раздел V. SDK и UI-библиотеки/02.md index df3e1ce..fab7a02 100644 --- a/src/ru/drafts/06-Раздел V. SDK и UI-библиотеки/02.md +++ b/src/ru/drafts/06-Раздел V. SDK и UI-библиотеки/02.md @@ -11,9 +11,11 @@ 4. Как правило, платформа/язык программирования диктуют свою парадигму работы с возникающими ошибками (в виде исключений и/или механизмов defer/panic), что опять же неприменимо в концепции универсального для всех клиентов сетевого API. - 5. API идёт в комплекте с рекомендациями (машино- или человекочитаемыми) по организации перезапросов в случае недоступности эндпойнтов. Эту логику необходимо реализовать разработчику клиента, поскольку библиотеки работы с сетью её, как правило, не предоставляют (и в общем-то не могут этого делать для потенциально неидемпотентных запросов). + 5. API идёт в комплекте с рекомендациями (машино- или человекочитаемыми) по организации перезапросов в случае недоступности эндпойнтов. Эту логику необходимо реализовать разработчику клиента, поскольку библиотеки работы с сетью её, как правило, не предоставляют (и в общем-то не могут этого делать для потенциально неидемпотентных запросов). Этот пункт, при всей видимой малозначительности, является критически важным для любого крупного API, поскольку именно на этом уровне разработчики API могут заложить предохранители от потенциальной перегрузки серверов API лавиной перезапросов, поскольку разработчики клиентов этой частью традиционно пренебрегают: + * читать заголовок Retry-After и не пытаться перезапросить эндпойнт раньше, чем указал сервер; + * ввести увеличивающие интервалы между перезапросами. -Эти проблемы, однако, являются тривиальными — в том смысле, что они не требуют изменять порядок с API (каждому вызову и каждому ответу в API однозначно соответствует какая-то конструкция на языке платформы, и нужно лишь описать правила построения такого соответствия), а только лишь адаптировать платформо-независимый формат API к правилам конкретного языка программирования. Помимо тривиальных проблем, при разработке SDK к клиент-серверному API мы сталкиваемся и с проблемами более высокого порядка: +Эти проблемы, однако, являются тривиальными — в том смысле, что они не требуют изменять порядок работы с API (каждому вызову и каждому ответу в API однозначно соответствует какая-то конструкция на языке платформы, и достаточно описать правила построения такого соответствия), а только лишь адаптировать платформо-независимый формат API к правилам конкретного языка программирования. Помимо тривиальных проблем, при разработке SDK к клиент-серверному API мы сталкиваемся и с проблемами более высокого порядка: 1. В клиент-серверных API данные передаются только по значению; чтобы сослаться на какую-то сущность, необходимо использовать какие-то внешние идентификаторы. Например, если у нас есть два набора сущностей — рецепты и предложения кофе — то нам необходимо будет построить карту рецептов по id, чтобы понять, на какой рецепт ссылается какое предложение: diff --git a/src/ru/drafts/06-Раздел V. SDK и UI-библиотеки/03.md b/src/ru/drafts/06-Раздел V. SDK и UI-библиотеки/03.md index d3ad554..ca270af 100644 --- a/src/ru/drafts/06-Раздел V. SDK и UI-библиотеки/03.md +++ b/src/ru/drafts/06-Раздел V. SDK и UI-библиотеки/03.md @@ -1,2 +1,15 @@ -### Паттерн «Кодогенерация» +### Кодогенерация +Как мы убедились в предыдущей главе, список задач, стоящих перед разработчиком SDK (если, конечно, его целью является качественный продукт) — очень и очень значительный. Учитывая, что под каждую целевую платформу необходим отдельный SDK, неудивительно, что многие вендоры API стремятся полностью или частично заменить ручной труд машинным. + +Одно из основных направлений такой автоматизации — кодогенерация, то есть разработка технологии, которая позволяет по спецификации API сгенерировать готовый код SDK на целевом языке программирования на целевой платформе. Многие современные стандарты обмена данными (в частности, gRPC) поставляются в комплекте с генераторами готовых клиентов на различных языках; к другим технологиям (в частности, OpenAPI/Swagger) такие генераторы пишутся энтузиастами. + +Генерация кода позволяет решить типовые проблемы, описанные в предыдущей главе: стиль кодирования, обработка исключений, (де)сериализацию сложных типов — словом все те задачи, которые зависят не от предметной области, а от особенностей конкретной платформы. Относительно недорого разработчик API может дополнить такой автоматизированный «перевод» правильными настройками используемых системных средств: обеспечить автоматические перезапросы для идемпотентных эндпойнтов (с реализацией какой-то политики), кэширование результатов, сохранение данных (например, токенов авторизации) в системном хранилище и т.д. Такой сгенерированный SDK часто называют термином «клиент к API». + +Удобство использования и функциональные возможности кодогенерации столь привлекательны, что многие вендоры API только ей и ограничиваются, предоставляя свои SDK в виде сгенерированных клиентов. + +**NB**: напомним, что кодогенерация по спецификации, при всех её достоинствах, имеет один очень существенный недостаток: она существенно искажает понятие обратной совместимости, поскольку вводит ещё одну прослойку между спецификацией и кодом, который пишет разработчик. В общем случае, гарантировать, что обратно-совместимое изменение спецификации не приведёт к обратно-несовместимому изменению клиента к API [т.е. к тому, что написанный когда-то разработчиком код поверх кодогенерированного клиента будет корректно работать с новой версией клиента] — достаточно нетривиальная задача, равно как такая гарантия отсутствует и при переходе от одной версии библиотеки кодогенерации к другой. Как минимум это означает, что сгенерированные клиенты должны интенсивно тестироваться с целью выявления непредвиденных ошибок. + +Как мы, однако, видим из предыдущей главы, проблемы более высокого порядка — получение серверных событий, обработка ошибок в бизнес-логике и т.п. — никак не может быть покрыта кодогенерацией, во всяком случае — стандартным модулем без его доработки применительно к конкретному API. В случае нетривиальных API со сложным основным циклом работы очень желательно, чтобы SDK решал также и высокоуровневые проблемы, иначе вы просто получите множество разработанных поверх API приложений, раз за разом повторяющие одни и те же «детские ошибки». Тем не менее, это не повод отказываться от кодогенерации полностью — её можно использовать как базис, на котором будет разработан высокоуровневый SDK. + +В соответствии с парадигмой «айсберга» (см. главу «О ватерлинии айсберга») доступ к функциональности сгенерированного клиента может быть скрыт (т.е. разработчики не будут иметь доступ к низкоуровневой работой с API), тем самым обеспечивая определённую свободу работы с API изнутри SDK, вплоть до бесшовного перехода на новые мажорные версии API. Этот подход, несомненно, предоставляет вендору API намного больше контроля над приложениями клиентов, но требует и намного больше ресурсов на разработку, и, что важнее, грамотного проектирования SDK — такого, чтобы у разработчиков не было необходимости обращаться к API напрямую в обход SDK по причине отсутствия в нём необходимых функций или их плохой реализации. \ No newline at end of file