Ние сме развълнувани да пуснем Lacinia, нашата библиотека GraphQL за Clojure! Използваме GraphQL в производството повече от година за множество услуги.

Като част от изданието, бихме искали да обсъдим обосновката зад приемането на GraphQL от нашия екип, с какви проблеми се сблъскахме, как GraphQL ги е решил и накъде се насочваме оттук нататък.

Това е предназначено да бъде нетехническо обяснение защо смятаме, че GraphQL е чудесен начин за решаване на проблеми. За документация относно Lacinia вижте Хранилището на Github.

Нашите проблеми

Една от основните отговорности на нашия екип е да предостави на клиентите на Walmart и Sam’s Club незабавен достъп до цялата им история на разписки в магазина, директно от техния iOS/Android смартфон или уеб браузър. Ние управляваме емисия в реално време за всяка покупка и връщане от над пет хиляди магазина до 500 касови бележки в секунда и до четирикратно този брой в Черния петък и през празничния сезон.

В крайна сметка разполагаме със значителна база данни на Касандра с клиенти, разписки и асоциациите, които ги свързват. Целият ни стек от страната на сървъра е написан на Clojure и е изграден да обслужва този входящ канал с данни ефективно и надеждно. За малко задкулисно как работи всичко това, вижте доклада на Антъни Маркар на Clojure/West 2015, „Clojure At Scale at WalmartLabs“.

В стремежа си да обслужваме клиентите на Walmart и Sam’s Club възможно най-добре, ние предлагаме нашето огромно количество данни на много групи в компанията. Всеки има свои собствени нужди и грижи, например:

  • Екипите на мобилното приложение искат само полетата, необходими за попълване на кратък изглед. Те се интересуват от неща като общата покупна цена на разписката, броя на артикулите, датата и местоположението. Те също се нуждаят от способността да извличат по-подробна информация чрез последващи обаждания. Мобилните инженери искат оптимизирани полезни натоварвания и възможност за бързо повторение по време на процеса на проектиране, без да чакат промени в задния край. Всички допълнителни данни, изпратени до клиента, са загубена честотна лента.
  • Екипите, разработващи уебсайтовете, предоставят по-богато изживяване при пазаруване, насочено към настолен уеб браузър. Те обикновено работят с повече пространство за гледане и искат да поискат по-подробна информация от екипите на мобилните приложения.
  • Други екипи често имат един или два основни въпроса, на които трябва да отговорят, и искат да могат да запитват нашата система само за тези конкретни параметри. Примерите включват Savings Catcher, който кредитира клиентите за закупени артикули, ако имат по-ниска цена в магазин на конкурент, и Customer Protections, който уведомява клиентите, ако закупен артикул бъде изтеглен по-късно.

Всеки от тези екипи има много различни изисквания за това до каква подгрупа от данни за транзакции трябва да има достъп. Да направим нашите данни лесни за консумация, особено след като сме малка група инженери, е основно предизвикателство, което традиционната REST API структура не реши за нас.

С течение на времето се озовахме в незавидната позиция да поддържаме, разширяваме и документираме колекция от API, всеки първоначално създаден за конкретен случай на употреба. Всеки от тези API използва различен HTTP стек, беше конфигуриран по различен начин и имаше свои собствени ad hoc конвенции за URL схеми, параметри на заявки и т.н., вдъхновени от практиките по времето, когато са създадени. Използвани по-нови услуги Пиедестал и Компонент; по-старите услуги използваха Ring манипулатори и разпръснато променливо състояние.

В същото време имахме повече екипи, които идваха при нас, заинтересовани да изградят услуги върху нашите – добър проблем. Развитието на някой от тези съществуващи API беше труден проблем. Добавянето на ново поле към JSON отговор беше рисковано, тъй като можеше да има непредвидени последици за някои клиенти, които разчитаха отговорът да бъде обвързан с точен набор от известни ключове. Тъй като нашите услуги използват споделени вътрешни библиотеки, промяна в една от тези библиотеки, предназначена за една услуга, може по невнимание да промени поведението на друга несвързана услуга.

Искаме да сме свободни да развиваме и подобряваме нашите API без сложни схеми за версии и сложни планове за миграция. Трябва да можем уверено да правим промени в поддържащия код, без да се напрягаме за неоткритите странични ефекти в далечни услуги. Като бек-енд екип решихме да спрем да вземаме продуктови решения за продуктовите екипи. Вместо това сме упълномощили всеки екип да прави запитвания към нашата система по начини, които най-добре поддържат конкретния продукт, който създават.

Решението

След като Facebook представи разговори и писания на конференцията за GraphQL и неговата обосновка, бяхме уверени, че технологията ще послужи като елегантен начин за подобряване на нашите услуги. Имаше обаче проблем: ние сме екип на Clojure. По онова време GraphQL нямаше добра история за Clojure. За щастие GraphQL има стабилна спецификация, така че ние се заехме да изградим наша собствена напълно реализирана реализация — Lacinia.

Спецификацията на GraphQL е изключително важна за нас. Нашата философия е, че нашата библиотека трябва да говори и разбира GraphQL като lingua franca и да има установена семантика за изпълнение, но ние се стремим към идиоматично решение, което работи най-добре в екосистемата на Clojure. Един пример за тази философия е да се управлява възможно най-много от данни: дефиницията на схемата, която декларира възможностите на GraphQL сървъра, е написана в EDN и използва структури от данни на Clojure. Манипулирането на тази структура от данни с код на Clojure не само се поддържа, но се насърчава.

Междувременно изпълнението на GraphQL заявки с помощта на Clojure е много естествено. GraphQL по същество е много функционален подход: процес на филтриране и трансформация, който започва с всички възможни данни и се стеснява само до желаните от клиента резултати. Използването от Clojure на чисти функции и постоянни структури от данни гарантира, че тези операции са безопасни и ефективни.

След като разработихме вътрешна версия на Lacinia, успяхме да отхвърлим почти всички наши съществуващи API и вградени настоящи и бъдещи клиенти в нашите услуги, задвижвани от GraphQL. Когато отворите разписките си от магазина Walmart на телефона си или посетите samsclub.com и прегледате разписките си в мрежата, вие се обслужвате от Lacinia. Ако използвате Walmart Grocery, вие използвате Lacinia.

Проектирахме нашите схеми около нашите модели на данни и клиентите могат просто да поискат това, от което се нуждаят. Разбира се, не можем да знаем всички възможни случаи на употреба, но когато клиент ни каже, че не може да търси това, което иска, можем обикновено да добавим липсващото поле или връзка и да внедрим за няколко минути. Често нашият вътрешен модел на данни вече включва информацията и е само въпрос на актуализиране на схемата, за да се покаже новото поле. Тъй като клиентите контролират какви данни виждат, добавянето на нови полета и типове към съществуваща схема винаги е безопасно и винаги обратно съвместимо.

Не само това, всичко в схемата е напълно откриваемо и документирано чрез интроспекция. Ние обслужваме екземпляр на GraphiQLi!) уеб базирана IDE: това позволява на разработчиците в други екипи интерактивно да създават и изпълняват заявки. Lacinia автоматично имплементира частите за интроспекция на спецификацията GraphQL; всичко, което трябва да направим, е да включим ориентирана към потребителя документация за полета и типове.

Преди, когато помагахме на други екипи, хвърляхме ad hoc curl команди и винаги беше предизвикателство да възпроизведем проблемите на нашите клиенти. Днес предаваме GraphiQL връзка, която може да включва цялата заявка — моментална възпроизводимост.

В по-голямата част от времето включването на нов клиент към нашата услуга е само въпрос на предоставяне на URL адреса за GraphiQL и те могат бързо и интерактивно да очертаят своите заявки и да научат за всички полета и типове през интерфейса. Ако имат проблеми или въпроси, те могат да ни изпратят своите проблемни запитвания. Изненадващо разклонение на това е доколко аз, като разработчик на услугата, започнах да разчитам на GraphiQL за собствения си ежедневен работен процес: по-бързо и по-лесно е да се създаде заявка за GraphQL, отколкото е за директен достъп до базовата база данни Cassandra. GraphiQL, на върха на GraphQL, представлява изключително мощен инструмент.

Смятаме, че clojure.spec е бъдещето на надеждните системи Clojure. Lacinia приема clojure.spec и се стреми да го използва навсякъде. Например персонализираните скалари се дефинират с помощта на конформери. За да останем стабилни на Clojure 1.8, докато Clojure 1.9 преминава през различни алфа версии, ние изтеглихме clojure-future-spec, backport на различните функции на clojure.spec, достъпни за Clojure 1.9.

Бъдещето

GraphQL представлява критична част от нашата система както от техническа, така и от културна гледна точка. Walmart е голямо място и когато ангажираме хора в други области на компанията относно GraphQL, те се интересуват да научат повече за това, което правим с него и как могат да се възползват от нашата работа и опит. Ние сме развълнувани да продължим развитието на Lacinia на открито. Lacinia все още не е напълно съвместима с публикуваната „спецификация“, но се надяваме скоро да стигнем до там. Имаме желание да работим заедно с общността и с радост ще приемем и обсъдим всякакви проблеми или заявки за изтегляне.

За повече информация и техническа документация посетете Github repository.