ИТ развој

Beyond Abstraction: Building PHP Services That Actually Work

Надвор од апстракцијата: Градење PHP сервиси што навистина функционираат

Повеќето PHP сервиси не откажуваат затоа што PHP не е способен. Тие откажуваат затоа што кодната база станува поверна на апстракцијата отколку на работата што сервисот навистина мора да ја извршува: прифаќање влез, заштита на податоци, повикување зависности, преживување при делумен неуспех и давање доволно докази на операторите за да дејствуваат.

Доброто backend инженерство не е натпревар за создавање најмногу слоеви. Тоа е дисциплина на правење важното однесување лесно за наоѓање, лесно за тестирање и тешко за погрешна употреба. PHP е целосно способен да ја поддржи таа дисциплина кога неговите граници се избрани внимателно.

Започнете со корисна граница

Границата на сервисот треба да одговара на значајна одговорност, а не на модерен шаблон. На пример, endpoint што создава нарачка има потреба од валидација, одлука за авторизација, трансакција во базата на податоци и можеби порака за последователна обработка. Тоа се реални грижи. Воведувањето генерички „manager“, „handler“, „factory“ и „provider“ за секоја од нив можеби нема да ги разјасни.

Практичната структура често е едноставна: HTTP-грижите нека останат во контролер, случајот на употреба нека биде во апликациски сервис, а перзистентноста или надворешните системи нека бидат изолирани зад мали интерфејси. Основниот случај на употреба треба да се чита како деловната операција што ја претставува.

final class CreateOrder
{
    public function __construct(
        private OrderRepository $orders,
        private TransactionManager $transactions,
        private OrderEvents $events,
    ) {}

    public function handle(CreateOrderCommand $command): Order
    {
        return $this->transactions->run(function () use ($command): Order {
            $order = Order::fromCommand($command);

            $this->orders->save($order);
            $this->events->record(new OrderCreated($order->id()));

            return $order;
        });
    }
}

Ова не е „чисто“ затоа што има интерфејси. Корисно е затоа што намерата за трансакцијата, перзистентноста и настаните е видлива. Читателот веднаш може да го постави вистинското оперативно прашање: како се испорачуваат евидентираните настани откако трансакцијата ќе се потврди?

Направете го неуспехот првокласен влез во дизајнот

Кодот за среќниот пат обично е краток. Производното однесување живее во истечени временски ограничувања, повторни обиди, дупликат-барања, недостапни бази на податоци, истечени акредитиви и пораки испорачани повеќе од еднаш. Ова не се исклучителни детали што треба да се додадат подоцна; тие го обликуваат дизајнот од самиот почеток.

Разгледајте барање што наплаќа клиент и создава нарачка. Трансакција во базата на податоци не може атомски да вклучи далечински давател на плаќања. Ако наплатата успее, а запишувањето во базата не успее, слепото повторување може повторно да го наплати клиентот. Одговорот не е поголема трансакција. Тоа е експлицитна стратегија за идемпотентност: прифатете клуч обезбеден од клиентот, зачувајте го резултатот поврзан со тој клуч и осигурете се дека истиот клуч го враќа првичниот исход.

Исто така, директното објавување на настан по потврдување на трансакција во базата има празнина. Процесот може да запре откако ќе ја потврди нарачката, но пред да го објави настанот. Outbox табела ја затвора таа празнина: запишете ги нарачката и outbox записот во една трансакција во базата на податоци, а потоа нека посебен worker ги испорача записите што чекаат. Испораката и понатаму мора да биде идемпотентна, бидејќи worker може успешно да испрати порака и да откаже пред да ја означи како завршена.

  • Поставете временски ограничувања за излезните HTTP и конекции кон базата на податоци.
  • Повторувајте само неуспеси што веројатно се привремени, со ограничен број обиди.
  • Користете клучеви за идемпотентност за команди видливи надворешно.
  • Евидентирајте доволно контекст за истражување неуспеси без логирање тајни.
  • Дизајнирајте ги потрошувачите да толерираат дупликат-пораки и испорака со променет редослед каде што е применливо.

Одржувајте го моделот на базата на податоци чесен

ORM-ите можат да ја забрзаат вообичаената перзистентност, но не го отстрануваат однесувањето на базата на податоци. Барање во циклус сè уште е барање во циклус. Недостасувачки индекс сè уште е недостасувачки индекс. Трансакција што останува отворена додека се повикува друг сервис сè уште е извор на конкуренција и неуспех.

Моделирајте ги ограничувањата таму каде што припаѓаат. Ако адресата за е-пошта мора да биде единствена, спроведете го тоа со уникатно ограничување во базата на податоци, а потоа претворете го настанатиот конфликт во корисен одговор на апликацијата. Проверките на страната на апликацијата го подобруваат корисничкото искуство, но истовремените барања може да ја поминат истата проверка. Само базата на податоци може да ја спроведе инваријантата во моментот на запишување.

За важни читања, проверете го барањето што навистина се извршува и неговиот план за извршување. Претпочитајте пагинација што одговара на шемата на пристап. Offset пагинацијата може да биде во ред за мали административни листи; за голем, често променлив feed, keyset пагинацијата заснована на стабилно, индексирано подредување често е попредвидлива.

Трансакциите треба да бидат кратки и намерни

Користете трансакции за зачувување поврзани локални промени, не како општа обвивка околу цело барање. Кога е можно, валидирајте ги влезовите и извршете бавни далечински повици пред да отворите трансакција. Во трансакцијата, направете ги суштинските запишувања, спроведете ги инваријантите и потврдете ја. Ова ги одржува заклучувањата кратки и ја прави конкуренцијата полесна за расудување.

Контејнерите треба да ги намалат изненадувањата

Docker помага кога дефиницијата на контејнерот ги прави претпоставките за извршувањето експлицитни. Производната слика треба да ги содржи апликацијата и само потребните runtime зависности. Алатките за градење, алатките за тестирање и развојната конфигурација обично припаѓаат во посебна фаза на градење или поставка за локален развој.

FROM php:8.3-cli AS runtime

WORKDIR /app
COPY . /app

CMD ["php", "bin/worker.php"]

Овој пример е намерно мал, а не универзална производна слика. На вистински сервис му требаат и намерен чекор за инсталирање зависности, runtime корисник без root-привилегии каде што е соодветно, конфигурација обезбедена преку неговата околина за распоредување и стратегија за здравствена проверка што одразува што процесот навистина може да опслужи. Не правете здравствената проверка да зависи од бавно API од трета страна, освен ако таа зависност е потребна за секое барање.

Конфигурацијата треба да се валидира при стартување. Раното откажување затоа што отсуствува потребен URL за базата на податоци е побезбедно отколку прифаќање сообраќај и неуспех при секое барање. Чувајте ги тајните надвор од изворната контрола, логовите, пораките за исклучоци и слоевите на сликата на контејнерот.

Набљудливоста е дел од интерфејсот

Сервис што не може да објасни што прави е скап за одржување. Логовите треба да бидат доволно структурирани за филтрирање и корелација. Вклучете идентификатор на барање или трага, име на операција, исход и безбедни идентификатори како ID на нарачка. Избегнувајте исфрлање цели тела на барања, заглавија за авторизација, лозинки, токени или детали за плаќање.

Метриките треба да одговорат на оперативни прашања: колку барања откажуваат, колку траат клучните операции, дали редиците растат и дали повиците кон зависностите го надминуваат временското ограничување. Алармите треба да бидат врзани за симптоми што влијаат на корисниците или јасни ризици за капацитет, а не за секој исклучок што повторниот обид успешно го решил.

Изберете јасност без церемонии наместо церемонијална архитектура

Апстракциите го заслужуваат своето место кога кријат променливи детали, создаваат стабилна граница за тестирање или олеснуваат спроведување на правило што се повторува. Не го заслужуваат само затоа што прават дрвото на директориуми да изгледа подготвено за претпријатија.

Најсилните PHP сервиси ретко се мистериозни. Нивните барања имаат јасни патеки, нивните податоци имаат спроведливи правила, нивните зависности имаат однесување при неуспех, а нивното распоредување има експлицитни претпоставки. Градете за тие реалности. Кога системот е под притисок, јасноста не е естетско дотерување; таа е функционалноста што го одржува сервисот во работа.

Портрет на автор на блогот

Mihajlo

Јас сум Михајло - развивач поттикнат од љубопитност, дисциплина и постојаната желба да создадам нешто значајно. Споделувам увиди, упатства и бесплатни услуги за да им помогнам на другите да ја поедностават својата работа и да растат во постојано развивачкиот свет на софтверот и вештачката интелигенција.