# Тимур Турдыев - Backend / Full-stack разработчик > Backend / full-stack разработчик из Москвы, в разработке с 2014 года. Laravel, Go, Vue, PostgreSQL. Продуктовая разработка в Waviot (IoT-платформа NB-Fi), собственная платформа Mercurio для интернет-магазинов, пять open-source пакетов. В разработке с 2014 года, самоучка. До кода был свой интернет-магазин и несколько лет SEO - в разработку пришёл, уже понимая, что нужно бизнесу и где именно он теряет деньги. Первой платформой стал OpenCart, несколько лет фриланса - форум OpenCart, затем сарафанное радио. Сейчас основное время - продуктовая разработка в Waviot (IoT-платформа на протоколе NB-Fi): несколько поколений кода в одной компании, от самописного PHP-MVC и Laminas до Laravel, Go и TypeScript. Параллельно строю Mercurio - собственную платформу для интернет-магазинов (админка, CMS и витрина), а куски, полезные сами по себе, выношу в open-source. Локация: Москва, Россия. В разработке с 2014 года. Контакты: borodatimur@gmail.com · https://t.me/borodatimur · https://github.com/TimurTurdyev ## Сильные стороны - Жить с несколькими поколениями кода в одном проекте: legacy без фреймворка, Yii2 и Laminas, современный TypeScript и Go-сервисы. - Миграции и апгрейды без downtime: PHP 5.6 -> 8.x, Zend Framework -> Laminas, обновления Yii, переключение поисковых движков под задачу. - Интеграции: CRM, облачные АТС, службы доставки, сервисы данных, IoT-устройства, собственные шлюзы поверх RabbitMQ/AMQP. - Инженерное суждение важнее стека: иногда правильный ответ - shell-скрипт или SQL-вьюха, а не новый сервис. - Настройка серверов не живёт в моей голове и в истории ssh: всё описано в Ansible, секреты в Vault. Сервер можно пересобрать с нуля, и он получится таким же. Релизы - Deployer. - Код должен работать у клиента без меня: запускается, обновляется и чинится в понятных местах. ## Навыки - **languages**: PHP 8+, JavaScript, TypeScript, Go, Python, Java, Bash - **backend**: Laravel, MoonShine, Yii2, Laminas, OpenCart, самописный PHP-MVC, FastAPI, aiohttp - **frontend**: Vue, Alpine.js, Blade, jQuery, Tailwind, Bootstrap, Vite, Wails, Electron - **db**: MySQL, MariaDB, PostgreSQL, SQLite, MongoDB, Redis, SphinxSearch, Manticore, Meilisearch - **queues**: RabbitMQ, AMQP - **infra**: Docker, Ansible, Deployer, Linux, Nginx, Git, systemd - **integr**: Mango Office, Megaplan, Dadata, СДЭК, ВКонтакте API, OpenCart marketplace API ## Стек боевых систем | Слой | Стек | Почему так | | --- | --- | --- | | Кабинет клиентов lk.waviot.ru | Laminas · PHP 8 · MongoDB ODM | Кабинет для интеграторов, партнёры ставят его у себя deb-пакетом под своим брендом. В 2022 перевёл с Zend Framework 2 и PHP 5.6 на Laminas и PHP 8, дальше развиваю на месте | | Новый кабинет | Go · Connect/protobuf · React | Слоистый сервис рядом со старым, старая БД кабинета переезжает по частям | | Телеметрия b.waviot.ru | Laminas (ZF2) · Doctrine · AMQP | Центральное приложение: не переписывается разом, его по кускам заменяет HES | | HES | PHP 8 · свой MVC · JWT | Замена бэкенда телеметрии на той же базе, JWT вместо сессий | | Авторизация auth.waviot.ru | PHP 8 · свой MVC · JWT | Учётки, компании и лицензии для всех кабинетов; один JWT для соседних сервисов | | Лицензии lic.waviot.ru | Yii2 · RBAC · JWT + Go | Работает стабильно, развиваю без переписывания; файлы лицензий отдаёт Go-сервис из deb-пакета | | Учёт производства | Laravel 12 · Inertia · React · TypeScript | Моя система с нуля, на бою с июня 2026: платы по отделам, сканер, брак, сдельные отчёты; приборы и гарантия на подходе | | Сервер производства (legacy) | Python · aiohttp | Работает, пока его не заменил новый: дорабатываю без простоя | | Вебхуки телеметрии | Python · RabbitMQ | Телеметрия из очереди -> HTTP подписчикам; сбой БД или AMQP переживается ретраями | | CLI эксплуатации | Laravel Zero · PHAR | Один файл на сервере: перенос деревьев объектов, каналы модемов, калибровка | | Desktop-утилиты | Go · Wails · Vue 3 | Кроссплатформенный бинарник без Electron-веса | | Свои и клиентские серверы | Ansible · Deployer · Ubuntu LTS | Сервер описан кодом, а не историей ssh-сессий: его можно пересобрать с нуля, и после меня в нём разберётся другой человек | | Mercurio (свой проект) | Laravel 13 · Inertia · React · PostgreSQL | Сырьё поставщиков неизменяемо, человек правит решения, каталог - пересобираемая проекция; ядро пакетом, у клиента свой сервер | | Витрины Mercurio | React SSR · Manticore · imgproxy | Своя тема и своё приложение у каждого клиента, поиск и фильтры на Manticore, картинки отдаёт imgproxy | ## Open-source пакеты - [mercurioplatform/tables](https://github.com/mercurioplatform/tables) (packagist, PHP · Laravel 13 · Bootstrap 5 · jQuery): Декларативный Resource-класс -> admin-страница одной строкой роута: поиск, сортировка, фильтры, saved views, bulk/row actions, экспорт, prefs, history + undo. Пять сознательных точек расширения - дальше vendor:publish. Страница рендерится сервером, AJAX подгружает только корень таблицы. - [timurturdyev/simple-settings](https://github.com/TimurTurdyev/Simple-Settings) (packagist, PHP 8.2+ · Laravel 12/13): Группы, кэш, автоприведение типов, пакетная запись одним upsert: невалиден хоть один ключ - не сохранится ни один. Фасад, Artisan-команды, обход кэша через fresh. - [simple-db-settings](https://github.com/TimurTurdyev/simple-settings-py) (pypi, Python 3.11+ · SQLAlchemy 2 · pydantic): Порт на SQLAlchemy 2, совместимый с PHP-версией по таблице: сервис на Laravel и сервис на Python читают и пишут одни и те же настройки. TTL-кэш, инвалидация после коммита, аудит изменений с причинителем, работа на внешнем соединении. - [timurturdyev/simple-cart](https://github.com/TimurTurdyev/simple-cart) (packagist, PHP 8.3+ · Laravel 12/13): Каждый список - один примитив с политикой из конфига. Деньги считает только корзина, через конвейер классов-корректировок: без float, без магических строк, без обязательных миграций. Написан на замену darryldecode/laravelshoppingcart. - [timurturdyev/simple-seo](https://github.com/TimurTurdyev/simple-seo) (packagist, PHP 8.2+ · Laravel 11/12): Ядро без зависимостей, слой Laravel в том же пакете. Замена artesaos/seotools: дефолты подставляются на рендере и не прилипают к странице, JSON-LD собирается коллекцией в @graph вместо одного перетираемого блока. ## Проекты - [Mercurio](/cases/mercurio) (Laravel 13, Inertia, React, TypeScript, PostgreSQL, Manticore): Своя платформа для интернет-магазинов: сырьё прайсов поставщиков хранится как есть, человек правит только решения, каталог собирается из них детерминированно. Приём прайсов профилями без кода под поставщика, поиск и фильтры на Manticore, обмен с 1С. У каждого клиента своя витрина, своя база и свой сервер, общее ядро - пакетом. - [tim](https://github.com/TimurTurdyev/tim) (Go, cobra, macOS): Утилита для терминала, которой пользуюсь каждый день: переход в проект с поиском, ssh мимо VPN, туннели через свой сервер, локальный запуск проектов. Одна команда проверяет все проекты разом: незакоммиченное, ответ сайта, сроки сертификатов и доменов, настройку почты. - [Экран продаж lpack](https://github.com/TimurTurdyev/dashboard-lpack) (Laravel 12, jQuery, Chart.js): Показатели отдела продаж оптового магазина упаковки на мониторе в офисе: виджеты из 1С, графики, вкладки сменяются по кругу. - [Laravel-Mango-Office](https://github.com/TimurTurdyev/Laravel-Mango-Office) (PHP, Laravel): Небольшой пакет для API облачной АТС Mango Office: сотрудники, статистика звонков, записи разговоров. Вырос из экрана продаж для Prime Wood. - [Export-Import OpenCart + Vue](https://github.com/TimurTurdyev/Export-Import-Opencart-Vue) (PHP, Vue, OpenCart): Импорт и экспорт товаров OpenCart через Vue-интерфейс без перезагрузки страницы, форматы CSV и XLS. - [opencart-dadata](https://github.com/TimurTurdyev/opencart-dadata) (Vue, PHP, OpenCart): Подсказки адресов и ФИО от Dadata при оформлении заказа в OpenCart: быстрее ввод, меньше ошибок в адресах. - [my-tools-parse-message-waviot](https://github.com/TimurTurdyev/my-tools-parse-message-waviot) (Go, Wails, Vue 3, Tailwind 4): Десктопная утилита для инженеров: разбирает ответы WAVIOT API в карточки по модемам и obis-кодам. Один бинарник без Electron-веса. - [Экран продаж Prime Wood](/cases/prime-wood-sales-screen) (PHP, Megaplan API, Mango Office API): Монитор отдела продаж: сделки из Megaplan и звонки из Mango Office, план месяца и доля закрытых сделок по менеджерам. PHP и cron, без фреймворка. - [opencart-image-remover](https://github.com/TimurTurdyev/opencart-image-remover) (PHP, OpenCart): Чистка каталога изображений OpenCart от файлов, на которые больше не ссылается ни один товар. - [vpn-help](https://github.com/TimurTurdyev/vpn-help) (Bash, macOS): Утилиты macOS против конфликта OpenVPN и AmneziaVPN: диагностика, зачистка повисших utun-интерфейсов, сброс DNS. - [timcode.ru](https://timcode.ru) (VitePress, Vue, TypeScript): Этот сайт: резюме, кейсы и статьи, справочник по паттернам на четырёх языках с тестами, curl-режим и машиночитаемые форматы. Собирается статикой. ## Статьи - [Очередь перед чужим API](https://timcode.ru/articles/queue-before-foreign-api): Почему между вебхуком и внешней CRM всегда стоит очередь, и какие три вещи делают её надёжной. Стек: Laravel, RabbitMQ, интеграции. Дата: 2026-10-11. ## Кейсы - [Майятрица: калькулятор, админка и сайт, три кодовые базы за два года](https://timcode.ru/cases/mayatriza): Долгий клиент: эзотерический сайт с расчётом «майянской матрицы» по дате рождения. С 2024 года написал с нуля три кодовые базы: сайт на Laravel 11, отдельную админку на MoonShine и нынешний сайт на Laravel 13 с новым дизайном, который прорабатывал вместе с заказчицей, и своей админкой на Inertia, React и shadcn. Расчёты сверены с документами заказчицы, клипы перекодирует ffmpeg. Стек: Laravel, PHP, MySQL, Tailwind, Inertia, React, shadcn, MoonShine, ffmpeg. Дата: 2026. - [Магазин упаковки: задачи вокруг старого OpenCart, не ломая его](https://timcode.ru/cases/lpack): Оптово-розничный магазин упаковки в Петербурге с учётом в 1С. За год вокруг боевого OpenCart 2.3 появились экран показателей отдела продаж, видеораздел с фоновой конвертацией, портал аттестации сотрудников, отчёт по трафику и демо новой витрины на Mercurio. Магазин при этом не останавливался. Стек: OpenCart, PHP, MySQL, Laravel, Chart.js, ffmpeg, Python, Mercurio. Дата: 2026. - [Магазин офисной мебели, 2022-2026](https://timcode.ru/cases/office-furniture-shop): Веду prvoffice.ru с нуля четвёртый год: своя админка, своя витрина, три редизайна, три этапа поиска и массовое редактирование каталога. Стек: Laravel, Livewire, Manticore, imgproxy, MariaDB. Дата: 2026. - [Учёт производства с нуля: платы, сканер и один маршрут в коде](https://timcode.ru/cases/factory): Написал систему учёта плат для производства Waviot: сначала прототип на готовой админке, потом своя платформа. С июня 2026 года цех ведёт в ней каждую плату, и теперь в неё переезжает всё производство с легаси 2016 года. Стек: Laravel 12, PHP 8.5, Inertia, React, TypeScript, MySQL, Pest. Дата: 2026. - [Mercurio: своя платформа для интернет-магазинов](https://timcode.ru/cases/mercurio): С 2025 года строю Mercurio: платформу, где сырьё поставщиков неизменяемо, человек правит только решения, а каталог собирается детерминированно. Два клиента, обе витрины пока на демо-стендах. Стек: Laravel, PHP, PostgreSQL, Inertia, React, TypeScript, Manticore, imgproxy, 1С. Дата: 2025. - [На один сервис меньше: перенос HES в сервер авторизации](https://timcode.ru/cases/hes-to-auth): Два самописных PHP-сервиса Waviot делили один интерфейс: экраны жили в auth, их бэкенд в HES. Перенёс HES внутрь auth так, что фронт не заметил, и закрыл один сервис с отдельным деплоем. Стек: PHP 8, MySQL, RabbitMQ. Дата: 2024. - [Кабинет и телеметрия IoT-платформы: с Zend Framework 2 на Laminas и PHP 8](https://timcode.ru/cases/zend-to-laminas): Перевёл кабинет клиентов и бэкенд телеметрии Waviot с Zend Framework 2.3 и PHP 5.6 на Laminas и PHP 8 без остановки платформы, а потом закрепил результат: PSR-12, миграции БД, Sentry. Стек: PHP 8, Laminas, Zend Framework 2, MongoDB, Doctrine, Sentry. Дата: 2022. - [Экран отдела продаж: сделки из Мегаплана, звонки из Mango Office](https://timcode.ru/cases/prime-wood-sales-screen): Для отдела продаж Prime Wood сделал экран на офисный монитор: план месяца, оплаты за день, необработанные заявки и звонки по менеджерам. PHP без фреймворка и крон, потому что этого хватало. Работу с API Mango Office потом вынес в Laravel-пакет. Стек: PHP, Megaplan API, Mango Office API, Laravel. Дата: 2020. - [Магазин мебели: с самописной CMS на OpenCart за два месяца](https://timcode.ru/cases/prime-wood-opencart): В одиночку перенёс prime-wood.ru с самописной MyCMS 2013 года на OpenCart, релиз перед Новым годом. Покупатели смены движка не заметили, магазин работает на этой платформе седьмой год. Стек: OpenCart, PHP, MySQL, Sphinx, 1С, Roistat. Дата: 2019. ## Справочник - [https://timcode.ru/handbook/](https://timcode.ru/handbook/): паттерны, архитектура и принципы на PHP, Go, Python и TypeScript, с тестами и примерами из практики. - [Чистая архитектура (Clean Architecture)](https://timcode.ru/handbook/architecture/clean-architecture/): Делает каждый сценарий приложения отдельным объектом с явным входом и выходом, а все зависимости в коде направляет внутрь, к бизнес-правилам. - [Разделение команд и запросов (CQRS)](https://timcode.ru/handbook/architecture/cqrs/): Команды меняют модель записи и ничего не возвращают, запросы читают отдельную модель, собранную под экран, и не трогают агрегат. - [Тактический DDD (Domain-Driven Design)](https://timcode.ru/handbook/architecture/ddd/): Правила предметной области живут в самих объектах домена: агрегат не даёт привести себя в неверное состояние, значения проверяются при создании, а о случившемся сообщают события. - [Хранение событий (Event Sourcing)](https://timcode.ru/handbook/architecture/event-sourcing/): Вместо текущего состояния заказа хранится журнал всего, что с ним случилось, а состояние каждый раз получается проигрыванием этого журнала. - [Гексагональная архитектура (Hexagonal Architecture)](https://timcode.ru/handbook/architecture/hexagonal/): Ставит предметную область в центр: ядро объявляет порты, а база, платёжная система, HTTP и почта подключаются к ним адаптерами снаружи. - [Слоистая архитектура (Layered Architecture)](https://timcode.ru/handbook/architecture/layered/): Делит приложение на слои представления, бизнес-логики и данных, где каждый слой вызывает только тот, что под ним. - [Ошибки как значения в Go (Errors and wrapping)](https://timcode.ru/handbook/languages/go/01-errors-and-wrapping/): Ошибка в Go - обычное значение, которое можно обернуть контекстом, проверить по смыслу и сложить с другими: так импорт прайса сообщает обо всех плохих строках сразу, а вызывающий код решает, что делать, без разбора текста. - [Readonly и перечисления в PHP (Readonly classes and enums)](https://timcode.ru/handbook/languages/php/01-readonly-and-enums/): Статус заказа становится типом, а деньги - неизменяемым значением: опечатку в статусе и тихую правку суммы ловит язык, а не код-ревью. - [Protocol и ABC в Python (Protocols vs abstract base classes)](https://timcode.ru/handbook/languages/python/01-protocols-vs-abc/): Protocol описывает, что нужно вызывающему коду, и принимает любой объект подходящей формы; ABC описывает, что обязаны уметь наследники, и делится с ними общим кодом. Хранилищам файлов нужны оба, но в разных местах. - [Размеченные объединения в TypeScript (Discriminated unions)](https://timcode.ru/handbook/languages/ts/01-discriminated-unions/): Ответ платёжного шлюза описывается объединением вариантов с общим полем kind: компилятор сам сужает тип в каждой ветке, ловит забытый вариант через never, а ошибки разбора возвращаются как значение Result, а не летят исключением. - [Шаг 1: одна функция на все способы доставки](https://timcode.ru/handbook/lessons/001-refactor-to-strategy/step-1/): Исходный код: switch по способу доставки, внутри if по стране, цены зашиты прямо в ветках. - [Шаг 2: способы доставки за общим интерфейсом](https://timcode.ru/handbook/lessons/001-refactor-to-strategy/step-2/): Каждая ветка switch становится своим классом с методом cost, а калькулятор только выбирает, кому отдать посылку. - [Шаг 3: реестр тарифов и правила из конфига](https://timcode.ru/handbook/lessons/001-refactor-to-strategy/step-3/): Тарифы регистрируются по коду, а какой тариф у какого способа в какой стране - решает список правил из конфига. - [Шаг 1: SQL прямо в контроллере](https://timcode.ru/handbook/lessons/002-extract-repository/step-1/): Исходный код: контроллер заказов сам пишет SQL, разбирает ряды и решает, можно ли отменить заказ. - [Шаг 2: шлюз таблицы](https://timcode.ru/handbook/lessons/002-extract-repository/step-2/): Все SQL-строки для orders уезжают в класс OrdersTable, контроллер зовёт его методы, но всё ещё работает с рядами. - [Шаг 3: репозиторий в домене](https://timcode.ru/handbook/lessons/002-extract-repository/step-3/): Заказ становится сущностью Order с правилом отмены, доступ к заказам - интерфейсом OrderRepository, а SQL уходит в одну из его реализаций. - [Шаг 1: касса зовёт всех сама](https://timcode.ru/handbook/lessons/003-events-instead-of-calls/step-1/): Исходный код: Checkout сохраняет заказ и по очереди вызывает склад, аналитику и почту. - [Шаг 2: доменное событие и синхронный диспетчер](https://timcode.ru/handbook/lessons/003-events-instead-of-calls/step-2/): Checkout публикует OrderPlaced, а склад, аналитика и почта подписываются на него сами. - [Шаг 3: независимые подписчики](https://timcode.ru/handbook/lessons/003-events-instead-of-calls/step-3/): Диспетчер ловит ошибку каждого слушателя, отдаёт её в отчёт и зовёт следующих: упавшая аналитика больше не мешает письму. - [Цепочка обязанностей (Chain of Responsibility)](https://timcode.ru/handbook/patterns/behavioral/chain-of-responsibility/): Передаёт запрос по цепочке обработчиков: каждый либо обрабатывает его сам, либо отдаёт следующему, и отправитель не знает, кто в итоге ответит. - [Практика: приём вебхуков АТС (PBX webhooks)](https://timcode.ru/handbook/patterns/behavioral/chain-of-responsibility/practice-webhook/): Облачная АТС шлёт события о звонках и повторяет их, пока не получит 2xx: подпись, дедупликация и фильтр тестовых номеров стоят звеньями цепочки, а заканчивается она маршрутизатором, который по типу события выбирает, что записать в CRM. - [Практика: middleware входящего запроса (Request middleware)](https://timcode.ru/handbook/patterns/behavioral/chain-of-responsibility/practice/): Лимит запросов, аутентификация и валидация как звенья перед обработчиком: каждое может ответить само и остановить цепочку, а от порядка звеньев зависит, кого и от чего они защищают. - [Команда (Command)](https://timcode.ru/handbook/patterns/behavioral/command/): Превращает запрос в объект: его можно сохранить, передать, выполнить позже и отменить, а тот, кто его запускает, не знает, что именно он делает. - [Практика: команды прибору учёта по радио (Device commands)](https://timcode.ru/handbook/patterns/behavioral/command/practice-device/): Команда прибору - объект с параметрами и ожидаемым ответом: её ставят в очередь, отправляют по одной, ждут подтверждения, повторяют по таймауту и отменяют, пока она не ушла в эфир. - [Практика: редактор с undo/redo (Undo history)](https://timcode.ru/handbook/patterns/behavioral/command/practice/): История правок документа из команд: отмена и повтор с курсором, обрезка ветки повтора, склейка набранных подряд символов и правило, что неудачная правка в историю не попадает. - [Интерпретатор (Interpreter)](https://timcode.ru/handbook/patterns/behavioral/interpreter/): Правила маленького языка становятся деревом объектов, и каждый узел сам вычисляет свою часть выражения. - [Практика: фильтры товаров (Product filter language)](https://timcode.ru/handbook/patterns/behavioral/interpreter/practice/): Маркетолог пишет условие подборки строкой, а код превращает её в дерево выражений и проверяет на каждом товаре; опечатка ловится при разборе, с позицией в строке. - [Итератор (Iterator)](https://timcode.ru/handbook/patterns/behavioral/iterator/): Даёт обойти коллекцию по одному элементу, не раскрывая, как она устроена внутри. - [Практика: постраничный обход API заказов (Paginated iterator)](https://timcode.ru/handbook/patterns/behavioral/iterator/practice/): Итератор прячет пагинацию внешнего API: клиент видит один длинный поток заказов, а следующая страница запрашивается, только когда до неё дошли. - [Посредник (Mediator)](https://timcode.ru/handbook/patterns/behavioral/mediator/): Убирает прямые связи между объектами: каждый говорит только с посредником, а правила их взаимодействия собраны в одном месте. - [Практика: форма оформления заказа (Checkout form)](https://timcode.ru/handbook/patterns/behavioral/mediator/practice/): Поля формы зависят друг от друга: доставка прячет и показывает адрес, страна меняет список способов, промокод пересчитывает итог. Посредник CheckoutForm собирает эти правила в одном месте и не даёт уведомлениям ходить по кругу. - [Снимок (Memento)](https://timcode.ru/handbook/patterns/behavioral/memento/): Объект сохраняет своё состояние в непрозрачный снимок и потом восстанавливается из него, не раскрывая внутренностей тому, кто снимки хранит. - [Практика: черновики анкеты (Form drafts)](https://timcode.ru/handbook/patterns/behavioral/memento/practice/): Длинная заявка сохраняется сама: снимки по таймеру с лимитом, именованные точки для отката и черновик, который переживает перезагрузку страницы и узнаёт чужой формат. - [Наблюдатель (Observer)](https://timcode.ru/handbook/patterns/behavioral/observer/): Объект сообщает об изменениях всем, кто подписался, и не знает, кто они: подписчиков можно добавлять и убирать, не трогая источник. - [Практика: поток показаний приборов учёта (Meter readings stream)](https://timcode.ru/handbook/patterns/behavioral/observer/practice-meters/): Счётчики воды присылают показания, а тревога, контроль связи и суточный расход подписаны на поток: подписчики хранят состояние между событиями, а пропажу связи видно только по тику часов. - [Практика: доменные события заказа (Domain event dispatcher)](https://timcode.ru/handbook/patterns/behavioral/observer/practice/): Заказ создан или отменён, а почта, склад и аналитика узнают об этом через диспетчер событий: подписчики падают, отписываются на ходу и публикуют новые события, и рассылка должна это пережить. - [Состояние (State)](https://timcode.ru/handbook/patterns/behavioral/state/): Объект меняет поведение вместе со своим состоянием: каждое состояние — отдельный объект, и вместо if (state === ...) в каждом методе контекст просто передаёт вызов текущему состоянию. - [Практика: плата на производстве (Production line)](https://timcode.ru/handbook/patterns/behavioral/state/practice-production/): Печатная плата идёт по этапам монтаж -> контроль -> прошивка -> упаковка, а сканеры на участках присылают одни и те же четыре действия: что значит "передать дальше" или "вернуть на доработку", решает этап, на котором плата сейчас. - [Практика: жизненный цикл заказа (Order lifecycle)](https://timcode.ru/handbook/patterns/behavioral/state/practice/): Заказ проходит путь от «новый» до «доставлен», «отменён» или «возврат»: каждый статус решает, какие события допустимы, что сделать с деньгами на переходе и как объяснить отказ. - [Стратегия (Strategy)](https://timcode.ru/handbook/patterns/behavioral/strategy/): Выносит семейство взаимозаменяемых алгоритмов в отдельные объекты, чтобы менять их без правки кода, который их использует. - [Практика: цены из прайса поставщика (Supplier pricing)](https://timcode.ru/handbook/patterns/behavioral/strategy/practice-pricing/): Наценку выбирает категория товара, а описана она в конфиге: тип и параметры. Общие правила магазина применяются после любой стратегии, и стратегия не может их нарушить. - [Практика: расчёт доставки (Shipping rates)](https://timcode.ru/handbook/patterns/behavioral/strategy/practice/): Способ доставки выбирается правилами из конфига, а не if в коде: страна, вес и сумма заказа решают, какая стратегия посчитает цену. - [Шаблонный метод (Template Method)](https://timcode.ru/handbook/patterns/behavioral/template-method/): Порядок шагов алгоритма фиксирует базовый класс, а наследники подставляют только сами шаги и, если нужно, меняют поведение крючков. - [Практика: экспорт платёжного реестра (Payment registry export)](https://timcode.ru/handbook/patterns/behavioral/template-method/practice/): Реестр платежей уходит в разные банки в разных форматах, но проверка платежей, итоговая строка и контрольная сумма одни на всех, и ни один банк не может их обойти. - [Посетитель (Visitor)](https://timcode.ru/handbook/patterns/behavioral/visitor/): Новые операции над набором классов без правки самих классов: каждая операция живёт в своём объекте, а узел только говорит, какой он. - [Практика: НДС и чек по заказу (Order visitors)](https://timcode.ru/handbook/patterns/behavioral/visitor/practice/): Заказ из товаров, скидок, доставки и упаковки, а над ним растёт список операций: НДС, фискальный чек, проверка. Операции живут в посетителях с состоянием, узлы заказа остаются данными. - [Абстрактная фабрика (Abstract Factory)](https://timcode.ru/handbook/patterns/creational/abstract-factory/): Создаёт семейство связанных объектов целиком, чтобы клиент не мог смешать продукты разных семейств. - [Практика: платёжные провайдеры (Payment providers)](https://timcode.ru/handbook/patterns/creational/abstract-factory/practice/): Платёжный провайдер — это семейство: приём платежа, возвраты и разбор вебхуков. Возврат, ушедший не тому провайдеру, который принял платёж, — реальный инцидент, и абстрактная фабрика делает его невозможным по построению. - [Строитель (Builder)](https://timcode.ru/handbook/patterns/creational/builder/): Собирает сложный объект по шагам: обязательные части, необязательные в любом порядке, одна проверка в конце. - [Практика: запрос отчёта (Report query)](https://timcode.ru/handbook/patterns/creational/builder/practice/): Строитель SQL-запроса для отчётов: значения только в параметрах, пагинация с границами, и неизменяемость, из-за которой базовый запрос можно делить между отчётами без сюрпризов. - [Фабричный метод (Factory Method)](https://timcode.ru/handbook/patterns/creational/factory-method/): Общий алгоритм живёт в базовом классе, а решение «какой именно объект создать» отдаётся наследникам. - [Практика: разбор пакетов приборов учёта (Meter telemetry)](https://timcode.ru/handbook/patterns/creational/factory-method/practice-telemetry/): Проверка кадра одна для всех приборов, а парсер полезной нагрузки выбирает тип прибора из заголовка: это параметризованный фабричный метод, и новый тип прибора добавляется наследником, без правки конвейера. - [Практика: импорт прайс-листов (Price list import)](https://timcode.ru/handbook/patterns/creational/factory-method/practice/): Конвейер импорта один для всех поставщиков, а парсер зависит от формата файла: его создаёт наследник, и ровно это делает конвейер проверяемым без файлов. - [Прототип (Prototype)](https://timcode.ru/handbook/patterns/creational/prototype/): Новый объект создаётся копией уже настроенного, а глубину копии решает сам объект. - [Практика: КП из шаблона (Quote from template)](https://timcode.ru/handbook/patterns/creational/prototype/practice/): Копия коммерческого предложения для нового клиента: сбросить идентичность, продублировать строки, оставить общими товары каталога. Глубина копии — бизнес-решение, а не техническая деталь. - [Одиночка (Singleton)](https://timcode.ru/handbook/patterns/creational/singleton/): Гарантирует один экземпляр на процесс и даёт к нему глобальную точку доступа. Первое полезно, второе почти всегда вредно. - [Практика: тарифы, загруженные один раз (Loaded once)](https://timcode.ru/handbook/patterns/creational/singleton/practice/): Единственность без глобальной точки доступа: справочник тарифов загружается один раз на процесс, потокобезопасно, а клиенты получают его через конструктор и подменяют в тестах. - [Преобразователь данных (Data Mapper)](https://timcode.ru/handbook/patterns/enterprise/data-mapper/): Отдельный объект переносит данные между доменной моделью и хранилищем, так что модель не знает ни таблиц, ни колонок, ни того, что её вообще сохраняют. - [Практика: заказ в двух таблицах (Order mapper)](https://timcode.ru/handbook/patterns/enterprise/data-mapper/practice/): Маппер собирает заказ с деньгами, почтой и статусом из двух таблиц, раскладывает его обратно, удаляет снятые строки заказа и не пускает в модель битые данные из базы. - [Доменное событие (Domain Event)](https://timcode.ru/handbook/patterns/enterprise/domain-event/): Агрегат записывает, что с ним случилось, в виде неизменяемых фактов в прошедшем времени, а реагируют на эти факты другие части системы: агрегат не знает ни о почте, ни о биллинге. - [Практика: публикация событий после сохранения (Publishing domain events)](https://timcode.ru/handbook/patterns/enterprise/domain-event/practice/): Подписка меняет тариф, биллинг и почта узнают об этом из событий, но только если изменение действительно записано в базу: событие о том, чего нет в базе, хуже, чем никакого. - [Нулевой объект (Null Object)](https://timcode.ru/handbook/patterns/enterprise/null-object/): Вместо null подставляет объект с тем же интерфейсом и нейтральным поведением, чтобы код не проверял на null перед каждым вызовом. - [Практика: гость, лояльность и аналитика (Shop profile)](https://timcode.ru/handbook/patterns/enterprise/null-object/practice/): Гость вместо null-пользователя, отсутствующая программа лояльности и выключенная аналитика как нулевые объекты, которые выбираются в одном месте, и ненайденный заказ, который нулевым объектом быть не должен. - [Outbox (Transactional Outbox)](https://timcode.ru/handbook/patterns/enterprise/outbox/): Событие о бизнес-изменении записывается в ту же базу и в той же транзакции, что и само изменение, а в брокер его отправляет отдельный процесс: данные и событие не расходятся, даже если брокер лежит. - [Практика: уведомления клиентам в Telegram (Telegram notifications)](https://timcode.ru/handbook/patterns/enterprise/outbox/practice-telegram/): Уведомление о заказе пишется в outbox одной транзакцией с заказом, а фоновый отправитель шлёт его через Bot API: пачками, с паузой на лимит, с пометкой мёртвых чатов и без дублей благодаря ключу идемпотентности. - [Практика: надёжный отправитель outbox (Outbox relay)](https://timcode.ru/handbook/patterns/enterprise/outbox/practice/): Брокер то отказывает, то теряет подтверждения, а события одного заказа должны прийти по порядку и ровно один раз в смысле результата: отправитель повторяет, получатель отбрасывает дубли. - [Репозиторий (Repository)](https://timcode.ru/handbook/patterns/enterprise/repository/): Даёт предметной области коллекцию сущностей с методами на её языке и прячет за ней, где и как они хранятся. - [Практика: контрагенты из учётной системы (Repository over HTTP API)](https://timcode.ru/handbook/patterns/enterprise/repository/practice-api/): Контрагенты живут не в нашей базе, а в учётной системе вроде 1С за HTTP API, и домен всё равно видит обычный репозиторий: страницы превращаются в обход, коды ответа в доменные ошибки, поля API в модель. - [Практика: один контракт на два хранилища (Repository contract tests)](https://timcode.ru/handbook/patterns/enterprise/repository/practice/): Репозиторий в памяти и репозиторий в JSON-файле проходят один и тот же набор тестов: копии вместо общих объектов, поиск по критерию и конфликт версий. - [Сервисный слой (Service Layer)](https://timcode.ru/handbook/patterns/enterprise/service-layer/): Собирает сценарии приложения в один слой: контроллер, консольная команда и очередь вызывают один метод сервиса, а не повторяют шаги каждый у себя. - [Практика: регистрация на мероприятие (Event registration)](https://timcode.ru/handbook/patterns/enterprise/service-layer/practice/): Регистрация на лекцию с ограниченным числом мест: сервис принимает проверенную команду, держит транзакцию вокруг сценария, отдаёт DTO вместо сущности и шлёт письмо только после записи. - [Спецификация (Specification)](https://timcode.ru/handbook/patterns/enterprise/specification/): Бизнес-правило становится отдельным объектом с одним вопросом «подходит ли кандидат», и одно и то же правило проверяет объект, отбирает список и собирается с другими через «и», «или», «не». - [Практика: доступ к заказу в админке (Access rules)](https://timcode.ru/handbook/patterns/enterprise/specification/practice-access/): Правило "кто может редактировать заказ" собирается из маленьких спецификаций над парой пользователь и заказ, и при отказе называет условие, которое не выполнено. - [Практика: аудитория рассылки (Mailing audience)](https://timcode.ru/handbook/patterns/enterprise/specification/practice/): Одно правило отбора клиентов проверяет клиента в памяти и переводится в SQL WHERE с параметрами, так что приложение и база отбирают одних и тех же людей. - [Единица работы (Unit of Work)](https://timcode.ru/handbook/patterns/enterprise/unit-of-work/): Собирает все изменения бизнес-операции (новые, изменённые и удалённые объекты) и записывает их в хранилище одним проходом в конце, а не по одному в момент каждой правки. - [Практика: перевод и заказ в одной транзакции (Unit of Work)](https://timcode.ru/handbook/patterns/enterprise/unit-of-work/practice/): Оформление заказа списывает деньги клиента, зачисляет их магазину и уменьшает остатки склада. Единица работы держит все эти объекты, сама находит изменённые и записывает их в одной транзакции: всё или ничего. - [Адаптер (Adapter)](https://timcode.ru/handbook/patterns/structural/adapter/): Даёт старому или чужому классу тот интерфейс, которого ждёт клиент, не меняя ни класс, ни клиента. - [Практика: SMS через старый шлюз (SMS gateway adapter)](https://timcode.ru/handbook/patterns/structural/adapter/practice/): Старый SMS-шлюз подключается к интерфейсу уведомлений: адаптер переводит номера телефонов в формат шлюза, а числовые коды ответа в причины, по которым приложение решает, что делать дальше. - [Мост (Bridge)](https://timcode.ru/handbook/patterns/structural/bridge/): Разделяет иерархию на две — абстракцию и реализацию, — чтобы каждая росла сама по себе, а не перемножалась с другой. - [Практика: отчёты в разных форматах (Report renderers)](https://timcode.ru/handbook/patterns/structural/bridge/practice/): Два отчёта и три формата вывода без шести классов: отчёт решает, что показать, рендерер — как это записать, и новый формат не трогает отчёты. - [Компоновщик (Composite)](https://timcode.ru/handbook/patterns/structural/composite/): Дерево из простых и составных объектов с одним интерфейсом: клиент спрашивает узел, не выясняя, лист это или контейнер. - [Практика: цена корзины с наборами (Bundle pricing)](https://timcode.ru/handbook/patterns/structural/composite/practice/): Наборы товаров со своей скидкой, вложенные друг в друга: цена считается по дереву, а дерево приходит из каталога, где редактор может ошибиться. - [Декоратор (Decorator)](https://timcode.ru/handbook/patterns/structural/decorator/): Добавляет объекту поведение обёрткой с тем же интерфейсом: обёртки складываются в любом количестве и порядке, а исходный класс не меняется. - [Практика: слои над медленным отчётом (Report source layers)](https://timcode.ru/handbook/patterns/structural/decorator/practice-reports/): Кэш, замер времени, журнал и лимит строк над одним медленным запросом к базе: набор и порядок слоёв задаёт конфиг, и от порядка зависит, какие данные получит соседний экран. - [Практика: клиент курсов валют (API client layers)](https://timcode.ru/handbook/patterns/structural/decorator/practice/): Ретраи, кэш и журнал как слои поверх одного клиента внешнего API: каждый слой простой, а поведение целого определяет порядок, в котором их сложили. - [Фасад (Facade)](https://timcode.ru/handbook/patterns/structural/facade/): Даёт один простой вход в набор подсистем: клиент вызывает одну операцию, а порядок шагов и связи между частями знает фасад. - [Практика: оформление заказа (Place order)](https://timcode.ru/handbook/patterns/structural/facade/practice/): Оформление заказа — это резерв на складе, списание оплаты и уведомление. Фасад PlaceOrder прячет не только порядок шагов, но и то, что делать, когда шаг не удался. - [Приспособленец (Flyweight)](https://timcode.ru/handbook/patterns/structural/flyweight/): Много мелких объектов делят одно неизменяемое общее состояние, а уникальное хранят у себя или получают параметрами. - [Практика: стили ячеек таблицы (Cell styles)](https://timcode.ru/handbook/patterns/structural/flyweight/practice/): Десять тысяч ячеек выгрузки делят три стиля: стиль неизменяемый и приходит из пула по ключу, а правка стиля одной ячейки не задевает соседей, потому что создаёт новый стиль, а не меняет общий. - [Заместитель (Proxy)](https://timcode.ru/handbook/patterns/structural/proxy/): Подставляет вместо объекта другой с тем же интерфейсом, который решает, когда и пускать ли вызов дальше. - [Практика: кэш репозитория товаров (Caching proxy)](https://timcode.ru/handbook/patterns/structural/proxy/practice/): Кэширующий заместитель перед медленным репозиторием: ответы из памяти с TTL, сброс при записи и честная граница — запись мимо заместителя кэш не видит. - [Композиция вместо наследования (Composition over Inheritance)](https://timcode.ru/handbook/principles/composition-over-inheritance/): Поведение собирается из небольших частей, а не наследуется: новый набор каналов уведомлений - это новый список, а не новый класс. - [Не повторяйся, не усложняй, не делай впрок (DRY, KISS, YAGNI)](https://timcode.ru/handbook/principles/dry-kiss-yagni/): Три коротких правила против лишнего кода: одно знание - в одном месте, простое решение вместо хитрого, и ничего "на будущее". - [Закон Деметры (Law of Demeter)](https://timcode.ru/handbook/principles/law-of-demeter/): Объект общается только с ближайшими соседями: спрашивает заказ, куда его везти, а не лезет через заказ в покупателя и его адрес. - [Принцип инверсии зависимостей (Dependency Inversion Principle)](https://timcode.ru/handbook/principles/solid/dependency-inversion/): Модули верхнего уровня зависят от абстракций, а не от деталей: сервис уведомлений знает порт Mailer, а SMTP - деталь, которая подключается снаружи. - [Принцип разделения интерфейсов (Interface Segregation Principle)](https://timcode.ru/handbook/principles/solid/interface-segregation/): Клиент не должен зависеть от методов, которыми не пользуется: печать и сканирование - отдельные узкие интерфейсы, а не одно "устройство". - [Принцип подстановки Лисков (Liskov Substitution Principle)](https://timcode.ru/handbook/principles/solid/liskov-substitution/): Подтип должен работать везде, где ждут базовый тип: хранилище только для чтения не наследует запись, которую не умеет выполнять. - [Принцип открытости/закрытости (Open/Closed Principle)](https://timcode.ru/handbook/principles/solid/open-closed/): Модуль открыт для расширения и закрыт для изменения: новый способ оплаты подключается к кассе без правки её кода. - [Принцип единственной ответственности (Single Responsibility Principle)](https://timcode.ru/handbook/principles/solid/single-responsibility/): У модуля одна причина для изменения: подсчёт продаж и форматирование отчёта меняются по разным причинам, поэтому живут порознь. ## Другие форматы - [https://timcode.ru/llms-full.txt](https://timcode.ru/llms-full.txt): всё то же плюс полные тексты статей и кейсов одним запросом. - [https://timcode.ru/resume.json](https://timcode.ru/resume.json): резюме по схеме JSON Resume v1.0.0. - [https://timcode.ru/json](https://timcode.ru/json): краткая карточка в JSON. - [https://timcode.ru/rss.xml](https://timcode.ru/rss.xml): лента статей и кейсов. - `curl timcode.ru`: та же страница в терминале.