Дизајн на бази на податоци: Создавање на основата за вистински интелигентни системи
Интелигентните системи не започнуваат со модел, контролна табла или импресивен API одговор. Тие започнуваат со податоците што на секој друг слој му даваат нешто доверливо врз кое може да расудува. Ако тие податоци се двосмислени, дуплирани, слабо поврзани или невозможно е безбедно да се развиваат, интелигенцијата изградена врз нив на крајот ќе стане скапо нагаѓање.
Затоа, дизајнот на базата на податоци не е прашање на складирање што се делегира во завршната фаза на имплементацијата. Тој е 'рбетот на однесувањето на системот. Тој дефинира што апликацијата може да знае, колку сигурно може да го знае тоа и колку безбедно може да се менува како што се менува бизнисот.
Моделирајте го доменот, а не првиот екран
Честа грешка во дизајнот е првиот кориснички интерфејс да ја диктира шемата. Формуларот има име на клиент, адреса и листа на нарачки, па една широка табела изгледа практично. Таа практичност исчезнува штом клиентите имаат повеќе адреси, нарачките имаат потреба од историја на статуси или повеќе услуги треба да се повикуваат на истиот клиент.
Наместо тоа, почнете со трајните концепти и односи во доменот. Прашајте што мора да остане точно без оглед на тоа како се организирани тековниот API, административниот панел или мобилната апликација. Во систем за нарачки, клиентите, нарачките, ставките на нарачката, производите, плаќањата и испораките се одделни концепти бидејќи се менуваат независно и имаат различни правила.
CREATE TABLE orders (
id BIGINT PRIMARY KEY,
customer_id BIGINT NOT NULL,
status VARCHAR(30) NOT NULL,
created_at TIMESTAMP NOT NULL,
FOREIGN KEY (customer_id) REFERENCES customers(id)
);
CREATE TABLE order_items (
id BIGINT PRIMARY KEY,
order_id BIGINT NOT NULL,
product_id BIGINT NOT NULL,
quantity INTEGER NOT NULL,
unit_price DECIMAL(12, 2) NOT NULL,
FOREIGN KEY (order_id) REFERENCES orders(id),
FOREIGN KEY (product_id) REFERENCES products(id)
);
Оваа структура пренесува повеќе од складирање. Таа експлицитно прави важна граница: ставката на нарачката ја бележи цената во моментот на купување, наместо да зависи од тековната цена на производот. Таа мала одлука ги штити фактурите, известувањето, повратите и проверливоста.
Поставете ги инваријантите таму каде што не можат да се игнорираат
Валидацијата во апликацијата е суштинска, но не е единственото место за критични деловни правила. Работниците во заднина, скриптите за увоз, задачите за одржување, идните услуги и рачните операции можат да заобиколат одреден PHP валидатор на барања. Базата на податоци треба да ги наметне фактите што е единствено позиционирана да ги заштити.
- Користете примарни клучеви за да воспоставите стабилен идентитет.
- Користете надворешни клучеви кога односот мора да се повикува на вистински запис.
- Користете
NOT NULLкога отсуството нема значење. - Користете уникатни ограничувања за вредности што не смеат да се повторуваат, како што е референца од надворешен давател.
- Користете ограничувања за проверка таму каде што се поддржани и соодветни за едноставни, трајни правила.
Ограничувањата не ја отстрануваат потребата од јасни грешки во апликацијата. Тие обезбедуваат последна линија на одбрана. Во добро дизајниран бекенд, апликацијата валидира рано за добро корисничко искуство, додека базата на податоци валидира на крај за точност.
Трансакциите исто така припаѓаат во овој разговор. Заробувањето на плаќање, резервирањето на залихи и преминот на состојбата на нарачката можеби треба да успеат како една целина или да не остават делумен резултат. Дефинирањето на границите на трансакцијата е одлука за дизајн, а не дополнителна мисла што се додава кога неуспесите во продукција ќе откријат неконзистентни записи.
Прво нормализирајте, денормализирајте со докази
Нормализацијата често се опишува како академска вежба, но нејзината практична цел е едноставна: чувајте еден факт еднаш, на местото каде што припаѓа. Ако истата адреса на е-пошта или опис на производ е копиран во многу редови, ажурирањата стануваат ненадежни и никој не може да биде сигурен која копија е авторитативна.
Тоа не значи дека секое читање мора да реконструира сложен објект преку многу спојувања. Патеките со интензивно читање понекогаш имаат корист од внимателно избрана денормализација: кеширан агрегат, документ за пребарување, табела за известување или зачувана вредност за приказ. Важната разлика е намерата. Прво нормализирајте го оперативниот извор на вистината; воведувајте дупликатни претставувања само кога постои измерена причина, јасен сопственик и сигурна стратегија за ажурирање.
На пример, чувањето на order_total може да биде разумно кога е изведено од ставките на нарачката и се одржува во рамките на истата трансакција. Неговото чување без дефинирано правило за повторно пресметување повикува на отстапување. Изведените податоци не се суштински опасни; необјаснетите изведени податоци се.
Дизајнирајте за барања, индекси и промени
Шемата е успешна само ако ги поддржува прашањата на кои системот мора да одговори. Пред да додадете индекси, идентификувајте ги вистинските обрасци на пристап: пронајдете клиент по е-пошта, наведете ги неодамнешните нарачки за клиент, изберете задачи во редица по статус или преземете настани во рамките на временски опсег.
Индексот треба да служи на барање, а не на нејасна желба базата на податоци да биде побрза. Секој индекс додава работа при запишување и трошок за складирање. Сложените индекси исто така имаат импликации за редоследот: индекс на (customer_id, created_at) е добро приспособен за преземање на неодамнешните нарачки на клиент, но не е еквивалентен на индекс на (created_at, customer_id).
Користете планови за барања за да ги потврдите претпоставките во вистинскиот механизам на базата на податоци и обликот на множеството податоци. Индекс што изгледа правилно во миграција сепак може да не се користи поради неселективен услов, израз околу колона или барање што бара податоци во некомпатибилен редослед.
Направете ги миграциите досадни и во духот повратни
Продукциските шеми се развиваат под оптоварување и покрај повеќе распоредени верзии на апликацијата. Безбедните миграции ја земаат предвид таа реалност. Додавањето nullable колона обично е полесно за воведување отколку веднаш додавање задолжителна. Честа низа е да се додаде колоната, да се распореди код што ги запишува и старите и новите претставувања, да се пополнат старите податоци во контролирани серии, да се потврди резултатот, а потоа да се наметне конечното ограничување откако ќе исчезнат постарите инстанци на апликацијата.
За големи табели, разгледајте ги и однесувањето при заклучување, времетраењето на трансакцијата, репликацијата и опциите за враќање назад. Технички валидна миграција сепак може да биде оперативно небезбедна ако предолго блокира зафатена табела.
Користете API-ја за да ги зачувате границите на базата на податоци
API треба да изразува доменски операции наместо директно да изложува табели. Крајна точка што прифаќа произволни колони од клиентот ги врзува надворешните потрошувачи за внатрешните одлуки за складирање. Исто така, го отежнува расудувањето за авторизацијата и валидацијата.
Наместо записот за нарачка да го третирате како изменлива торба со полиња, моделирајте значајни дејства како создавање нарачка, додавање ставка, откажување нарачка или потврдување плаќање. Бекендот потоа може да примени авторизација, да наметне премини на состојби и да координира трансакции без да бара од клиентите да ја разберат шемата.
Ова е уште поважно во системи ориентирани кон услуги. Базата на податоци не е заеднички договор за интеграција. Услугите може да објавуваат настани или да изложуваат API-ја, но дозволувањето на неколку системи слободно да запишуваат во истите табели ги претвора внатрешните детали на имплементацијата во кревок граф на зависности.
Оперативните детали се дел од дизајнот
На сигурна шема ѝ се потребни сигурни операции околу неа: резервни копии што може да се обноват, процедури за миграција што се тестирани, следење на бавни барања и неуспешни задачи и правила за задржување на податоци што не треба да живеат засекогаш. Docker може да го направи локалното поставување на базата на податоци репродуцибилно, но контејнерот не го заменува планирањето на трајноста, управувањето со акредитиви или тестирањето на опоравувањето.
Исто така, намерно одлучете како се претставени времето, парите, идентификаторите и бришењето. Чувајте ги паричните износи во типови со фиксна прецизност или во помали единици според правилата на системот. Чувајте ги временските ознаки доследно и зачувајте доволно контекст за деловното значење. Претпочитајте експлицитни политики за меко бришење или архивирање кога записите мора да останат референцијално видливи, наместо тивко да бришете податоци од кои сè уште зависат други записи.
Шемата е долгорочна одлука за производот
Добриот дизајн на база на податоци не се состои во создавање на најсложениот дијаграм или во следење на чистотата по секоја цена. Се состои во тоа правилното однесување да биде лесно, неправилното однесување тешко, а идните промени разбирливи. Промислената шема им дава на PHP услугите појасни одговорности, на API-јата побезбедни договори и на тимовите заеднички јазик за доменот.
Кога се очекува системите да станат поинтелигентни, нивните податоци прво мора да станат посигурни. Изградете ја таа сигурна основа со експлицитни односи, правила што може да се наметнат, намерни патеки за барања и планови за еволуција што ја почитуваат продукциската реалност. Интелигенцијата над неа ќе има нешто цврсто врз кое може да стои.