Системска архитектура: Прифаќање на прагматизмот наместо догматизмот
Дискусиите за архитектурата можат да станат чудно религиозни. Еден тим ги смета микросервисите за единствената сериозна опција. Друг инсистира дека секој систем мора да започне како модуларен монолит. Некој предлага event bus пред првиот клиент да заврши работен тек. Некој друг ја отфрла секоја апстракција што не била потребна вчера.
Добрата архитектура не е натпревар за докажување дека некој образец е чист. Таа е континуирана практика на правење корисни компромиси, притоа зачувувајќи ја способноста подоцна да се направат подобри.
За backend инженерите, прагматизмот не е изговор за невнимателни кратенки. Тој значи разбирање на цената на одлуката: оперативен товар, начини на откажување, брзина на развојот, конзистентност на базата на податоци, безбедност, перформанси и веројатноста дека барањата ќе се променат. Најдобриот дизајн обично е наједноставниот што ги опфаќа денешните реални ограничувања без утрешниот ден непотребно да го направи скап.
Започнете со ограничувања, а не со претпочитан образец
Пред да изберете сервиси, редици, бази на податоци или топологија на распоредување, дефинирајте што системот навистина треба да прави. Ограничувањата можеби се технички, но често се обликувани од производот и тимот.
- Колку корисници и барања мора системот да поддржува сега, и каков раст е веројатен?
- Кои работни текови бараат силна конзистентност, а кои можат да толерираат доцнење?
- Кои податоци се чувствителни, регулирани или тешко се обновуваат?
- Колку инженери ќе го одржуваат системот и ќе управуваат со него кога нешто ќе откаже?
- Што е поскапо: одложена испорака, прекин во работата, неточни податоци или бавно време на одговор?
Мала PHP апликација со конвенционална релациска база на податоци може да поддржи изненадувачка количина деловна сложеност. Таа може да обезбеди автентикација, интеграции за наплата, позадински задачи, извештаи и добро дизајниран API без веднаш да има потреба од посебни сервиси што се распоредуваат независно. Прераното разделување често создава нови проблеми: дистрибуирано следење, дуплирана конфигурација за распоредување, верзионирани договори, мрежни откажувања и сложен локален развој.
Тоа не значи дека монолитот е универзално исправен. Компонента на која ѝ требаат независно скалабилни работници, има посебна граница на доверливост или ја одржува одделен тим, можеби заслужува изолација. Поентата е дека границата треба да решава познат проблем, а не само да наликува на архитектурски дијаграм од поголема компанија.
Изградете модуларен монолит пред да дистрибуирате сложеност
Монолитот не е автоматски заплеткан, исто како што микросервисите не се автоматски модуларни. Корисната разлика е дали кодот има разбирливи граници.
Во PHP backend, организирајте го однесувањето околу домени, наместо само околу технички слоеви. Модулот за нарачки, на пример, може да ги поседува создавањето нарачки, правилата за цени, операциите за зачувување и настаните поврзани со нарачките. Другите модули треба да комуницираат преку намерно дефинирани интерфејси, наместо директно да пристапуваат до внатрешни табели или класи.
final class CreateOrder
{
public function __construct(
private OrderRepository $orders,
private PaymentGateway $payments
) {
}
public function handle(CreateOrderRequest $request): Order
{
$order = Order::fromRequest($request);
$this->payments->authorize($order->total());
$this->orders->save($order);
return $order;
}
}
Точната рамка и распоредот на директориумите се помалку важни од дисциплината зад нив. Држете ги HTTP контролерите едноставни. Избегнувајте да ставате деловни правила во миграции на базата на податоци, шаблони или повратни повици на рамката. Направете ги зависностите видливи. Пишувајте тестови околу значајно однесување. Овие избори овозможуваат идно издвојување ако модулот навистина треба да стане сервис.
Издвојувањето треба да биде последица на притисок, а не шпекулативна инвестиција. Кога границата веќе е јасна во монолитот, преместувањето зад API или редица е тешко, но разбирливо. Кога границата не постои, издвојувањето сервис обично ја преместува збунетоста наместо да ја отстрани.
Избирајте бази на податоци заради исправност, пред новитет
Одлуките за бази на податоци се уште едно место каде модата може да го надвладее расудувањето. Релациската база на податоци често е најпрагматичниот стандарден избор за оперативни системи, бидејќи трансакциите, ограничувањата, спојувањата и зрелите алатки директно ги решаваат вообичаените деловни потреби.
Ако фактурата мора да има валиден клиент, претставете ја таа врска со странски клуч каде што е соодветно. Ако салдото не смее да стане негативно, разгледајте дали инваријантата припаѓа во логиката на апликацијата, трансакциските ажурирања или ограничување во базата на податоци. Чист модел на објекти не ги заштитува податоците кога истовремено пристигнуваат конкурентни барања.
Прагматизмот значи и користење специјализирано складиште кога оптоварувањето го оправдува тоа. Индекс за пребарување може да го подобри пронаоѓањето. Object store може да одговара за прикачени датотеки. Кеш може да ги намали повторените скапи читања. Но секое дополнително складиште на податоци воведува прашања за сопственост: која копија е авторитетна, како се синхронизираат податоците, како се поправаат откажувањата и како ќе функционираат резервните копии и контролите за пристап?
Кешот генерално треба да се третира како потрошен. Ако неговото бришење го оштети производот, тој не бил само кеш; бил недокументирано примарно складиште на податоци.
Дизајнирајте API како договори, а не како излез од контролер
API станува архитектура во моментот кога друг клиент зависи од него. Тој клиент може да биде прелистувач, мобилна апликација, партнерска интеграција, алатка од командна линија или друг внатрешен процес. Стабилните договори заслужуваат намерен дизајн.
Користете експлицитна валидација на барањата, предвидливи одговори за грешки и јасни проверки за авторизација. Држете ги транспортните аспекти одвоени од однесувањето на доменот. Ако крајна точка создава ресурс, одлучете што се случува кога клиентот ќе се обиде повторно по истекување на времето. Дупликат плаќање или дупликат нарачка не е мал граничен случај; тоа е нормална последица од ненадежни мрежи.
Клучевите за идемпотентност можат да ги направат операциите чувствителни на повторни обиди побезбедни. На пример, клиентот може да испрати единствен клуч со барање за плаќање, а серверот може да го врати оригиналниот исход кога повторно ќе го види тој клуч. Имплементацијата мора да ги зачувува клучот и исходот доволно сигурно за да го преживее релевантниот период за повторен обид. Самото прифаќање заглавие без спроведување на однесувањето создава лажна сигурност.
И верзионирањето треба да биде пропорционално. Верзија во секоја URL не е замена за дисциплина за компатибилност. Претпочитајте адитивни промени каде што е можно, јасно документирајте го однесувањето и отстранувајте полиња само откако потрошувачите ќе имаат реалистичен пат за миграција.
Користете Docker за да ги намалите разликите, а не да ги скриете
Docker е вреден кога ги прави средините за развој, тестирање и распоредување поповторливи. Слика на контејнер може да спакува PHP runtime, екстензии и зависности на апликацијата, така што софтверот работи со помалку изненадувања на различни машини.
Тоа не ги отстранува оперативните грижи. Конфигурацијата и понатаму треба безбедно да се обезбеди. Миграциите на базата на податоци и понатаму бараат координација. Дневниците мора да стигнат до место што операторите можат да го прегледаат. Тајните не треба да се вградени во сликите. Контејнер што успешно се стартува сепак може да откаже затоа што не може да пристапи до својата база на податоци, нема дозвола за запишување во потребен директориум или користи застарена конфигурација.
Одржувајте го патот за распоредување доволно едноставен за да биде разбирлив под притисок. Кога е можно, направете здравствените проверки да одразуваат значајна подготвеност. Одвојте ги артефактите во време на градење од конфигурацијата во време на извршување. Тестирајте го планот за миграција и враќање пред инцидент во продукција да го наметне тоа.
Мерете пред да оптимизирате
Работата на перформансите е најефективна кога започнува со видливо тесно грло. Крајна точка може да биде бавна поради барање без индекс, повторен пристап до базата на податоци, прекумерна серијализација, повик кон надворешен API или worker од редица што не успева да го одржи чекорот. Секој проблем бара различно решение.
Во PHP апликации, честите добивки вклучуваат избегнување N+1 барања, избирање само на потребните колони, додавање индекси што одговараат на вистинските обрасци на барања, преместување несуштинска работа во позадински задачи и внимателно кеширање на избрани резултати со многу читања. Ништо од ова не треба да се применува слепо. Индексот забрзува некои читања, но ја зголемува цената на запишувањето. Редицата го подобрува времето на одговор, но воведува асинхроно справување со откажувања. Кеширањето го намалува оптоварувањето, но создава правила за поништување.
Практичното прашање не е: „Која е најбрзата архитектура?“ Туку: „Што е доволно бавно за да биде важно, зошто е бавно и која промена го подобрува тоа без да создаде поголем проблем?“
Зачувајте ја можноста за избор таму каде што промената е веројатна
Одржливоста во голема мера е способност безбедно да се промени системот. Тоа не бара градење точки за проширување за секоја замислена иднина. Бара препознавање на областите што најверојатно ќе се променат: даватели на плаќања, правила за цени, интеграции со трети страни, канали за известување и потреби за известување.
Користете интерфејси таму каде што постои вистинска алтернатива или веродостојна граница. Користете конфигурација за реални разлики меѓу средините. Избегнувајте апстракции што само обвиткуваат еден метод и го прават кодот потежок за следење. Целта не е максимална флексибилност; туку корисна флексибилност.
Архитектурата ја заслужува својата вредност кога ја прави следната важна промена побезбедна, појасна или поевтина.
Прагматичните системи не се поедноставени. Тие ја сфаќаат доверливоста сериозно, ја признаваат неизвесноста и ја држат сложеноста врзана за докажана потреба. Започнете со јасни граници, заштитете го интегритетот на податоците, дизајнирајте за вообичаени откажувања и измерете ги проблемите што корисниците навистина ги чувствуваат. Потоа дозволете архитектурата да се развива како одговор на докази. Тоа е помалку драматично од догмата, но така софтверот останува корисен долго откако неговиот прв дијаграм ќе застари.