Надвор од ВИ: Архитектирање софтвер што ги учи вашите деловни правила
Повеќето софтвери не треба да станат „интелигентни“ пред да станат вредни. Треба да станат сигурни во одлуките што веќе го дефинираат бизнисот: кои нарачки може да се испорачаат, кои попусти може да се комбинираат, кога на некоја сметка ѝ е потребна проверка и кој може да одобри исклучок.
Овие правила често почнуваат како нешто расфрлано низ контролери, барања кон базата на податоци, формули во табеларни пресметки, знаење на тимот за поддршка и итни закрпи во продукција. Апликацијата можеби работи, но секоја нова функционалност се претвора во археолошка вежба. Вистинскиот архитектонски предизвик не е додавање ВИ. Тој е градење софтвер што може да изразува, применува, менува и објаснува бизнис-правила без да ја направи кодната база кршлива.
Бизнис-правилата се првокласна грижа во дизајнот
Бизнис-правило е исказ што одредува исход во некоја домена. „Нарачките над кредитниот лимит бараат одобрување“ е правило. „Клиентот може да добие бесплатна испорака само во подобни региони“ е правило. „Фактурите може да се откажат само пред порамнување“ е правило.
Правилата не се исто што и техничката валидација. Проверувањето дали е-пошта е синтаксички валидна е техничка грижа. Проверувањето дали таа е-пошта припаѓа на одобрен корпоративен домен за договорно ниво е бизнис-логика. Мешањето на двете создава код што е тешко да се тестира и уште потешко безбедно да се измени.
Првата корисна разлика е меѓу стабилни доменски концепти и променлива политика. Order, Invoice и Customer се трајни концепти. Прагот при кој нарачката бара одобрување е политика. Дајте му место на секој во дизајнот.
Чувајте ги правилата надвор од транспортните слоеви
Во PHP апликација, контролерите треба да ги преведуваат HTTP-барањата во апликациски дејства. Тие не треба да одлучуваат дали трансакцијата е дозволена. Исто така, ORM-моделот обично е погрешно место за растечка збирка на меѓусебно засегнати одлуки за политики.
Мала апликациска услуга може да ја координира работата, додека фокусиран објект за политика ја поседува одлуката:
final class CreditApprovalPolicy
{
public function requiresApproval(Order $order, Customer $customer): bool
{
return $order->totalAmount()->isGreaterThan(
$customer->creditLimit()
);
}
}
final class PlaceOrder
{
public function __construct(
private CreditApprovalPolicy $approvalPolicy,
private OrderRepository $orders
) {
}
public function handle(Order $order, Customer $customer): void
{
if ($this->approvalPolicy->requiresApproval($order, $customer)) {
$order->markPendingApproval();
} else {
$order->confirm();
}
$this->orders->save($order);
}
}
Ова не е формалност сама за себе. Одлуката има име, може независно да се тестира и повторно да се користи од API-крајна точка, работник во редица, процес на увоз или задача од командната линија. Апликациската услуга го опишува работниот тек; политиката го опишува правилото.
Моделирајте го јазикот што луѓето навистина го користат
Одржливиот бизнис-софтвер има корист од заеднички речник. Ако финансискиот тим вели „порамнување“, не именувајте ја состојбата completed во една услуга, paid во друга и finalized во трета, освен ако тие термини навистина значат различни нешта.
Добрите имиња ги намалуваат грешките при преведувањето. Тие исто така рано ја откриваат двосмисленоста. Метод наречен canCancel() го повикува тимот да дефинира што значи откажување. Дали вклучува поврат на средства? Дали е дозволено по испораката? Дали банкарски трансфер што чека ја менува одлуката? Тие прашања се одлуки за производот, а архитектурата треба да ги направи видливи наместо да ги крие во вгнездени услови.
За работни текови со значајни последици, моделирајте ги премините меѓу состојби експлицитно. Булова вредност како is_approved не е доволна кога вистинскиот животен циклус вклучува поднесено, во преглед, одобрено, одбиено, истечено и повлечено. Јасните состојби го отежнуваат случајното воведување невалидни премини.
Користете податоци за политика, код за значење
Тимовите често се нишаат меѓу две некорисни крајности: тврдо кодирање на секој праг или сместување на целата домена во генерички механизам за правила. Прагматична граница функционира подобро.
Чувајте како податоци вредности што бизнис-корисниците разумно треба да можат да ги приспособат: лимити за одобрување, подобни земји, датуми на стапување во сила, мапирања на категории на производи и права за функционалности. Чувајте ги доменското значење и сложеното однесување во код: како се споредуваат износите, како правилата заемно дејствуваат, како се извршува премин и што се случува кога недостига потребна политика.
На пример, табела со верзионирана политика може да содржи праг за одобрување со период на важност. Кодот сè уште одлучува дека мора да се избере точно една активна политика и дека недостигот на политика ја блокира операцијата наместо тивко да ја одобри.
- Користете ограничувања во базата на податоци за инваријанти што секогаш мора да важат, како ненегативни количини и единствени надворешни референци.
- Користете трансакции кога правило зависи од тоа повеќе запишувања да успеат заедно.
- Користете политики на ниво на апликација за одлуки што бараат доменски контекст.
- Запишете ја верзијата на политиката или влезовите употребени за значајни одлуки кога е важно подоцнежно објаснување.
Оваа поделба спречува конфигурацијата да стане нетипизиран програмски јазик, а сепак дозволува контролирана промена на политиката.
Направете ги одлуките објасниви
Булов одговор ретко е доволен. Оперативните тимови, клиентите и идните развивачи треба да знаат зошто барањето било одбиено или испратено на преглед. Целта не е да се откриваат внатрешни детали на имплементацијата; таа е да се задржи значаен контекст за одлуката.
Наместо да враќате само false, вратете мал резултатен објект со статус и код за причина. API-то може да го мапира тој резултат во стабилен одговор, додека дневниците и ревизорските записи ги зачувуваат релевантните идентификатори и верзијата на политиката.
final class EligibilityResult
{
public function __construct(
public readonly bool $eligible,
public readonly string $reason
) {
}
}
// Example reasons: "region_not_supported", "account_overdue"
Кодовите за причина се подобри од копирање проза наменета за луѓе низ секој слој. Тие остануваат доволно стабилни за клиенти и известување, додека презентацијата може да се локализира или измени без промена на основниот договор за правилото.
Дизајнирајте API-ја околу исходи, а не околу табели од базата на податоци
API што само ја пресликува перзистенцијата открива внатрешна структура и ги повикува клиентите повторно да ја создаваат бизнис-логиката. Ако клиентите мора да проверат пет полиња за да одредат дали фактурата може да се откаже, правилото ќе оттурка меѓу веб-апликации, интеграции и мобилни клиенти.
Наместо тоа, изложете значајни дејства и исходи. Крајна точка како POST /invoices/{id}/cancellation-requests може да претставува намерна бизнис-операција. Нејзиниот одговор може да соопшти дали барањето е прифатено, одбиено или бара преглед.
Идемпотентноста е важна секогаш кога се можни повторни обиди. Мрежен тајмаут не му кажува на повикувачот дали серверот го обработил барањето. За команди иницирани однадвор, прифатете клуч за идемпотентност, зачувајте го со добиениот исход и вратете го првичниот исход при валиден повторен обид. Ова го штити правилото од двојна примена затоа што инфраструктурата се однесувала нормално.
Тестирајте го правилото на соодветното ниво
Бизнис-правилата заслужуваат директни тестови со читливи примери. Тест наречен order_over_credit_limit_requires_approval е компактен дел од извршлива документација за политиката. Тој е повреден од тест на контролер што ја закопува истата состојба под поставување на барање и грижи за автентикација.
Тестирајте ги и границите: износот точно на лимитот, првиот момент кога политика станува ефективна, истечено право и спротивставени услови што може да произведат различни исходи. Интеграциските тестови потоа треба да потврдат дека перзистенцијата, трансакциите и API-мапирањето го задржуваат планираното однесување.
Кога правилата стануваат сложени, користете табели со примери наместо умешни помошници за тестирање. Идниот одржувач треба да може да ги види случаите што бизнисот ги сметал за важни без обратно инженерство на апстракција.
Менувајте ги правилата како продукциски софтвер
Промените на политиките може да имаат поголеми последици од промените во кодот, бидејќи веднаш ги менуваат одлуките во живо. Постапувајте соодветно. Валидирајте нови конфигурации пред активирање, зачувајте претходни верзии, внимателно дефинирајте датуми на стапување во сила и обезбедете патека за враќање. Ако промената на политика влијае врз постојни записи, изречно одлучете дали важи само за нови одлуки или активира контролирана повторна евалуација.
Набљудливоста е дел од архитектурата. Следете неуспешни премини, одбиени команди, недостигачка конфигурација и неочекувани комбинации на правила. Избегнувајте непотребно евидентирање чувствителни вредности, но задржете доволно контекст за да дијагностицирате одлука без да репродуцирате цел продукциски инцидент од меморија.
Трајната алтернатива на магичниот софтвер
Системите стекнуваат доверба кога нивните одлуки се предвидливи, проверливи и приспособливи. Тоа не ги прави помалку софистицирани. Ја прави нивната софистицираност корисна.
Почнете со лоцирање на правилата што моментално се скриени во контролери, барања и неформално знаење. Именувајте ги. Дајте им фокусирани места. Зачувајте ја променливата политика со верзионирање. Враќајте објаснувања, дизајнирајте команди безбедни при повторен обид и тестирајте ги граничните случаи со кои луѓето на крајот ќе се сретнат.
Софтверот што ги учи вашите бизнис-правила не е софтвер што погодува. Тој е софтвер чија структура му дозволува на бизнисот јасно да го подучува — и им дозволува на инженерите тоа подучување да го одржуваат точно додека бизнисот се развива.