ИТ развој

Beyond CRUD: Architecting APIs with Purpose and Longevity

Надвор од CRUD: Архитектирање API-ја со цел и долговечност

CRUD е местото каде што започнуваат многу API-и и каде што премногу од нив остануваат. Создавање, читање, ажурирање, бришење: образецот е лесен за објаснување, брз за поставување и често доволен за внатрешен прототип. Но API што опстојува со вистински корисници, променливи барања, неуспешни барања, позадински задачи и неколку клиентски апликации има потреба од посилно тежиште од табелите во базата на податоци.

Трајното прашање не е „Кои крајни точки го изложуваат овој модел?“ Туку „Кои способности треба да ги понуди овој систем и кои правила мора да останат вистинити додека ги нуди?“ Таа промена го претвора API-то од тенок транспортен слој во намерно поставена граница околу деловното однесување.

Дизајнирајте според намерата, не според табелите

API ориентиран кон ресурси е вреден, но имињата на ресурсите не треба да ја принудуваат секоја операција во генерички ажурирања на полиња. Разгледајте нарачка. Наивна крајна точка може да му дозволи на клиентот да испрати PATCH /orders/42 со нов status. Тоа изгледа флексибилно сè додека системот не мора да спречи испорака пред плаќање, да резервира залиха, да извести магацин и да запише ревизорска трага.

Статусот не е само колона. Тој претставува премин со последици. Направете ја таа намера видлива:

POST /orders/42/confirm
POST /orders/42/cancel
POST /orders/42/ship

Овие крајни точки не се изговор за напуштање на доследен API дизајн. Тие се признавање дека некои промени на состојбата се доменски дејства. Секое дејство може да ги провери предусловите, да изврши работа атомски каде што е можно, да емитува настан и да врати грешка што му кажува на клиентот што може да направи понатаму.

Користете CRUD слободно за едноставни податоци: преференции, нацрти, референтни записи или адресар управуван од корисник. Воведете експлицитни команди кога ажурирањето носи политика, несакани ефекти или значаен премин во животниот циклус.

Нека инваријантите живеат на едно место

Најлесниот начин да создадете кревок бекенд е да ги распрснете деловните правила низ контролери, валидатори на барања, работници во редици и код за предниот дел. Контролер проверува еден услов, скрипта за увоз го прескокнува, а нова администраторска крајна точка тивко нарушува претпоставка што никогаш не била запишана.

Во PHP апликација, контролерите обично треба да го преведуваат HTTP во повик кон апликацијата. Тие не треба да станат единственото место каде што системот знае како да потврди нарачка или да додели претплата. Ставете ја операцијата зад услуга, дејство или случај на употреба фокусиран на доменот, чие име го одразува однесувањето.

final class ConfirmOrder
{
    public function handle(Order $order): void
    {
        if (! $order->canBeConfirmed()) {
            throw new OrderCannotBeConfirmed();
        }

        DB::transaction(function () use ($order) {
            $order->confirm();
            $this->inventory->reserveFor($order);
            $this->events->dispatch(new OrderConfirmed($order->id));
        });
    }
}

Точната структура на рамката е помалку важна од границата. Истата операција треба да може да ја користи HTTP контролер, CLI команда или потрошувач на пораки без дуплирање правила. Ограничувањата во базата на податоци сè уште се важни: кодот на апликацијата обезбедува јасно однесување, додека уникатните индекси, странските клучеви и соодветните проверки ги штитат податоците кога кодот на апликацијата е заобиколен или кога се судираат конкурентни барања.

Изберете договори што можат да еволуираат

API договорот е површина на производот. Штом клиентите зависат од него, промената на поле или облик на одговор има цена. Практичниот одговор не е бескрајно верзионирање; туку дополнителните промени да бидат стандард.

  • Додавајте незадолжителни полиња во одговорите наместо да го менувате значењето на постојните.
  • Прифаќајте само документирани влезни полиња и отфрлајте или намерно игнорирајте непознати полиња според јасна политика.
  • Користете стабилни идентификатори што не откриваат претпоставки за внатрешноста на складирањето.
  • Враќајте предвидливи облици на грешки за клиентите да можат да разликуваат неуспеси на валидација, неуспеси на авторизација, конфликти и привремени дефекти.
  • Пагинирајте ги крајните точки за колекции од самиот почеток кога колекцијата може да расте.

Верзионирајте само кога промената е навистина некомпатибилна и не може да се воведе заедно со постојното однесување. Бројот на верзијата не решава двосмисленост. Јасната семантика, периодите за застарување и тестовите на договори прават повеќе за зачувување на довербата.

Идемпотентноста е избор во дизајнот

Мрежите повторуваат. Корисниците кликнуваат двапати. Извршувачите на задачи се рестартираат. Крајна точка што создава плаќање, нарачка или барање за обезбедување не треба да претпоставува дека ќе биде повикана точно еднаш.

За операции кај кои дупликатното извршување е штетно, прифатете клуч за идемпотентност и зачувајте ја поврзаноста меѓу тој клуч, повикувачот, барањето и завршениот резултат. Повторното барање тогаш може да го добие оригиналниот резултат наместо да го повтори несаканиот ефект. Дизајнот на складирањето мора да се справи со конкурентна употреба на истиот клуч; уникатно ограничување во базата на податоци е посигурно од заклучување ограничено на процесот.

Идемпотентноста не значи дека секое барање е безбедно за повторување. Таа значи дека API-то дефинира што значи повторувањето и доследно ја спроведува таа дефиниција.

Нека базата на податоци ја наметне реалноста

Перформансите и точноста често се среќаваат кај моделот на податоци. Индексирајте ги колоните што се користат за спојувања, селективни филтри, подредување и клучеви за пребарување, но не додавајте индекси по инерција: секој индекс додава трошок за запишување и просторен товар. Проверете ги пребарувањата што API-то навистина ги извршува, па потоа мерете со репрезентативни податоци.

Чест образец на неуспех е вчитување страница со родителски записи, а потоа пребарување поврзани податоци еднаш за секој запис. Кодот е читлив изолирано, но барањето станува побавно како што расте страницата. Користете претходно вчитување или насочено спојување кога врската е потребна и избирајте ги само полињата што ги бара одговорот.

Трансакциите треба да штитат кохерентна единица работа, а не да обвиткуваат произволна бавна работа. Избегнувајте повикување надворешни услуги додека трансакцијата во базата на податоци е отворена. Најпрво потврдете ја трајната состојба, а потоа испратете задача или објавете настан преку сигурен образец соодветен за системот. Ако дејството надолу по текот не успее, му треба стратегија за повторување и видлив пат за неуспех; полошо е да се преправате дека се случило отколку да го пријавите.

Нека HTTP работата биде кратка, а позадинската работа експлицитна

API барањето генерално треба да го валидира влезот, да го авторизира повикувачот, да ја направи потребната промена на состојбата и да врати одговор брзо. Испраќање голема серија е-пошта, генерирање извештај, промена на големина на медиуми или синхронизирање систем од трета страна припаѓа во позадинска обработка.

Тоа раздвојување помага само ако задачите се дизајнирани сериозно како и крајните точки. На задачата ѝ треба јасен товар, безбедно однесување при повторување, политика за максимален број повторувања и доволно контекст за дневници и алатки за поддршка. Треба да е безбедно да се изврши по пад на процесот. Ако не може да се направи идемпотентна, заштитете го неповратниот чекор со трајна промена на состојбата.

Docker може да ги направи локалните средини и средините за распоредување поповторливи, но не ги брише оперативните избори. Чувајте ја конфигурацијата на апликацијата во променливи на околината, извршувајте миграции како намерен чекор при распоредување и направете здравствените проверки да ги одразуваат зависностите потребни за опслужување сообраќај. Успешното стартување на контејнер не е доказ дека апликацијата може да обработува барања.

Набљудливоста е дел од интерфејсот

Кога API-то не успее, на клиентот му треба корисен одговор, а на операторот му треба корисна трага. Вклучете идентификатор на барање или корелација во дневниците и одговорите. Запишувајте структуриран контекст како рута, автентициран актер каде што е соодветно, име на операцијата и категорија на исклучок. Не запишувајте акредитиви, токени или чувствителни тела на барања само затоа што се пригодни при дебагирање.

Метриките треба да одговараат на оперативни прашања: Колку барања не успеваат? Кои крајни точки се бавни? Дали редиците растат? Дали повторувањата успеваат или се натрупуваат? Дневниците објаснуваат поединечни инциденти; метриките откриваат обрасци. И двете се највредни кога се додадени пред првиот тежок продукциски инцидент.

Градете за следната промена

Долговечноста не се постигнува со предвидување на секоја идна функционалност. Таа произлегува од тоа денешните правила да бидат експлицитни, одговорностите да се тесни и да се зачуваат можностите таму каде што промената е веројатна. Добро дизајнираното API не ја изложува секоја операција во базата на податоци. Тоа комуницира способности, ги штити инваријантите и им дава на клиентите однесување на кое можат да се потпрат.

CRUD останува корисен. Тој едноставно не е архитектурата. Архитектурата започнува кога API-то престанува да ги пресликува табелите и почнува да ги изразува ветувањата што системот има намера да ги одржи.

Портрет на автор на блогот

Mihajlo

Јас сум Михајло - развивач поттикнат од љубопитност, дисциплина и постојаната желба да создадам нешто значајно. Споделувам увиди, упатства и бесплатни услуги за да им помогнам на другите да ја поедностават својата работа и да растат во постојано развивачкиот свет на софтверот и вештачката интелигенција.