понеділок, 22 січня 2018 р.

Шаблон TolerantReader и Закон Постела

При взаимодействии множества систем постоянно встает вопрос об интеграции. Когда мы начинаем работать с чьим-то API, существует риск, что представители API могут его изменить и мы будем к этому не готовы. Системы эволюционируют, развиваются и, порой, следствие этой эволюции - развитие API. По началу, изменения могут быть незначительными, почти совместимыми, но даже к таким изменениям мы должны быть готовы. С обратной стороны, когда мы кому-то отдаем своё API, нам нужно понимать, что наши клиенты могут быть готовы к переменам, если мы будем осторожны, консервативны к изменениям.

Закон Джона Постела гласит:

будь консервативным к тому, что делаешь, будь либеральным к тому, что получаешь от других

Для успешного развития распределенных систем, сервис предоставляющий API, будем называть его поставщик, должен отдавать клиенту API, будем называть его потребитель, такое API, которое в минимальном виде может что-то сломать. От части этому способствуют такие форматы общения как Thrift и Protocol Buffers. Однако, всё не ограничивается одним только способом транспортировки данных и преобразования их в конечных точках, хотя это тоже важно.

Как быть либеральным к входящим данным? Можно выстроить следующий свод павил:

  • Используйте только те поля в данных, которые вам нужны, не используйте лишнего.

Допустим, к вам прилетел JSON объект со следующей структурой:

{
  "username": "myusername",
  "address": "Россия, Самара, Московское шоссе, д. 1, кв. 1" 
}

Для наглядности примера, представим, что вы фронтенд. Вы отображаете главную страницу, на которой есть имя пользователя, а адрес будет нужен только в личном кабинете. Конечно, следовало бы не отправлять все данные в принципе, но на практике, такие упущения случаются. Однажды, бекэнд меняет формат JSON на следующий:

{
  "username": "myusername",
  "address": {
    "country": "Россия",
    "city": "Самара",
    "street": "Московское шоссе",
    "house_number": "1",
    "apartment_number": "1"
  } 
}

В тот момент, когда вы получите эти изменения, для успешной интеграции, всё что вам будет нужно, это изменить обработку адреса в личном кабинете, т.к. на главной странице вы не стали трогать поле, которое вам не нужно.

  • Делайте минимальные предположения о структуре данных.

Рассмотрим, еще один интересный случай. Допустим, вы извлекаете поля из json напрямую, для упрощения примера представим что перед вами динамически типизируемый язык. Самый простой способ получить из jsonObject поле username, это явно его запросить, например, так jsonObject.username. Но это не совсем толератно, не так ли? Когда мы работаем с XML Martin Fowler предлагает воспользоваться XPath, для получения имени пользователя, для json же существует JsonPath. Как это использовать? Представим, что поставщик изменяет структуру JSON объекта следующим образом:

{
  "naming": {
    "username": "myusername",
    "firstname": "myfirstname",
    "lastname": "mylastname"
  },
  "address": {
    "country": "Россия",
    "city": "Самара",
    "street": "Московское шоссе",
    "house_number": "1",
    "apartment_number": "1"
  } 
}

Если бы мы жестко завязались на структуру, то наш потребитель API пришлось бы знатно переделывать, но если мы воспользовались JsonPath, и получали имя пользователя, выполняя поиск по выражению $..username, то мы бы просто не заметили изменения входного объекта и в перспективе сумарно проделали бы на много меньше работы. Естественно, такой подход кроет в себе угрозу, например, если в json объекте окажется два поля с именем username, то какое нужно выбирать? Однако, в простых случаях это нас защитит. Такое чтение должно происходить для одной цели в одном месте, чтобы остальная часть системы могла без труда оттуда получить данные, не задумываясь о том как именно они получены.

Этот пост вдохновлён вот этой статьёй.

Немає коментарів:

Дописати коментар

HyperComments for Blogger

comments powered by HyperComments