Архитектура на API за отпорни системи надвор од привремените трендови
Повеќето неуспеси на API не се предизвикани од непозната рамка или услуга во облак што недостасува. Тие започнуваат со мали кратенки што изгледаат безопасно: крајна точка што враќа што и да содржи базата на податоци, повторен обид што повторува неидемпотентно плаќање, истекување на времето без јасен сопственик или одговор за грешка што го менува обликот на секои неколку месеци.
Отпорните API не се градат со предвидување на секој тренд. Тие се градат со донесување неколку трајни одлуки за договорите, неуспесите, сопственоста на податоците и операциите. Тие одлуки продолжуваат да носат придобивки кога сообраќајот расте, тимовите се менуваат и модерните алатки на моментот веќе се заменети.
Почнете со договорот, а не со контролерот
API е ветување дадено на друг систем. Тој систем може да биде прелистувач, мобилна апликација, интеграција со партнер, работник во заднина или друга услуга. Имплементацијата може слободно да се менува само кога ветувањето останува разбирливо и сигурно.
Дефинирајте ги ресурсите и дејствијата со термини што ги препознаваат потрошувачите. Клиентот не треба да ја разбира вашата внатрешна структура на табели за да креира нарачка. Избегнувајте да откривате споредни детали, како што се имиња на колони, ORM-врски или идентификатори специфични за складиштето, во јавните одговори.
Стабилниот одговор има предвидливи полиња, типови, статусни кодови и семантика на грешки. Не мора да го изложи секое можно поле уште од првиот ден. Всушност, воздржаните одговори полесно се развиваат бидејќи може да се додаваат нови опционални полиња без да се принудат потрошувачите да се приспособат.
{
"data": {
"id": "ord_8f3a",
"status": "pending",
"total": {
"amount": 2499,
"currency": "USD"
}
}
}
Претставувањето на парите како цел број во нивната најмала единица избегнува изненадувања со подвижна запирка. Уште поважно, групирањето на износот и валутата го прави значењето експлицитно. Договорот треба да ја отежни неправилната употреба, а не само да ја документира правилната употреба.
Направете го неуспехот првокласно однесување на API
Секој мрежен повик може да не успее, да пристигне доцна или да се повтори. Секоја база на податоци може привремено да биде недостапна. Отпорниот дизајн ги претпоставува овие услови пред продукцијата да го наметне прашањето.
Истекувањата на времето треба да бидат намерни. Без нив, една бавна зависност може да ги зафати работниците на апликацијата сè додека неуспеат неповрзани барања. Со премногу агресивни истекувања, здравата работа може предвреме да се напушти. Поставете разумно ограничување врз основа на буџетот на повикувачот, а потоа направете го неуспехот видлив преку дневници, метрики и одговор врз кој клиентот може да дејствува.
Телата на грешките заслужуваат исто внимание како и успешните одговори. Корисната грешка обезбедува стабилен код читлив за машина, порака читлива за човек и релевантни детали за полињата без изложување на внатрешни информации.
{
"error": {
"code": "validation_failed",
"message": "Барањето содржи невалидни полиња.",
"details": {
"email": ["Потребна е валидна е-пошта."]
}
}
}
Не враќајте необработени пораки за исклучоци или грешки од базата на податоци. Во најдобар случај, тие се нестабилни договори, а во најлош случај, безбедносни ризици. Внатрешно, задржете богат дијагностички контекст со идентификатор на барањето. Надворешно, вратете доволно информации за клиентот да може да го исправи барањето или да одлучи дали да се обиде повторно.
Повторните обиди бараат идемпотентност
Повторните обиди се вредни при привремени неуспеси, но се опасни кога операцијата создава нешто со последици во реалниот свет. Ако на клиентот му истече времето по поднесување на нарачка, тој не може да знае дали серверот ја завршил работата непосредно пред да падне врската.
За операции за креирање што мора да толерираат повторни обиди, прифатете клуч за идемпотентност и трајно зачувајте ја неговата поврзаност со резултирачката операција. Повторено барање со истиот клуч треба да го врати оригиналниот резултат наместо да креира друга нарачка. Клучот мора соодветно да биде ограничен по опсег, трајно складиран и валидиран во однос на барањето, за да не може случајно да повтори друга операција.
Во PHP, ова често значи да се третира идемпотентноста како логика на апликацијата, наместо да се надевате дека само HTTP-методот ќе обезбеди безбедност. Ограничување на единственост во базата на податоци може да биде дел од решението, но треба да биде спарено со трансакциско ракување и дефиниран одговор за дупликатни обиди.
Одржувајте ги границите на базата на податоци чесни
Базите на податоци се одлични во спроведувањето факти што секогаш мора да останат вистинити. Користете ограничувања за единствени надворешни идентификатори, задолжителни врски, валидни опсези и референтен интегритет каде што е соодветно. Валидацијата во кодот на апликацијата ја подобрува повратната информација за корисникот; ограничувањата во базата на податоци ја штитат точноста кога друга кодна патека, работник или идна услуга ја заобиколува таа валидација.
Трансакциите треба да покриваат една кохерентна единица локална работа. На пример, креирањето нарачка и резервирањето локален инвентар може да припаѓаат во една трансакција. Испраќањето е-пошта, повикувањето давател на плаќања или објавувањето порака до друга услуга не треба лежерно да се ставаат во таа трансакција. Надворешните повици може да бидат бавни, не можат да се поништат од вашата база на податоци и може да создадат збунувачки делумни исходи.
Практичен образец е outbox: запишете ја деловната промена и запис за настан во истата трансакција, а потоа дозволете работник да објавува настани што чекаат. Работникот мора да толерира и дупликатна испорака, бидејќи сигурното објавување обично значи прифаќање дека потрошувачот може да види настан повеќе од еднаш.
Дизајнирајте за перформанси без да ја криете работата
Работата на перформансите започнува со знаење каде одат времето и капацитетот. Брза крајна точка не е онаа со најмногу кешови; таа е онаа чија скапа работа е намерна, измерена и ограничена.
Внимавајте на вообичаените замки во задниот дел:
- N+1 прашања: вчитување поврзани податоци еден ред во исто време, наместо нивно намерно преземање.
- Неограничени листи: враќање на секој соодветен запис наместо задолжително страничење.
- Скапа серијализација: вчитување големи графови на објекти само за да се отфрлат повеќето полиња.
- Нејаснотија во кешот: сервирање застарени податоци без јасна политика за свежина или патека за поништување.
Страничењето треба да воспостави стабилно подредување. Страничењето со отстапување е едноставно за многу административни прикази, додека страничењето засновано на курсор може подобро да се однесува за големи колекции што често се менуваат. Ниту еден избор не е универзално подобар; изберете врз основа на обликот на прашањето, потребите на потрошувачот и конзистентноста што корисниците ја очекуваат додека прелистуваат страници.
Docker помага претпоставките за извршување да станат експлицитни, но контејнерот не е архитектура. Чувајте ја конфигурацијата на апликацијата надвор од сликата, внимателно користете поставки специфични за околината и осигурете се дека контејнерот правилно реагира на сигнали за прекин. Распоредување што нагло ги прекинува работниците може да дуплира задачи или да прекине барања дури и кога кодот на апликацијата инаку е исправен.
Изберете едноставни споеви и јасна сопственост
Одржливоста во голема мера е способноста да се промени една област без страв од уште пет. Во PHP-заден дел, тоа обично фаворизира јасни слоеви: HTTP-ракувањето ги преведува барањата и одговорите, апликациските услуги ги координираат случаите на употреба, доменската логика ги изразува деловните правила, а инфраструктурните адаптери се справуваат со бази на податоци, редици и надворешни API.
Ова не е аргумент за церемонија околу секоја класа. Тоа е аргумент за сместување на сложеноста таму каде што може да биде именувана, тестирана и заменета. Контролер преполн со авторизација, валидација, SQL, повици за плаќање и форматирање одговори може да работи денес, но нема безбеден спој за утрешната промена.
Верзионирајте API само кога промената што крши компатибилност е навистина неопходна. Пред да креирате нова верзија, разгледајте дали дополнително поле, опционален параметар, ознака за способност или нова крајна точка го зачувува постојното ветување. Верзиите се скапи бидејќи создаваат паралелни договори што мора да се поддржуваат, документираат, следат и на крај да се повлечат.
Отпорноста е навика на експлицитност
Привремените трендови ветуваат кратенки. Трајната API-архитектура поставува појасни прашања: Што се случува ако ова барање се повтори? Кој е сопственик на овие податоци? Што може да не успее тука? Како се опоравува клиентот? Која инваријанта го штити ова правило кога кодот се менува?
Најсилните системи ретко се оние со најразработени дијаграми. Тие се оние каде што договорите се намерни, неуспесите не се изненадувачки, правилата за податоци се спроведени и оперативното однесување е разгледано пред инцидентот. Вградете ги тие навики во секоја крајна точка и вашето API ќе надживее многу повеќе од еден технолошки циклус.