Откажување од монолитот: прагматичен пат до скалабилни системи
Монолитот не е неуспех. Често е најбрзиот начин корисната идеја да се претвори во функционален производ: едно складиште, едно распоредување, една база на податоци и едноставен ментален модел. Проблемот започнува кога растот на системот ја надминува способноста на тимот безбедно да го менува.
Во тој момент, „преминете на микросервиси“ може да звучи како очигледен одговор. Не е. Разделувањето на кодната база воведува мрежни неуспеси, распределени податоци, потешко дебагирање, повеќе распоредувања и нови оперативни одговорности. Прагматичната цел не е начелно да се напушти монолитот. Таа е да се намалат трошокот и ризикот од промени, притоа зачувувајќи ја доверливоста.
Препознајте ги вистинските причини за разделување
Голема апликација може да остане здрава долго време. Бројот на датотеки, големината на складиштето или ентузијазмот на тимот за Kubernetes се слаби причини за нејзино разложување. Наместо тоа, барајте трајни граници што создаваат опипливи проблеми со испораката или скалирањето.
- На потсистем му треба независно скалирање, како обработка на слики, генерирање извештаи или испорака на webhook-ови.
- Промените во една област постојано наметнуваат ризични изданија во неповрзани области.
- Домен има различни барања за достапност, безбедност или задржување податоци.
- Тимовите не можат да работат независно бидејќи сопственоста и координацијата на изданијата се постојано испреплетени.
- Одредено оптоварување ја нарушува одзивноста на патеката за барања насочена кон клиентите.
Овие сигнали укажуваат на граница со оперативна вредност. Нејасната желба да се „модернизира“ не укажува. Ако главната болка е заплеткана кодна база, почнете со подобрување на нејзината внатрешна структура. Добро модуларизиран монолит често е најдобрата платформа од која подоцна може да се извлечат услуги.
Направете го монолитот модуларен пред да извлечете што било
Во PHP заднина, тоа обично значи организирање на кодот околу деловни способности, а не само околу технички слоеви. Наместо да дозволите секој контролер, команда и модел да пристапува до секоја табела и именски простор, воспоставете модули како наплата, идентитет, каталог или исполнување на нарачки. Секој модул треба да изложува намерни операции на ниво на апликација и да ги чува деталите за своето перзистирање приватни.
На пример, модулот за нарачки може да обезбеди операција што поставува нарачка и емитува доменски настан. Другите модули не треба директно да ги ажурираат табелите за нарачки само затоа што е погодно. Оваа дисциплина е вредна дури и ако сè уште работи во една PHP апликација и една база на податоци.
final class PlaceOrder
{
public function __construct(
private OrderRepository $orders,
private EventPublisher $events,
) {
}
public function handle(PlaceOrderRequest $request): Order
{
$order = Order::create($request->customerId, $request->items);
$this->orders->save($order);
$this->events->publish(new OrderPlaced($order->id()));
return $order;
}
}
Примерот е намерно едноставен. Во продукција, разгледајте ги границите на трансакциите, валидацијата, идемпотентноста и начинот на кој се зачувуваат настаните. Важното е дека модулот е сопственик на работниот тек. Таа сопственост станува договор за идна услуга, наместо случајна збирка табели.
Извлечете способност, не технички слој
Добрите први кандидати обично се тесни, асинхрони или способности што интензивно користат ресурси: индексирање за пребарување, известувања, конверзија на документи, извоз на ревизија или интеграција со давател на плаќања. Тие имаат јасни влезови и излези, а нивните неуспеси често може повторно да се обидат без да блокираат корисничко барање.
Лошо прво извлекување е основниот деловен работен тек што го допира секој дел од системот. Тоа можеби на крај ќе биде неопходно, но одеднаш го зголемува секој проблем со распределените системи: сопственост на податоци, конзистентност на трансакции, латентност, автентикација, верзионирање и одговор на инциденти.
Дефинирајте го договорот пред распоредувањето
Почнете со интеракцијата, не со рамката. Одлучете дали на новата компонента ѝ е потребен синхрон API, асинхрона порака или и двете. API за барање-одговор е соодветен кога повикувачот има потреба од непосреден одговор. Редицата обично е подобра кога работата може да се изврши подоцна и треба да преживее привремени неуспеси.
За надворешни API-и или API-и меѓу услуги, направете го однесувањето при неуспех експлицитно. Истекување на време, повторни обиди, дупликат барања и делумни прекини се нормални услови. На пример, барањето за плаќање треба да содржи клуч за идемпотентност, за повторниот обид да не создаде второ наплаќање. Слепото повторување на секоја грешка не е отпорност; може прекинот да го претвори во поплава од дупликат работа.
$response = $client->post('/payments', [
'headers' => [
'Idempotency-Key' => $idempotencyKey,
],
'json' => $payload,
'timeout' => 3.0,
]);
Истекувањето на времето му кажува само на повикувачот дека одговорот не пристигнал навреме. Не докажува дека оддалечениот систем не направил ништо. Осмислете ги API-то и процесот на усогласување околу таа неизвесност.
Внимателно преместете ја сопственоста на податоците
Најчестата архитектонска кратенка е истовремено една од најштетните: неколку услуги читаат и запишуваат во истите табели во базата на податоци. Делува ефикасно бидејќи податоците не треба да се преместуваат, но ја задржува старата спрегнатост додека ја крие зад одделни распоредувања.
Секоја извлечена услуга треба да стане авторитативен сопственик на своите податоци. Другите услуги комуницираат преку API-и или настани и одржуваат сопствени модели за читање каде што е потребно. Ова може да воведе евентуална конзистентност, што не е автоматски недостаток. Едноставно бара дизајн што е свесен за производот: прикажете состојба на чекање, обезбедете јасни ажурирања на статусот и избегнувајте да ветувате непосредни резултати кога работниот тек е асинхрон.
Миграцијата треба да биде постепена. Прво, насочете ги новите запишувања низ границата на модулот. Потоа воспоставете сигурен начин за синхронизација на податоците, дополнување на историските записи и проверка на бројките и однесувањето. Дури откако новиот сопственик ќе се докаже, старата патека за запишување треба да исчезне. Привремен слој за компатибилност често е побезбеден од едно единствено преминување сè или ништо.
Изградете ја оперативната основа рано
Услугите не се само помали апликации. Тие се независно управувани апликации. Пред да извлечете многу од нив, воспоставете повторлива основа за конфигурација, евидентирање, здравствени проверки, метрики, распоредување и враќање назад.
Docker може да ги направи локалните средини и распоредувањата поконзистентни, но контејнерот не е оперативна стратегија. На PHP услугата и понатаму ѝ се потребни предвидливо однесување при стартување, конфигурација базирана на околина, структурирани логови и јасна разлика меѓу подготвеност и живост. Подготвеноста одговара дали може да опслужува сообраќај сега; живоста одговара дали треба да се рестартира.
Исто така, направете ја набљудливоста дел од првото издание. Кога барањето поминува низ API порта, PHP апликација, worker за редица и нова услуга, логовите без идентификатори за корелација стануваат потрага по траги. Пренесувајте идентификатор на барање или трага преку границите, евидентирајте значајни грешки и следете ги деловните исходи што се важни: неуспешни задачи, одложена обработка, зголемени стапки на грешки и заостаток во редицата.
Одржувајте го системот полесен за менување
Најдобрата архитектура не е онаа со најмногу услуги. Таа е онаа што му дозволува на тимот да разбере промена, да ја тестира, да ја распореди и да се опорави од грешка без херојска координација. Понекогаш тоа значи една модуларна апликација. Понекогаш значи неколку фокусирани услуги околу јасни деловни граници.
Разложувањето функционира кога следи вистински притисок, ја зајакнува сопственоста и ја оправдува својата оперативна сложеност. Почнете така што ќе ги направите границите експлицитни во монолитот. Извлечете една способност со договор што можете да го тестирате и набљудувате. Учете од резултатот пред да ги умножите услугите.
Монолитот не е нешто што треба да се отфрли во драматично препишување. Тој е систем што треба да се развива со расудување. Кога е направено добро, патот до скалирање не е скок во распределена сложеност. Тој е низа мали одлуки што ја прават следната промена побезбедна од претходната.