В блог
4 марта 2026 Sogerien anti-mvcphpuniversal-engineархитектура

Почему я выкинул MVC

MVC учит резать приложение на контроллеры, модели и вью, а сверху навешивать репозитории, сервисы и DI-контейнер. Для дашборда или лендинга это слои-переходники: контроллер, который дёргает сервис, который дёргает репозиторий, который дёргает ORM. Пять файлов, чтобы отдать одну страницу.

Sogerien - это движок, а не MVC-фреймворк. Правило одно: никаких лишних сущностей, самое короткое решение с минимумом кода и максимумом переиспользования. Всё, что осталось от привычной обвязки:

  • Тонкий index.php в корне сайта - фронт-контроллер на десяток строк.
  • Движок в подпапке sogerien/: Sogerien.php - bootstrap плюс автозагрузка из classes/ и page/.
  • Routes - таблица точных маршрутов, без регэкспов и аннотаций.
  • page/*.php - одноразовый код страницы.
  • classes/ - только переиспользуемое.

Весь фронт-контроллер

Ни базового контроллера, ни маршрутного компилятора. Регистрируешь шаблоны, отдаёшь, помечаешь запрос обработанным:

<?php
declare(strict_types=1);
require __DIR__ . '/sogerien/Sogerien.php';

$routes = Sogerien::Routes();
$routes->add_template('/',         '/page/home.php');
$routes->add_template('features',  '/page/features.php');
$routes->add_template('blog',      '/page/blog.php');

Sogerien::Routes()->template();
Sogerien::markDone();

Куда делись M, V и C

Модель - это universal table плюс DbController: данные лежат в одной таблице, доступ идёт через один метод. Вью - это класс Template плюс скин, они несут <head>, шрифты и порядок ассетов. Контроллер - это сам файл page/x.php: он и есть точка, где сходятся данные и разметка. Отдельный слой для каждого из них не нужен - каждый и так существует ровно один раз.

Правило миграции кода

Код рождается в page/ как одноразовый. Как только он реально понадобился второй странице или второму проекту - тогда он выносится в classes/ настоящим классом. Не заранее "на всякий случай", а по факту второго использования. Абстракция, у которой один потребитель, - это мёртвый вес.

Тридцать сущностей без тридцати классов

// Тридцать отделов аналитики - это не тридцать классов и не тридцать файлов.
// Роут точечный (isset($t[$url]), без вайлдкардов), поэтому сущности
// разворачиваются циклом из конфига на ОДИН обработчик:
foreach (Sogerien::Config()->get('apis.telegram.groups') as $key => $_) {
    $routes->add_template('admin/analytics/binotel/' . $key, '/page/binotel_dept.php');
}
// Страница сама достаёт ключ из последнего сегмента URL и валидирует его
// по тому же конфигу. Добавить отдел = дописать строку в конфиг, код не трогается.

Итог: чтобы добавить раздел, я пишу одну строку маршрута, а не генерирую контроллер, модель, репозиторий, DTO и три интерфейса. Меньше кода - меньше поверхности для багов, и вся система целиком помещается в голове.