Внутри DenoX: маршрутизация файлов, MVC Slices и контракт AGENTS.md в Deno
Как фреймворк DenoX объединяет Hono, маршрутизацию на основе файлов, фичные слайсы, средство глобальной безопасности и рабочий процесс AGENTS.md, ориентированный на спецификации, для агентов по программированию с ИИ.
Подключение сервера редко является ключевой частью проекта с бэкендом; важнее — выпуск функций. Rails, Laravel и Next.js привлекли разработчиков тем, что брали на себя решение всех технических вопросов, и Deno с его по умолчанию безопасными правами доступа, встроенным TypeScript и инструментарием также является отличным кандидатом на аналогичную роль. DenoX — это open-source фреймворк полного стека для Deno, созданный на основе Hono, который стремится стать именно таким же определяющим слоем. Его решения типовых структурных вопросов и способ их фиксации для ИИ-агентов, занимающихся программированием, представляют собой шаблоны, которые можно использовать в любом бэкенде на TypeScript.
Пробел, который заполняет определяющий слой
Deno 2 принес совместимость с npm, реестр JSR, зрелую стандартную библиотеку и единый бинарник, способный выполнять проверку кода, форматирование, тестирование, компиляцию и сборку (см. как Deno 2.x решил проблемы совместимости с Node и усталость от инструментов для более подробной информации). То, чего не может предоставить простой движок выполнения, — это единое понимание вопросов, по которым спорят все команды: где должна находиться бизнес-логика, как сообщаются ошибки, кто проверяет конфигурацию и где реализовано ограничение скорости запросов.
DenoX отвечает на эти вопросы с помощью одного принципа: конвенции важнее настроек, что проверяется с помощью инструментов. Документированные конвенции могут меняться; те, что проверяются в CI, остаются неизменными.
Маршрутизация на основе файлов с генерируемой и сохраняемой таблицей
Маршруты берутся из файловой системы. Добавление файла в каталог pages добавляет соответствующий URL, причем элементы в квадратных скобках становятся параметрами:
src/frontend/pages/
├── index.ts → /
├── about/main.ts → /about
├── users/main.ts → /users
└── posts/[id].ts → /posts/:id
Обнаружение маршрутов не происходит во время выполнения. Запуск команды deno task routes просматривает структуру маршрутов и генерирует статическую, детерминированную таблицу маршрутов. Два фактора делают этот подход надежным:
- Статические маршруты всегда регистрируются до динамических, поэтому
/users/newникогда не может быть поглощен маршрутом/users/:id. В роутерах с правилом первого совпадения, таких как Hono, порядок регистрации определяет поведение, и его автоматическое генерирование устраняет классические причины скрытых ошибок. - Генерированный файл сохраняется в репозитории, и процесс интеграционного тестирования сбрасывается, если он устарел.
Страницы — это обычные функции
Страница представляет собой обычный модуль TypeScript. Он импортирует тип Context из библиотеки Hono и инструмент для экранирования HTML:
import type { Context } from "hono";
import { escapeHtml } from "@/shared/html.ts";
Затем он экспортирует объект config, в котором указывается шаблон разметки и функция-значение, возвращающая строку HTML:
export const config = { layout: "default" } as const;export default function homePage(c: Context): string {
const name = escapeHtml(c.req.query("name") ?? "world");
return `<h1>Hello, ${name}!</h1>`;
}
Следует обратить внимание на функцию escapeHtml. Параметр запроса name контролируется пользователем, и прямое вставление его в маркировку стало бы классической уязвимостью типа reflected XSS. В DenoX экранирование ненадежных данных — это не рекомендация, а правило, закрепленное в техническом контракте проекта. Поскольку движок шаблонов не выполняет автоматического экранирования и возвращает сырые строки, любое вставление внешних данных должно проходить через эту вспомогательную функцию.
Фрагменты функционала с фиксированной структурой
С точки зрения API каждый фрагмент функционала представляет собой самодостаточный блок с одинаковым набором файлов, каждый из которых выполняет одну задачу:
src/api/users/
├── user.model.ts entities only
├── user.dto.ts unknown → typed DTO (boundary validation)
├── user.repository.ts interface + default implementation
├── user.service.ts business rules only — no HTTP, no HTML
├── user.controller.ts HTTP adapter only
└── user.routes.ts composition root (constructor injection)
Модуль DTO преобразует входные данные типа unknown в объект с определенным типом на границе, поэтому более низкие уровни стека не имеют дела с необработанными телами запросов. Сервисы содержат только бизнес-правила и не знают ничего об HTTP или HTML. Контроллеры представляют собой простые адаптеры HTTP. Файл маршрутов является центром композиции, где зависимости подключаются с помощью внедрения через конструктор.
Сервисы зависят от интерфейсов хранилищ, а не от конкретных классов. Поэтому замена внутреннего хранилища на Postgres или Deno KV означает необходимость изменения по одному файлу на каждую функциональность.
Ошибки в виде исключений с указанием типа
Бизнес-правила сигнализируют об ошибках путем выбрасывания исключений с указанием типа. Метод сервиса ниже отказывается создавать второго пользователя с уже существующей почтовой адресной строкой:
async create(dto: CreateUserDto): Promise<User> {
const existing = await this.repository.findByEmail(dto.email);
if (existing !== null) {
throw new ConflictException(`Email "${dto.email}" is already registered`);
}
return await this.repository.create(dto);
}
Сервис не выбирает код состояния и не форматирует ответ. Один централизованный обработчик ошибок преобразует каждый тип исключения в единообразный JSON-объект и обеспечивает, чтобы трейсы стека никогда не доходили до клиентов. Следует отметить, что возврат электронного письма позволяет проводить перечисление учетных записей, чего следует избегать на публичных конечных точках.
Безопасность реализуется один раз и применяется везде
Механизмы защиты, необходимые во многих частях системы, находятся в глобальном промежуточном слое, а не в каждой отдельной функции: политика безопасности контента, усиленные заголовки ответа, правила CORS, проверки CSRF на основе источника запроса, ограничения по частоте запросов с учетом IP-адреса клиента, лимиты размера тела запроса, таймауты и маскировка внутренних ошибок. Функции используют эти механизмы, вместо того чтобы реализовывать их заново.
Конфигурация обрабатывается аналогичным образом. Каждая переменная среды анализируется, проверяется и фиксируется при запуске процесса; приложение откажется загружаться, если чего-то не хватает или данные имеют неверный формат. В производственной среде значение CORS_ORIGIN=* полностью отклоняется. Быстрое обнаружение ошибок при запуске лучше, чем отладка наполовину настроенного сервиса в производстве.
Три уровня тестирования за одним командным приказом
Система тестирования выходит за рамки простых проверок:
- Тесты модулей охватывают чистую логику с использованием имитаций для записи вызовов и вообще не требуют разрешений Deno.
- Интеграционные тесты проверяют полностью настроенное приложение с помощью
app.request(), убеждаясь в корректности кодов состояния, структуры ответа и даже заголовков безопасности, при этом не открывая сокетов.
Deno.serve на временном порту и отправляют к нему запросы с использованием реальных функций fetch, включая запрос, преднамеренно вызывающий ограничение по частоте запросов, чтобы подтвердить возврат кода 429.Полная система контроля качества, охватывающая форматирование, проверку синтаксиса, анализ устаревших таблиц маршрутов, строгую проверку типов и все уровни тестирования, запускается с помощью команды deno task ci, причем пайплайн GitHub Actions выполняет именно эту последовательность действий.
Одна команда развертывания, без обработки учетных данных
В репозитории содержатся манифесты для сервисов Fly.io, Railway, Render, Docker, а также усиленный модуль systemd для VPS, плюс первоклассная поддержка Deno Deploy. Одна команда может указать цели, выполнить пробный запуск или фактически осуществить развертывание:
deno task deploy # list targets
deno task deploy fly # dry run: steps + env reminders
deno task deploy fly --run # execute (auth delegated to the platform CLI)
Инструмент развертывания намеренно не обрабатывает учетные данные. Он проверяет предварительные условия, отображает план развертывания вместе с напоминаниями о необходимых переменных окружения и оставляет вопросы аутентификации на усмотрение официальных CLI каждой платформы. Секретные данные совершенно не присутствуют в фреймворке.
AGENTS.md как обязательный технический контракт
Самой выдающейся особенностью DenoX является файл AGENTS.md, расположенный в корне репозитория, который служит авторитетным контрактом как для людей-разработчиков, так и для ИИ-агентов. В нем указывается технологический стек, определяется каноническая структура директорий и перечисляются общие компоненты, которые ни в коем случае нельзя пересоздавать: логгер, иерархия исключений, структура ответа и модуль конфигурации.
Кроме того, в этом файле описан процесс разработки, основанный на спецификациях:
- Спецификация вроде
specs/feature.mdсоздаётся с параметромstatus: draft. - Человек проверяет её и меняет статус на
status: approved. - Только после одобрения начинается работа над архитектурой, планом, реализацией, тестированием и документацией.
Агентам прямо указывается остановиться после написания спецификации и дождаться её одобрения человеком, поэтому агент не может сам одобрить свой план и затем переписать часть кодовой базы. Полный цикл работы с управлением пользователями показывает агентам эту схему, а системы CI механически соблюдают установленные правила, прерывая процесс сборки при ручном изменении генерированных файлов.
По мере того как агенты пишут всё больше кода, конвенции становятся важными лишь в той мере, в которой их можно автоматически проверять; версионирование контракта рядом с кодом в сочетании с инструментами CI превращает рекомендации в строгие ограничения. Чтобы узнать больше о файлах инструкций для ассистентов, посмотрите навык AGENTS.md от Vercel по лучшим практикам React.
Запуск локально
Клонируйте репозиторий, сгенерируйте файл с настройками окружения по примеру и запустите сервер разработки:
git clone https://github.com/olavomello/denox.git
cd denox
cp .env.example .env
deno task dev
Затем откройте http://localhost:8000, вызовите /api/users, намеренно отправьте некорректные данные и проверьте, что сообщение об ошибке останется чистым и не будет содержать трассировок стека. Также доступна рабочая версия. Проект распространяется под лицензией MIT; в его планах — адаптеры Deno KV и Postgres, автоматическая регистрация структур, специальная команда линейной оболочки, модуль аутентификации и генерация OpenAPI, причем каждый из этих элементов планируется создаваться с использованием одинакового подхода, основанного на спецификациях. Перед использованием ознакомьтесь с текущим состоянием репозитория.
Основные выводы
- Генерируйте таблицы маршрутов во время сборки, регистрируйте статические маршруты перед динамическими, сохраняйте результаты и пусть система CI отклоняет устаревшие файлы.
- Для каждой функции определите фиксированную структуру: DTO для границ данных, репозитории на основе интерфейсов, сервисы без использования HTTP и легкие контроллеры.
AGENTS.md, требующий человеческого утверждения спецификаций, и применяйте его правила в системах CI, чтобы они действовали как для агентов, так и для людей.