diff --git a/src/ru/clean-copy/05-Раздел IV. HTTP API и архитектурные принципы REST/04.md b/src/ru/clean-copy/05-Раздел IV. HTTP API и архитектурные принципы REST/04.md index 7927c94..8b968ee 100644 --- a/src/ru/clean-copy/05-Раздел IV. HTTP API и архитектурные принципы REST/04.md +++ b/src/ru/clean-copy/05-Раздел IV. HTTP API и архитектурные принципы REST/04.md @@ -180,4 +180,4 @@ HTTP-глагол определяет два важных свойства HTTP Короче говоря, ситуация с кейсингом настолько плоха и запутана, что консистентного и удобного решения попросту нет. В этой книге мы придерживаемся следующего правила: токены даются в том кейсинге, который является общепринятым для той секции запроса, в которой находится токен; если положение токена меняется, то меняется и кейсинг. (Мы далеки от того, чтобы рекомендовать этот подход всюду; наша общая рекомендация, скорее — не умножать энтропию и пытаться минимизировать такого рода коллизии.) -**NB**: вообще говоря, JSON исходно — это JavaScript Object Notation, а в языке JavaScript кейсинг по умолчанию - `camelCase`. Мы, тем не менее, позволим себе утверждать, что JSON давно перестал быть форматом данных, привязанным к JavaScript, и в настоящее время используется для организации взаимодействия агентов, реализованных на любых языках программирования. Использование `snake_case`, по крайней мере, позволяет легко перебрасывать параметр из query в тело и обратно, что, обычно, является наиболее частым кейсом при разработке HTTP API. Впрочем, обратный вариант (использование `camelCase` в именах query-параметров) тоже допустим. \ No newline at end of file +**NB**: вообще говоря, JSON исходно — это JavaScript Object Notation, а в языке JavaScript кейсинг по умолчанию - `camelCase`. Мы, тем не менее, позволим себе утверждать, что JSON давно перестал быть форматом данных, привязанным к JavaScript, и в настоящее время используется для организации взаимодействия агентов, реализованных на любых языках программирования. Использование `snake_case`, по крайней мере, позволяет легко перебрасывать параметр из query в тело и обратно, что, обычно, является наиболее частотным кейсом при разработке HTTP API. Впрочем, обратный вариант (использование `camelCase` в именах query-параметров) тоже допустим. \ No newline at end of file diff --git a/src/ru/clean-copy/05-Раздел IV. HTTP API и архитектурные принципы REST/05.md b/src/ru/clean-copy/05-Раздел IV. HTTP API и архитектурные принципы REST/05.md index 14caf56..b088a90 100644 --- a/src/ru/clean-copy/05-Раздел IV. HTTP API и архитектурные принципы REST/05.md +++ b/src/ru/clean-copy/05-Раздел IV. HTTP API и архитектурные принципы REST/05.md @@ -210,4 +210,4 @@ Authorization: Bearer Этот подход можно в дальнейшем усложнять: добавлять гранулярные разрешения выполнять конкретные операции, вводить уровни доступа, проверку прав в реальном времени через дополнительный вызов ACL-сервиса и так далее. -Важно, что кажущаяся избыточность перестала быть таковой: `user_id` в запросе теперь не дублируется в данных токена; эти идентификаторы имеют разный смысл: *над каким ресурсом* исполняется операция и *кто* исполняет операцию. Совпадение этих двух сущностей — пусть распространенный, но всё же частный случай. Что, к сожалению, не отменяет его неочевидности и возможности легко забыть выполнить проверку в коде. Таков путь. \ No newline at end of file +Важно, что кажущаяся избыточность перестала быть таковой: `user_id` в запросе теперь не дублируется в данных токена; эти идентификаторы имеют разный смысл: *над каким ресурсом* исполняется операция и *кто* исполняет операцию. Совпадение этих двух сущностей — пусть частотный, но всё же частный случай. Что, к сожалению, не отменяет его неочевидности и возможности легко забыть выполнить проверку в коде. Таков путь. \ No newline at end of file diff --git a/src/ru/clean-copy/05-Раздел IV. HTTP API и архитектурные принципы REST/06.md b/src/ru/clean-copy/05-Раздел IV. HTTP API и архитектурные принципы REST/06.md index 5c165ca..0b6a4a6 100644 --- a/src/ru/clean-copy/05-Раздел IV. HTTP API и архитектурные принципы REST/06.md +++ b/src/ru/clean-copy/05-Раздел IV. HTTP API и архитектурные принципы REST/06.md @@ -36,7 +36,7 @@ 4. Насколько строго должна выдерживаться буквальная интерпретация конструкции `ГЛАГОЛ /ресурс`? Если мы принимаем правило «части URL обязаны быть существительными» (и ведь странно применять глагол к глаголу!), то в примерах выше должно быть не `prepare`, а `preparator` или `preparer` (а вариант `/action=prepare&coffee_machine_id=&recipe=lungo` вовсе недопустим, так как нет объекта действия), что, честно говоря, лишь добавляет визуального шума в виде суффиксов «ator», но никак не способствует большей лаконичности и однозначности понимания. - 5. Если сигнатура вызова по умолчанию модифицирующая или неидемпотентная, означает ли это, что операция *обязана* быть модифицирующей / не идемпотентной? Двойственность смысловой нагрузки глаголов (семантика vs побочные действия) порождает неопределённость в вопросах организации API. Рассмотрим, например, ресурс `/v1/search`, осуществляющий поиск предложений кофе в нашем учебном API. С каким глаголом мы должны к нему обращаться? + 5. Если сигнатура вызова по умолчанию модифицирующая или неидемпотентная, означает ли это, что операция *обязана* быть модифицирующей / неидемпотентной? Двойственность смысловой нагрузки глаголов (семантика vs побочные действия) порождает неопределённость в вопросах организации API. Рассмотрим, например, ресурс `/v1/search`, осуществляющий поиск предложений кофе в нашем учебном API. С каким глаголом мы должны к нему обращаться? * С одной стороны, `GET /v1/search?query=<поисковый запрос>` позволяет явно продекларировать, что никаких посторонних эффектов у этого запроса нет (никакие данные не перезаписываются) и результаты его можно кэшировать (при условии, что все значимые параметры передаются в URL). * С другой стороны, согласно семантике операции, `GET /v1/search` должен возвращать *представление ресурса `search`*. Но разве результаты поиска являются представлением ресурса-поисковика? Смысл операции «поиск» гораздо точнее описывается фразой «обработка запроса в соответствии с внутренней семантикой ресурса», т.е. соответствует методу `POST`. Кроме того, можем ли мы вообще говорить о кэшировании поисковых запросов? Страница результатов поиска формируется динамически из множества источников, и повторный запрос с той же поисковой фразой почти наверняка выдаст другой список результатов. @@ -178,4 +178,4 @@ * `/v1/orders/search?user_id=` для поиска заказов через метод `GET` (простые случаи) или `POST` (если необходимы сложные фильтры); * `PUT /v1/orders/{id}/archive` для архивирования заказа. -плюс, вероятно, потребуются частные операции типа `POST /v1/orders/{id}/cancel` для проведения атомарных изменений. Именно это произойдёт в реальной жизни: идея CRUD как методологии описания типичных операций над ресурсом с помощью небольшого набора HTTP-глаголов быстро превратится в семейство эндпойнтов, каждый из которых покрывает какой-то аспект жизненного цикла сущности. Это всего лишь означает, что CRUD-мнемоника даёт только стартовый набор гипотез; любая конкретная предметная область требует вдумчивого подхода и дизайна подходящего API. Если же перед вами стоит задача разработать «универсальный» интерфейс, который подходит для работы с любыми сущностями, лучше сразу начинайте с номенклатуры в 10 методов, которые были описаны выше. +плюс, вероятно, потребуются частные операции типа `POST /v1/orders/{id}/cancel` для проведения атомарных изменений. Именно это произойдёт в реальной жизни: идея CRUD как методологии описания типичных операций над ресурсом с помощью небольшого набора HTTP-глаголов быстро превратится в семейство эндпойнтов, каждый из которых покрывает какой-то аспект жизненного цикла сущности. Это всего лишь означает, что CRUD-мнемоника даёт только стартовый набор гипотез; любая конкретная предметная область требует вдумчивого подхода и дизайна подходящего API. Если же перед вами стоит задача разработать «универсальный» интерфейс, который подходит для работы с любыми сущностями, лучше сразу начинайте с номенклатуры в 10 методов, подобной описанной выше. diff --git a/src/ru/clean-copy/05-Раздел IV. HTTP API и архитектурные принципы REST/08.md b/src/ru/clean-copy/05-Раздел IV. HTTP API и архитектурные принципы REST/08.md index c724bf5..d88b9f5 100644 --- a/src/ru/clean-copy/05-Раздел IV. HTTP API и архитектурные принципы REST/08.md +++ b/src/ru/clean-copy/05-Раздел IV. HTTP API и архитектурные принципы REST/08.md @@ -38,4 +38,4 @@ и заложите защиту от этих векторов атак на уровне вашего серверного ПО. Организация OWASP предоставляет хороший обзор лучших security-практик для HTTP API[ref REST Security Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/REST_Security_Cheat_Sheet.html). -В заключение хотелось бы сказать следующее: HTTP API — это способ организовать ваше API так, чтобы полагаться на понимание семантики операций как разнообразным программным обеспечением, от клиентских фреймворков до серверных гейтвеев, так и разработчиком, который читает спецификацию. В этом смысле экосистема HTTP предоставляет пожалуй наиболее широкий (и в плане глубины, и в плане распространённости) по сравнению с другими технологиями словарь для описания самых разнообразных ситуаций, возникающих во время работы клиент-серверных приложений. Разумеется, эта технология не лишена своих недостатков, но для разработчика *публичного* API она является выбором по умолчанию — на сегодняшний день скорее надо обосновывать отказ от HTTP API чем выбор в его пользу. \ No newline at end of file +В заключение хотелось бы сказать следующее: HTTP API — это способ организовать ваше API так, чтобы полагаться на понимание семантики операций как разнообразным программным обеспечением, от клиентских фреймворков до серверных гейтвеев, так и разработчиком, который читает спецификацию. В этом смысле экосистема HTTP предоставляет, пожалуй, наиболее широкий (и в плане глубины, и в плане распространённости) по сравнению с другими технологиями словарь для описания самых разнообразных ситуаций, возникающих во время работы клиент-серверных приложений. Разумеется, эта технология не лишена своих недостатков, но для разработчика *публичного* API она является выбором по умолчанию — на сегодняшний день скорее надо обосновывать отказ от HTTP API чем выбор в его пользу. \ No newline at end of file diff --git a/src/ru/clean-copy/06-Раздел V. SDK и UI-библиотеки/02.md b/src/ru/clean-copy/06-Раздел V. SDK и UI-библиотеки/02.md index ec4fe7f..c742114 100644 --- a/src/ru/clean-copy/06-Раздел V. SDK и UI-библиотеки/02.md +++ b/src/ru/clean-copy/06-Раздел V. SDK и UI-библиотеки/02.md @@ -3,15 +3,16 @@ Первый вопрос об SDK (напомним, так мы будем называть нативную клиентскую библиотеку, предоставляющую доступ к technology-agnostic клиент-серверному API), который мы должны прояснить — почему вообще такое явление как SDK существует. Иными словами, почему использование обёртки для фронтенд-разработчика является более удобным, нежели работа с нижележащим API напрямую. Некоторые причины лежат на поверхности: -1. Протоколы клиент-серверных API, как правило, разрабатываются так, что не зависят от конкретного языка программирования и, таким образом, без дополнительных действий полученные из API данные будут представлены в не самом удобном формате. Например в JSON нет типа данных «дата и время», и его приходится передавать в виде строки; или, скажем, поддержка (де)сериализации хэш-таблиц в протоколах общего назначения отсутствует. -2. Большинство языков программирования императивные (и чаще всего — объектно-ориентированные), в то время как большинство форматов данных — декларативные. Работать с сырыми данными, полученными из API, таким образом почти всегда неудобно с точки зрения написания кода, программистам зачастую было бы удобнее работать с полученными из API данными как с объектами. + 1. Протоколы клиент-серверных API, как правило, разрабатываются так, что не зависят от конкретного языка программирования и, таким образом, без дополнительных действий полученные из API данные будут представлены в не самом удобном формате. Например в JSON нет типа данных «дата и время», и его приходится передавать в виде строки; или, скажем, поддержка (де)сериализации хэш-таблиц в протоколах общего назначения отсутствует. -3. Разные языки программирования предполагают разный стиль кодирования (кейсинг, организация неймспейсов и т.п.), в то время как концепция API не предполагает адаптацию форматирования под запрашивающую платформу. + 2. Большинство языков программирования императивные (и чаще всего — объектно-ориентированные), в то время как большинство форматов данных — декларативные. Работать с сырыми данными, полученными из API, таким образом почти всегда неудобно с точки зрения написания кода, программистам зачастую было бы удобнее работать с полученными из API данными как с объектами. -4. Как правило, платформа/язык программирования диктуют свою парадигму работы с возникающими ошибками (в виде исключений и/или механизмов defer/panic), что опять же неприменимо в концепции универсального для всех клиентов сетевого API. + 3. Разные языки программирования предполагают разный стиль кодирования (кейсинг, организация неймспейсов и т.п.), в то время как концепция API не предполагает адаптацию форматирования под запрашивающую платформу. -5. API идёт в комплекте с рекомендациями (машино- или человекочитаемыми) по организации перезапросов в случае недоступности эндпойнтов. Эту логику необходимо реализовать разработчику клиента, поскольку библиотеки работы с сетью её, как правило, не предоставляют (и в общем-то не могут этого делать для потенциально неидемпотентных запросов). Этот пункт, при всей видимой малозначительности, является критически важным для любого крупного API, поскольку именно на этом уровне разработчики API могут заложить предохранители от потенциальной перегрузки серверов API лавиной перезапросов, поскольку разработчики клиентов этой частью традиционно пренебрегают: + 4. Как правило, платформа/язык программирования диктуют свою парадигму работы с возникающими ошибками (в виде исключений и/или механизмов defer/panic), что опять же неприменимо в концепции универсального для всех клиентов сетевого API. + + 5. API идёт в комплекте с рекомендациями (машино- или человекочитаемыми) по организации перезапросов в случае недоступности эндпойнтов. Эту логику необходимо реализовать разработчику клиента, поскольку библиотеки работы с сетью её, как правило, не предоставляют (и в общем-то не могут этого делать для потенциально неидемпотентных запросов). Этот пункт, при всей видимой малозначительности, является критически важным для любого крупного API, поскольку именно на этом уровне разработчики API могут заложить предохранители от потенциальной перегрузки серверов API лавиной перезапросов, поскольку разработчики клиентов этой частью традиционно пренебрегают: * читать заголовок `Retry-After` и не пытаться перезапросить эндпойнт раньше, чем указал сервер; * ввести увеличивающие интервалы между перезапросами. diff --git a/src/ru/clean-copy/06-Раздел V. SDK и UI-библиотеки/05.md b/src/ru/clean-copy/06-Раздел V. SDK и UI-библиотеки/05.md index b78ee79..f14beb6 100644 --- a/src/ru/clean-copy/06-Раздел V. SDK и UI-библиотеки/05.md +++ b/src/ru/clean-copy/06-Раздел V. SDK и UI-библиотеки/05.md @@ -21,7 +21,7 @@ class SearchBoxComposer } ``` -Кроме того, мы можем убрать `Composer` из цепочки подготовки данных так, чтобы подчинённые компоненты напрямую получали нужные поля из `SearchBox`: +Кроме того, мы можем убрать `Composer` из цепочки подготовки данных так, чтобы подчинённые компоненты напрямую получали нужные поля напрямую из `SearchBox`: ```typescript class OfferListComponent @@ -97,4 +97,4 @@ SDK, реализованный в MV*-парадигмах, в теории п Предложенная Фаулером парадигма во многом схожа с концепцией `Composer`-а, которую мы обсуждали в предыдущей главе, но с одним заметным различием. По мысли Фаулера собственного состояния у presenter-а нет (за исключением, возможно, кэшей и замыканий), он только вычисляет данные, необходимые для показа визуального интерфейса, из данных модели. Если необходимо манипулировать каким-то низкоуровневым свойством, например, цветом текста в интерфейсе, то нужно разработать модель так, чтобы цвет текста вычислялся presenter-ом из какого-то высокоуровневого поля в модели (возможно, искусственно введённого), что ограничивает возможности альтернативных имплементаций субкомпонентов. -**NB**: на всякий случай уточним, что автор этой книги не предлагает `Composer` как альтернативную MV*-методологию. Идея предыдущей главы состоит в том, что сложные сценарии декомпозиции UI-компонентов решаются *только* искусственным введением некоторых мостиков в виде дополнительных уровней абстракции. Неважно, как мы этот мостик назовём и какие правила для него придумаем. \ No newline at end of file +**NB**: на всякий случай уточним, что автор этой книги не предлагает `Composer` как альтернативную MV*-методологию. Идея предыдущей главы состоит в том, что сложные сценарии декомпозиции UI-компонентов решаются *только* искусственным введением дополнительных мостиков-уровней абстракции. Неважно, как мы этот мостик назовём и какие правила для него придумаем. \ No newline at end of file diff --git a/src/ru/clean-copy/07-Раздел VI. API как продукт/02.md b/src/ru/clean-copy/07-Раздел VI. API как продукт/02.md index c0c8a3f..9f89538 100644 --- a/src/ru/clean-copy/07-Раздел VI. API как продукт/02.md +++ b/src/ru/clean-copy/07-Раздел VI. API как продукт/02.md @@ -12,7 +12,7 @@ 2. API может быть лицензирован под открытой лицензией с определёнными ограничениями, которые могут быть сняты путём покупки расширенной лицензии; это может быть как ограничение функциональности API (например, запрет публикации приложения в соответствующем магазине приложений или невозможность сборки приложения в продакшен-режиме без приобретения лицензии), так и ограничения на использование (например, открытая лицензия может быть «заразной», т.е. требовать распространения написанного поверх платформы кода под той же лицензией, или же использование бесплатного API может быть запрещено для определённых целей). - 3. API может быть бесплатен, но компания-разработчик API может оказывать платные услуги по консультированию и внедрению в его рамках или просто продавать расширенную техническую поддержку. + 3. API может быть бесплатен, но компания-разработчик API может оказывать платные услуги по консультированию и внедрению или просто продавать расширенную техническую поддержку. 4. Разработка API может спонсироваться явно или неявно владельцем платформы или операционной системы [в нашем кофейном примере — производителем «умных» кофе-машин], который заинтересован в том, чтобы разработчики имели как можно больше удобных инструментов для работы с платформой. diff --git a/src/ru/clean-copy/07-Раздел VI. API как продукт/03.md b/src/ru/clean-copy/07-Раздел VI. API как продукт/03.md index 95c36bf..26625b2 100644 --- a/src/ru/clean-copy/07-Раздел VI. API как продукт/03.md +++ b/src/ru/clean-copy/07-Раздел VI. API как продукт/03.md @@ -21,7 +21,7 @@ #### Проверка продуктовых гипотез -Помимо общих сложностей с формированием продуктового видения API есть и более частые сложности с проверкой продуктовых гипотез. «Святой грааль» управления продуктом — создание максимально недорогого с точки зрения затраченных ресурсов minimal viable product (MVP) — обычно недоступен для менеджера API. Дело в том, что вы не можете так просто *проверить* MVP, даже если вам удалось его разработать: для проверки MVP API партнёры должны *написать код* (читай — вложить свои деньги); если по итогам этого эксперимента будет принято решение о бесперспективности продукта, эти деньги окажутся потрачены впустую. Разумеется, партнёры к подобного рода предложениям относятся с некоторым скептицизмом. Таким образом «дешёвый» MVP включает в себя либо компенсацию расходов партнёрам, либо затраты на разработку референсного приложения (т.е. в дополнение к MVP API разрабатывается сразу и MVP приложения, использующего этот API). +Помимо общих сложностей с формированием продуктового видения API есть и частные сложности с проверкой продуктовых гипотез. «Святой грааль» управления продуктом — создание максимально недорогого с точки зрения затраченных ресурсов minimal viable product (MVP) — обычно недоступен для менеджера API. Дело в том, что вы не можете так просто *проверить* MVP, даже если вам удалось его разработать: для проверки MVP API партнёры должны *написать код* (читай — вложить свои деньги); если по итогам этого эксперимента будет принято решение о бесперспективности продукта, эти деньги окажутся потрачены впустую. Разумеется, партнёры к подобного рода предложениям относятся с некоторым скептицизмом. Таким образом «дешёвый» MVP включает в себя либо компенсацию расходов партнёрам, либо затраты на разработку референсного приложения (т.е. в дополнение к MVP API разрабатывается сразу и MVP приложения, использующего этот API). Частично эту проблему можно решить, если выпустить MVP от имени сторонней компании (например, в виде модуля с открытым кодом, опубликованного от лица разработчика). Однако тогда вы получите проблемы с собственно проверкой гипотезы, так как подобные модули рискуют быть просто оставленными без внимания.