Повеќе од микросервиси: Архитектура за сложеноста на системите во реалниот свет
Микросервисите често се претставуваат како природен следен чекор по монолитот: поделете ја апликацијата, распоредувајте ги услугите независно и дозволете им на тимовите да се движат побрзо. Во пракса, таа промена може да замени еден вид сложеност со неколку потешки. Кодната база може да стане помала по услуга, додека системот станува потежок за разбирање, тестирање, управување и развивање.
Корисното прашање не е „монолит или микросервиси?“ Туку: каде припаѓа сложеноста, кој ја поседува и дали тимот може сигурно да управува со неа? Добрата архитектура ја прави важната работа очигледна. Лошата архитектура го распрснува критичното однесување на места што поединечно се разумни, но заедно се нејасни.
Сложеноста не исчезнува кога услугите се раздвојуваат
Модуларна PHP апликација што работи како една единица за распоредување има реални ограничувања: изданијата се заеднички, користењето ресурси е заедничко и неуспехот во една област може да влијае на друга. Но таа има и вредни својства. Барањето може да се проследи во еден процес. Трансакција во базата на податоци може да наметне инваријанта. Рефакторирањето на граница на домен обично е промена во кодот, наместо дистрибуирана миграција.
Штом некоја можност стане посебна услуга, нејзиниот интерфејс станува долгорочен договор. Повиците може да истечат. Пораките може да пристигнат двапати. Распоредување може да остави две верзии да работат истовремено. Запишувањето во база на податоци може да успее, додека настанот наменет да извести друга услуга не успее. Ништо од ова не е аргумент против услугите; тоа е нормалната цена на дистрибуцијата.
Затоа, границата на услуга е оправдана со повеќе од структура на директориуми. Таа треба да претставува можност што се менува независно, граница на сопственост или потреба за скалирање и сигурност што не може чисто да се реши во постојната апликација.
Почнете со добро структурирана монолитна апликација
„Монолит“ не треба да значи „еден огромен слој на контролери поврзан директно со секоја табела“. Монолитна апликација што може да се одржува може да има силни внатрешни граници: модули за наплата, идентитет, каталог и известувања; експлицитни апликациски услуги; стабилни интерфејси; и тестови што ги проверуваат деловните правила без да го подигнат целиот веб-стек.
За PHP бекенд, ова често значи дека грижите на рамката се задржуваат на работ. Контролерот го преведува HTTP влезот во апликациска команда. Кодот на доменот одлучува што е валидно. Инфраструктурниот код зачувува податоци или повикува надворешни системи. Точниот образец е помалку важен од спречувањето деловните правила да се копираат во контролери, обработувачи на редици, CLI команди и закажани задачи.
Внатрешната модуларност создава опции. Ако на некој модул подоцна му треба независно распоредување, неговите зависности се веќе видливи. Ако никогаш не треба да се извлече, тимот сепак има корист од јасна сопственост и помалку случајни меѓумодулски барања.
Направете ги зависностите намерни
Практично правило е еден модул да не навлегува во деталите за перзистентноста на друг модул. Ако модулот за нарачки има потреба од информации за клиентот, изложете мал интерфејс на ниво на апликација наместо да дозволите произволни читања од табелите за клиенти. Тоа може да делува побавно од директно спојување табели, но ја открива поврзаноста пред таа да стане ограничување во продукција.
Границите на базата на податоци заслужуваат иста грижа. Заедничка база на податоци не е автоматски неуспех, особено во модуларен монолит. Проблемот почнува кога секоја компонента ја третира секоја табела како јавна. Доделете сопственост на табелите и миграциите, документирајте ги малкуте одобрени патишта за интеграција и одолејте на претворањето извештајни барања во скриени зависности за оперативните работни текови.
Користете API-ја како договори, не како поврзувачка инфраструктура
API е договор за значењето, не само за обликот на JSON. На полињата им требаат сопственост, правила за животен циклус, валидација и очекувања за компатибилност. Одговор наречен status е двосмислен сè додека потрошувачите не ги знаат неговите дозволени вредности, дали може да се појават нови вредности и кои дејства се безбедни за секоја вредност.
За синхрони HTTP API-ја, дизајнирајте за делумен неуспех уште од почетокот. Поставете временски ограничувања за поврзување и барања. Намерно обработувајте одговори што не се успешни. Користете повторни обиди само кога операцијата е безбедна за повторување или е заштитена со клуч за идемпотентност. Повторниот обид за читање по привремен неуспех може да биде разумен; слепото повторување на создавање плаќање може да создаде дупликат работа.
$response = $client->request('POST', '/orders', [
'headers' => [
'Idempotency-Key' => $requestId,
],
'json' => $payload,
'timeout' => 5,
]);
if ($response->getStatusCode() === 409) {
// Retrieve the existing result associated with the idempotency key.
}
Примерот намерно е нецелосен: на клиентот сè уште му треба обработка на исклучоци, евидентирање што избегнува чувствителни вредности и јасна политика за двосмислени исходи. Ако мрежата откаже откако оддалечениот систем ќе го прифати барањето, повикувачот можеби нема да знае дали операцијата се случила. Идемпотентноста е начинот на кој системот ја претвора таа двосмисленост во состојба од која може да се опорави.
Изберете асинхрона работа од вистинските причини
Редиците се вредни кога работата може да се изврши подоцна, кога оптоварувањето треба да се израмни или кога барањето не треба да чека бавна надворешна зависност. Тие не се кратенка околу конзистентноста. Задача ставена во редица може да доцни, да се изврши повеќе од еднаш или да откаже откако поврзана трансакција во базата на податоци ќе се потврди.
Дизајнирајте ги работниците како секоја порака повторно да може да биде доставена. Чувајте доволно состојба за да ја идентификувате завршената работа, направете ги обработувачите идемпотентни и дефинирајте што треба да се случи откако ќе се исцрпат повторните обиди. Редица за мртви пораки не е решение сама по себе; тоа е место каде што нерешената работа чека оперативна одлука.
Кога промена во базата на податоци мора на крајот да објави настан, образецот outbox често е посигурен од обидот да се извршат двете дејства во едно барање. Апликацијата ја запишува деловната промена и запис за појдовен настан во истата трансакција во базата на податоци. Посебен објавувач испраќа неиспратени записи и ги означува како објавени само по успешно предавање. Ова не обезбедува магична испорака точно еднаш, но му дава на системот трајна, проверлива патека за опоравување.
Оперативната едноставност е архитектонска карактеристика
Docker може да ги направи локалните околини репродуктивни, но контејнерите не ги отстрануваат оперативните одговорности. Услуга подготвена за продукција сè уште има потреба од управување со конфигурација, проверки на здравјето, структурирани логови, метрики, резервни копии, дисциплина за миграции и стратегија за враќање назад.
Миграциите на базата на податоци особено лесно се потценуваат. Распоредувањето на апликацијата може накратко да извршува стар и нов код истовремено. Безбедна низа на миграција вообичаено прво додава nullable колона или нова табела, распоредува код што може да работи со двете структури, пополнува податоци наназад во контролирани серии и ги отстранува старите структури само откако веќе не се користат. Преименувањето многу користена колона во еден чекор често е ризик за достапност маскиран како расчистување.
- Поставете експлицитни ограничувања на ресурсите и набљудувајте го растот на меморијата кај долготрајните PHP работници.
- Одделете ја подготвеноста од живоста: процесот може да биде жив, а сепак да не може да опслужува корисен сообраќај.
- Поврзете ги логовите, идентификаторите на барањата, задачите во редиците и појдовните повици за неуспесите да може да се следат преку границите.
- Вежбајте враќање резервни копии; резервна копија што никогаш не била вратена е претпоставка, не план за опоравување.
Оптимизирајте го системот што луѓето можат безбедно да го менуваат
Работата на перформансите треба да почне со измерено тесно грло. Тоа може да биде индекс што недостига, повторено барање, неограничен сет на резултати, бавна патека за серијализација, исцрпен базен на конекции или надворешен повик на патеката на барањето. Кеширањето може да помогне, но воведува и правила за невалидирање и однесување со застарени податоци што мора да се дизајнираат, а не да се надева дека ќе исчезнат.
Истиот прагматизам важи и за разложувањето. Извлечете услуга кога тековната граница создава повторувачка оперативна болка, конфликтен ритам на изданија, изолирани потреби за скалирање или модел на сопственост што постојната апликација не може да го поддржи. Извлекувањето затоа што дијаграмот изгледа модерно ретко е доволно.
Архитектурата не е бројот на контејнери во продукција. Таа е збирот на одлуки што му дозволуваат на тимот да размислува за промена, неуспех и опоравување. Дисциплиниран монолит може да биде одличен систем. Внимателно избрана услуга може да биде преобразувачка. Трајната цел е поедноставна: изградете граници што одговараат на реалноста, а потоа направете ги тешките патеки видливи пред продукцијата да мора да ви ги открие.