Престанете да проектирате за функции, почнете да градите за проток
Многу бекенд системи стануваат тешки многу пред да станат големи. Вообичаената причина не е сообраќајот, големината на тимот или егзотичен проблем со базата на податоци. Тоа е навика во дизајнот: секоја побарана функционалност да се третира како нов архитектонски објект.
Пристигнува функционалност, па се појавува нова услуга. Новата крајна точка добива сопствен слој за оркестрација. Мала варијација во работниот тек станува уште еден сет од табели, ознаки, настани и исклучоци. Поединечно, секој избор изгледа разумно. Заедно, создаваат систем оптимизиран за објаснување на своите делови, наместо за сигурно придвижување на работата од барање до исход.
Силната архитектура помалку се однесува на тоа колку граници можете да нацртате, а повеќе на тоа колку непречено важните текови минуваат низ нив.
Функционалностите се именки; текот е глагол
Дизајнот ориентиран кон функционалности природно се фокусира на именки: претплати, фактури, извози, известувања, профили, дозволи. Тие именки се важни, но корисниците доживуваат глаголи. Тие се регистрираат, плаќаат, увезуваат, пребаруваат, се обидуваат повторно, преземаат и закрепнуваат.
Архитектурата ориентирана кон текови започнува со прашањето што мора да се случи од почеток до крај. Разгледајте клиент што бара извоз на сметка:
- API-то го автентицира и овластува барањето.
- Системот евидентира дека е побаран извоз.
- Работник собира податоци без да држи отворена HTTP-врска.
- Генерираната датотека се складира безбедно.
- Клиентот добива траен начин да ја преземе.
- Неуспесите може повторно да се обработат без да создадат збунувачки дупликати.
Тоа е вистинската единица на дизајнот. Крајната точка за извоз, задачата во редицата, адаптерот за складирање, записот во базата на податоци и известувањето се придружни детали. Ако тие детали се дизајнираат независно, среќната патека може да работи, додека повторните обиди, делумните неуспеси и истрагите за поддршка стануваат мачни.
Мапирајте ја патеката пред да го изберете шаблонот
Пред да воведете услуга, настан, апстракција или нов модел на база на податоци, опишете го текот на едноставен јазик. Вклучете ги состојбите, сопственикот на секоја транзиција и што се случува кога зависноста е бавна или недостапна.
Корисен тест е дали развивач што не е запознаен со кодот може да одговори на овие прашања:
- Што ја започнува оваа операција?
- Каде е запишана нејзината моментална состојба?
- Кој чекор може безбедно да се повтори?
- Како изгледа успехот?
- Како изгледа неуспехот за корисник и за оператор?
- Како се наоѓа и продолжува незавршената работа?
Ако одговорите бараат пребарување низ контролери, слушачи, cron задачи и неколку репозиториуми, системот можеби има граници без кохерентен тек.
Ова не значи дека на секоја операција ѝ е потребен формален механизам за работни текови. Повеќето PHP апликации имаат корист од нешто поедноставно: експлицитно поле за состојба, мала апликациска услуга, трансакциска граница и задача во редица за работа што не треба да се случува за време на барањето.
Направете ја состојбата видлива
Скриената состојба е една од најскапите форми на сложеност. Запис означен како completed додека неговото известување надолу по текот тивко не успеало, и на корисникот и на тимот за поддршка им остава двосмислен систем.
Претпочитајте значајни, набљудливи состојби како requested, processing, ready и failed. Тие треба да ја опишуваат деловната операција, а не само последниот метод што се извршил. Транзицијата на состојба треба да има и јасно правило: кој смее да ја направи, под кои услови и дали е безбедно повторно да се обиде.
DB::transaction(function () use ($accountId) {
$export = AccountExport::create([
'account_id' => $accountId,
'status' => 'requested',
]);
GenerateAccountExport::dispatch($export->id);
});
Важната идеја не е синтаксата на рамката. Тоа е договорот: барањето се евидентира пред да започне асинхроната работа. Ако испраќањето бара посилни гаранции за испорака од предавање на редица со најдобар напор, користете траен запис во стил на outbox и објавете го одделно. Не претпоставувајте дека потврдата на база на податоци и операцијата со надворешна редица се едно атомско дејство, освен ако вашата инфраструктура изречно ја обезбедува таа гаранција.
Дизајнирајте API-ја околу завршувањето, а не околу обликот на контролерот
API крајната точка е ветување за тек. За брзи, детерминистички операции, синхрон одговор е соодветен. За бавна или променлива работа, преправањето дека секое барање може да заврши во рамките на еден HTTP одговор создава истекувања на време, притисок врз меморијата и кршливо однесување при повторни обиди.
За долготрајна операција, вратете прифатен одговор со ресурс што може да се провери подоцна. Клиентот може да анкетира крајна точка за статус или да добие известување преку канал што производот веќе го поддржува. Точниот транспорт е помалку важен од стабилен животен циклус.
POST /account-exports
Authorization: Bearer <token>
HTTP/1.1 202 Accepted
Location: /account-exports/exp_123
Идемпотентноста припаѓа во овој разговор. Мрежните клиенти се обидуваат повторно. Балансерите на оптоварување се обидуваат повторно. Луѓето кликнуваат двапати. Ако создавањето на истата операција двапати е штетно, прифатете клуч за идемпотентност и поврзете го со исходот на барањето. Повторниот обид треба да го врати првичниот резултат, а не да создаде втор извоз, наплата или низа е-пораки.
Одржувајте ги трансакциските граници чесни
Трансакциите во базата на податоци се одлични за заштита на поврзани локални промени. Тие не се решение за општа намена за координирање оддалечени API-ја, редици, даватели на е-пошта или складирање на објекти.
Честа патека на неуспех изгледа вака: кодот зачувува запис за плаќање, повикува надворешна услуга, па потоа потврдува. Ако оддалечениот повик успее, но потврдата во базата на податоци не успее, оддалечениот и локалниот свет не се согласуваат. Превртувањето на редоследот само менува која недоследност е можна.
Дизајнот ориентиран кон текови ја признава оваа реалност. Локално запишете ја намерата, извршете ја оддалечената работа со токен за идемпотентност каде што е достапен, зачувајте го резултатот и овозможете усогласување. За чувствителни операции, изградете јасна патека за поправка, наместо да се потпирате на едно непрекинато извршување.
Овој начин на размислување го подобрува и дизајнот на базата на податоци. Табелите треба да ги зачуваат информациите потребни за објаснување на моменталната состојба и закрепнување од неуспех. Една преоптоварена колона status може да биде во ред на почетокот, но не отфрлајте временски ознаки, причини за неуспех, обиди или надворешни референтни ID-а кога се суштински за управување со текот.
Користете Docker за да го направите текот репродуцируем
Контејнерите се највредни кога ја намалуваат неизвесноста на околината. Текот на PHP апликација често зависи од повеќе од PHP: веб-сервер, база на податоци, кеш, работник во редица, распоредувач и понекогаш локално складирање на објекти или пресретнувач на е-пошта.
Целта не е да се репродуцира продукцијата со максимална церемонија. Целта е да му се овозможи на развивачот да ги стартува зависностите потребни за проверка на важна патека и да разбере кој процес е сопственик на која одговорност.
Направете ги работникот и распоредувачот експлицитни. Ако извозот во редица успева само затоа што развивачот случајно извршува команда во друг терминал, таа зависност е дел од архитектурата без оглед дали е документирана или не.
services:
app:
build: .
worker:
build: .
command: php artisan queue:work
scheduler:
build: .
command: php artisan schedule:work
Имињата и командите ќе се разликуваат според рамката, но принципот важи: моделирајте ги процесите што го придвижуваат текот напред. Потоа обезбедете здравствени проверки, дневници и упатства на ниво на оперативен прирачник соодветни на оперативните потреби на системот.
Оптимизирајте ја патеката што е важна
Работата на перформансите е исто така полесна кога се поставува како тек. Не почнувајте со „ни треба кеширање“. Почнете со бавната или скапата патека: кое пребарување, чекор на серијализација, оддалечен повик, заклучување или заостаток во редицата го продолжува исходот видлив за корисникот?
Мерете на границите. Следете го времетраењето на барањето, доцнењето во редицата, времетраењето на задачата, однесувањето на пребарувањата во базата на податоци и стапките на грешки каде што тие сигнали се достапни. Додавајте индекси затоа што реалниот образец на пристап ги бара, а не затоа што некоја табела изгледа важна. Кеширајте стабилни, често читани податоци со јасни правила за поништување, а не цели одговори чија точност зависи од скриен кориснички контекст.
Размислувањето во текови спречува локални оптимизации што само ја преместуваат цената на друго место. Побрза крајна точка што создава неограничен заостаток во редицата не е побрза на начинот на кој им е важно на корисниците.
Нека границите му служат на патувањето
Модулите, услугите, репозиториумите, настаните и API-јата се вредни алатки. Тие стануваат штетни кога постојат главно за да задоволат дијаграм. Границата ја заслужува својата цена кога ја разјаснува сопственоста, изолира грижа што се менува или прави критичен тек побезбеден за развој.
Градете околу работата што вашиот систем мора сигурно да ја заврши. Направете ја нејзината состојба експлицитна. Очекувајте повторни обиди и делумен неуспех. Одржувајте ги асинхроните чекори набљудливи. Потоа изберете ја најмалата архитектура што го прави тоа патување јасно.
Функционалностите ќе продолжат да пристигнуваат. Ако текот остане разбирлив, секоја од нив има разумно место каде да оди.