Анотація
Цей стек дає Naly типізовану площину керування публікацією: Drizzle ORM зберігає записи статей, прогнозів, джерел, публікацій у соцмережах, винагород і cron як реляційні сутності в Neon Postgres, замість розпорошеного стану виконання. Оскільки кожен крок робочого процесу зберігається як явні рядки та enum-стани, Naly може безпечно повторно запускати cron-пайплайни, відновлюватися після часткових відмов і тримати дані редакторського UI синхронними з API-видимим станом. Станом на 26 червня 2026 року це є операційно стійким контрактом проєкту для публікації прогнозів.
Де це стоїть у Naly
У поточному стеці поведінка публікації спільна для кількох шляхів Next.js і cron-завдань, усі з яких посилаються на той самий контракт бази даних. Набір пакетів, що вже використовується,—next@16.0.7, react@19.2.1, drizzle-orm@^0.44.7, @neondatabase/serverless@^1.0.2, tsx@^4.21.0, typescript@^5.9.3—відповідає ORM-шару типу serverful, який працює всередині серверних маршрутів/дій та запланованих воркерів.
Naly зберігає ці домени як повноцінні таблиці:
- записи статей і прогнозів
- URL джерел і знімки походження
- публікації в соцмережах + метадані розповсюдження
- нагородні бали, метадані калібрування та поля аудиту
- метадані cron-запусків і контрольні точки публікації
Цінність не лише в персистенції; це спільна семантика. Кожен рендерер, воркер і API-дія читає одну й ту саму модель стану публікації та може координуватися без прихованих кроспроцесних прапорців.
Технічний механізм
Drizzle надає шлях із підходом schema-first для такого типу спільної семантики. Посібники Drizzle показують канонічний потік: визначте схему в TypeScript, налаштуйте Neon DATABASE_URL, drizzleі використовуйте згенеровані типи запитів для вставок/читання/оновлень (оглядові документи, керівництво Neon).
Мінімальний шаблон ініціалізації драйвера в стилі Naly виглядає так:
import { drizzle } from 'drizzle-orm/neon-http';
import { pgTable, text, timestamp, pgEnum, integer } from 'drizzle-orm/pg-core';
export const publicationState = pgEnum('publication_state', [
'queued', 'draft_ready', 'published', 'failed',
]);
export const publications = pgTable('publications', {
id: text('id').primaryKey(),
slug: text('slug').notNull().unique(),
state: publicationState('state').notNull(),
stateVersion: integer('state_version').notNull().default(1),
stateChangedAt: timestamp('state_changed_at').notNull().defaultNow(),
});
const db = drizzle(process.env.DATABASE_URL!);
Це відповідає документації Drizzle, яка підтримує drizzle-orm/neon-http та drizzle-orm/neon-serverless як варіанти транспорту для Neon, з HTTP для одноразових навантажень і поведінкою на кшталт WebSocket для інтерактивної транзакційної роботи за потреби. Drizzle pg-core визначення також дозволяють типізоване виведення ($inferInsert, $inferSelect) тож корисні навантаження публікації валідуються TypeScript на етапі компіляції, зберігаючи водночас гнучкий JSON для некритичних метаданих.
Для Naly критичний архітектурний патерн такий:
- визначте переходи станів (
queued -> drafted -> approved -> published -> archived) як явні рядки, - тримайте логіку переходів в одному модулі лише на сервері,
- логувати кожну мутацію з ідемпотентними ключами (id завдання + хеш стану),
- виконуйте критичні переходи всередині транзакцій бази даних, де важлива атомарність,
- генеруйте незмінні артефакти (наприклад, blob URL, соціальні копії, снапшоти схем) лише після просування стану.
Ефект подібний до невеликої скінченної машини станів, збереженої в Postgres із суворими контрактами схеми, а не неявною поведінкою всередині cron-воркерів.
Що стверджує література
Основна документація розглядає це як практичний дизайн-вибір для serverless-систем:
- Drizzle позиціонує себе як SQL-подібний за конструкцією та готовий до serverless, зменшуючи накладні витрати ORM-абстракції для команд, яким потрібна пряма семантика SQL, одночасно зберігаючи типобезпеку (Огляд Drizzle).
- Посібники Drizzle/Neon прямо підтримують нативні комбінації драйверів Neon HTTP/WebSocket і типізоване моделювання schema-first, включно з
@neondatabase/serverlessінтеграцією та прикладами виведення типів (Drizzle with Neon, Get Started with Drizzle + Neon). - Матриця з'єднань Drizzle демонструє явне розділення драйвера виконання в runtime, тож команди можуть підбирати режим виконання під навантаження та обмеження рантайму (Документація з’єднання з базою даних).
- Документація драйвера Neon і рекомендації для edge наголошують, що доступ до serverless/Postgres часто реалізується через HTTP або websocket-проксування, саме тому рішення про виконання на edge мають бути явними для кожного типу навантаження (Neon serverless driver, How to use Postgres at the Edge).
Щодо схеми, дослідження еволюції схеми показує, чому важливі явні міграції й моделі станів. Tesseract доводить, що еволюцію схеми можна розглядати як транзакційну операцію першого рівня, а надійні системи мають мінімізувати час простою за дизайном (online schema evolution). EvoSchema показує, що зміни схеми — особливо рівневі зміни таблиць — можуть дестабілізувати поведінку наступних компонентів, що є серйозним попередженням проти випадкових ad-hoc доповнень до таблиць публікації/стану (EvoSchema).
Компроміси дизайну
Вибір Naly фактично є компромісом між строгою валідацією та тертям. Суворо типізовані схеми та явні enum-стани покращують спостережуваність і надійність, але також збільшують початкові витрати на моделювання й вимагають дисципліни міграції. Крива компромісу сприятлива, коли логіка публікації стає спільною для cron-воркерів, AI-конвеєрів і рендерерів публічних сторінок.
- Вибір драйвера:
neon-httpпростіший і часто швидший для одноразових операцій;neon-serverlessкращий, коли потрібні інтерактивні сесії. - Дизайн schema-first: безпека під час компіляції зменшує помилки виконання, але зміни схеми вимагають планування міграцій і можуть проявлятися як заблоковані деплои, якщо тести не покривають переходи станів.
- Портативність виконання: edge-дружня модель драйвера Neon розширює варіанти розгортання, але режим транспорту впливає на поведінку сесії, профіль затримки та шлях автентифікації/TLS.
- Точність типів проти операційної зручності: явні enum-записи запобігають недійсним станам, але можуть сповільнити нічні термінові виправлення, якщо скрипти міграції не попередньо схвалені.
Сценарії відмов
- Неправильний драйвер для навантаження: використання одноразових HTTP-запитів для багатокрокових переходів стану може втрачати атомарні гарантії й створювати часткові стани публікації.
- Розбіжність схеми між воркерами та деплоєм: якщо
publication_stateзначення змінюються без узгоджених міграцій, старий cron-код може записувати недійсні стани. - Некеровані ретраї: перезапуски cron можуть дублювати соціальні записи, якщо контрольні точки не є ідемпотентними та унікальними за run-id.
- Відставання міграції: планові завдання, що працюють із застарілими снапшотами схеми, можуть падати, особливо під час rolling deploy.
- Edge cold-start + накладні витрати автентифікації: на обмежених edge-рівнях повторна ініціалізація з’єднання може збільшувати затримку і давати хибні таймаути, якщо не налаштовані бюджети timeout і розподіл задач.
Щодо цієї теми, аналіз відмов має розглядати стан публікації як артефакт коректності, а не деталь реалізації. Кожен перехід стану має бути безпечним для відтворення.
Нотатки з реалізації
- Тримайте схеми централізованими та версіонованими; генеруйте артефакти міграції з одного джерела правди.
- Відділяйте змінювані доменні таблиці від незмінних таблиць артефактів.
- Моделюйте переходи публікації як межу транзакції та забезпечуйте інваріанти (наприклад, без
queued -> publishedбезпосереднього стрибка). - Зберігайте метадані планувальника (
last_run,next_run_at,error_count) у власній таблиці для алертингу та аудиту. - Віддавайте перевагу серверно-тільки модулям для ініціалізації БД (
DATABASE_URLз.env.localу cron/runtime-середовищах). - Використовуйте структуровані запити замість сирого SQL для більшості операцій і залишайте сирий SQL для seed міграцій або звітності.
- Сприймайте
stateVersionі журнали подій як містки сумісності, коли схема еволюціонує.
Джерела
- Drizzle ORM - Drizzle with Neon Postgres
- Drizzle ORM Overview
- Drizzle ORM - Database connection
- Get Started with Drizzle and Neon
- Neon serverless driver (Neon Docs)
- Neon blog: How to use Postgres at the Edge
- Online Schema Evolution is (Almost) Free for Snapshot Databases
- EvoSchema: Towards Text-to-SQL Robustness Against Schema Evolution