Надвор од API-ја: Архитектура за непречена еволуција на системите
API може да овозможи два системи да комуницираат. Сам по себе, не може да ги натера безбедно да еволуираат.
Таа разлика е сѐ поважна како што PHP апликацијата расте надвор од една кодна база и една база на податоци. Чист REST крајна точка може да крие кревка реалност: споделени табели, недокументирани претпоставки, цврсто поврзани Docker распоредувања и потрошувачи што толкуваат поле на суптилно различни начини. Првата интеграција успева. Петтата промена станува скапа.
Беспрекорната еволуција на системите е дисциплина на претворање на промената во нешто вообичаено. API-јата се дел од таа дисциплина, но вистинската архитектура се наоѓа во границите, сопственоста на податоците, практиките за распоредување и начинот на кој се справуваме со неуспесите.
Дизајнирајте договори, не само крајни точки
Дефиницијата на крајна точка е само делумен договор. Целосниот договор ги вклучува значењето на полињата, стандардните вредности, гаранциите за редоследот, однесувањето при грешки, автентикацијата, пагинацијата, идемпотентноста и што се случува кога зависноста е бавна или недостапна.
Разгледајте услуга за нарачки што првично враќа е-пошта на клиентот со секоја нарачка:
{
"id": "ord_123",
"status": "paid",
"customer_email": "[email protected]"
}
Отстранувањето на customer_email може да изгледа како безопасно подобрување на приватноста ако друго поле сега го идентификува клиентот. За работник за извоз, алатка за извештаи или мобилен клиент, тоа може да биде промена што ја нарушува продукцијата. Полето не беше само податок; беше зависност.
Кога е можно, претпочитајте адитивни промени. Воведете заменско поле, документирајте го неговото значење, дајте им време на потрошувачите да мигрираат и измерете дали старото поле сѐ уште се бара или користи пред да го отстраните. Верзионирањето може да помогне, но не е замена за внимателно управување со договорите. Верзионирано API што споделува нестабилна база на податоци сѐ уште е нестабилно.
Направете ја сопственоста видлива
Најважното прашање во дистрибуиран систем често не е „која услуга ја изложува оваа крајна точка?“ Туку „која компонента го поседува овој факт?“
Ако услугата за наплата ја поседува состојбата на фактурата, другите услуги не треба директно да ги ажурираат табелите за наплата. Директниот пристап на почетокот изгледа ефикасен, особено во PHP монолит што постепено се разложува. Тој создава и невидливи зависности: миграцијата на шемата сега бара координација со код што никогаш не бил наменет да се потпира на таа табела.
Сопственоста не бара веднаш микросервиси. Модуларен монолит може да обезбеди одлични граници кога модулите комуницираат преку експлицитни интерфејси на апликацијата, а миграциите се третираат како детали од внатрешната имплементација. Практичната цел е да се спречи една област од системот да навлегува во друга само затоа што врската со базата на податоци е достапна.
- На секој домен доделете јасен сопственик за запишувања и деловни правила.
- Намерно изложувајте модели за читање наместо да дозволите секој потрошувач да пребарува во оперативните табели.
- Евидентирајте важни промени на состојбата како доменски настани кога другите компоненти навистина треба да реагираат.
- Одржувајте ги содржините на настаните фокусирани на стабилни деловни факти, а не на необработени редови од базата на податоци.
Дозволете базата на податоци да еволуира по фази
Промените во базата на податоци ја заслужуваат истата компатибилна мисла како и јавните API-ја. Распоредување што додава колона што не смее да биде null, веднаш ја бара во кодот на апликацијата, а потоа ги распоредува сите потрошувачи, претпоставува совршено време. Вистинските распоредувања вклучуваат постепени инстанци, одложени работници, неуспешни изданија и рачно закрепнување.
Побезбеден образец е прошири, мигрирај, стесни. Прво додадете компатибилна промена во шемата. Потоа распоредете код што може да ги чита и старите и новите форми. Пополнете ги постојните записи во контролирани серии. Штом сите запишувачи и читачи ќе преминат, отстранете ја застарената патека во подоцнежно издание.
На пример, делењето на една колона name на first_name и last_name не треба да започне со бришење на name. Додадете ги новите nullable колони, ажурирајте ги запишувачите да ги пополнуваат двете претставувања, пополнете ги историските податоци каде што поделбата е сигурна, мигрирајте ги читачите и дури потоа повлечете го старото поле. Некои историски вредности може да бидат двосмислени; планот за миграција треба да го признае тоа наместо тивко да измислува сигурност.
Користете трансакции за локална вистина, не за глобална надеж
Трансакцијата во база на податоци е идеална за зачувување на конзистентноста во рамки на една ограничена операција. Таа не може безбедно да ја координира секоја надворешна акција. Ако трансакцијата создаде нарачка, а потоа повика давател на плаќања, истекувањето на времето создава двосмисленост: давателот можеби го обработил барањето иако апликацијата не примила одговор.
Користете идемпотентни клучеви за операции што може да бидат повторени и зачувајте ја намерата за објавување важна последователна работа. Трансакциско излезно сандаче е вообичаен пристап: запишете ја деловната промена и запис за појдовен настан во истата локална трансакција, а потоа дозволете работник да го испорача настанот. Работникот мора да толерира дупликатна испорака, бидејќи сигурните системи се градат околу повторни обиди, а не околу претпоставката дека секоја порака пристигнува точно еднаш.
Градете за делумни неуспеси
Секој мрежен повик може да не успее доцна, да не успее двапати или да успее без одговорот да стигне до повикувачот. Патеките на кодот треба експлицитно да ја прикажат таа реалност.
$response = $client->request('POST', '/charges', [
'headers' => ['Idempotency-Key' => $paymentAttemptId],
'json' => $payload,
]);
if ($response->getStatusCode() >= 500) {
throw new RetryablePaymentException();
}
Ова е само почетна точка. Повторувањето на секој неуспех веднаш може да го засили прекинот. Поставете ограничени временски ограничувања, разликувајте повторливи неуспеси од грешки при валидација, користете постепено зголемување на чекањето и испратете ја исцрпената работа на патека за неуспеси што може да се прегледа. Најважно, осигурете се дека самата операција е безбедна за повторување. Политика за повторни обиди без идемпотентност е политика за дупликатни наплати што чека да се случи.
Временските ограничувања треба да се изберат на секоја граница: влезни барања, прашања кон база на податоци, потрошувачка од редици и HTTP клиенти. Отсуството на временско ограничување не е трпение; тоа е неконтролирана обврска за ресурси.
Нека распоредувањето биде здодевно
Docker помага извршните околини да бидат повторливи, но контејнерите не го елиминираат оперативниот дизајн. Конфигурацијата треба да доаѓа од околината или од механизам за управување со тајни соодветен за платформата за распоредување, а не од слоеви на слики или предадени конфигурациски датотеки. Сликите треба да се изградат еднаш и да се промовираат меѓу околини наместо повторно да се градат со различни скриени претпоставки.
Проверките на здравјето треба да одговараат на корисни прашања. Проверката на живост може да покаже дека PHP процесот работи. Проверката на подготвеност треба да покаже дека инстанцата може да го опслужи сообраќајот што ќе го прими, без секој минлив проблем со зависност да се претвори во бура од рестартирања.
На распоредувањата им се потребни и прозорци на компатибилност. Потрошувачите може да обработуваат пораки во редицата откако производителот е ажуриран. Работниците може кратко да извршуваат постар код за време на постепено издание. Дизајнирајте ги шемите и пораките така што старите и новите верзии можат да коегзистираат доволно долго за да заврши воведувањето.
Оптимизирајте откако ќе можете да го видите системот
Работата на перформансите е најефикасна кога започнува со измерено тесно грло. Пред да додадете кеширање, проверете го бројот на прашања, плановите на прашањата, големината на содржините, доцнењето на редиците и латентноста на надворешните повици. Во PHP апликациите, познати проблеми како N+1 прашања, непотребна серијализација и големи колекции во меморијата често се поважни од егзотични инфраструктурни промени.
Кеширањето е исто така договор. Одлучете што го прави записот застарен, кој го поништува и што гледаат корисниците кога не е достапен. Кеш што тивко враќа застарени податоци за авторизација не е само карактеристика за перформанси; тоа е ризик за исправноста.
Еволуцијата е архитектонска карактеристика
Најсилните системи не се оние што никогаш не се менуваат. Тие се оние што можат да се менуваат без да принудат секој тим, услуга, база на податоци и распоредување да се движат во ист чекор.
Почнете со третирање на интерфејсите како ветувања, сопственоста на податоците како дизајнерска одлука и повторните обиди како нормално однесување. Додадете компатибилност пред отстранување. Набљудувајте пред да оптимизирате. Направете ја следната промена полесна од претходната.
Тоа е работата надвор од API-јата: градење софтвер што останува разбирлив и доверлив додека сѐ околу него еволуира.