Портирование Laravel → Goravel
Цель портирования — сохранить внешний контракт и поведение действующего Laravel-сервиса в реализации на Go/Goravel. Проверять нужно HTTP, данные, фоновые процессы и наблюдаемые побочные эффекты: события, файлы, вызовы соседних сервисов и изменения кэша.
Создание каркаса и локальный запуск описаны в первом запуске Go-сервиса. Эта статья объясняет организацию переноса и критерии готовности. Рабочая последовательность, чек-листы этапов и форматы артефактов поддерживаются в Go-шаблоне:
| Документ шаблона | Назначение |
|---|---|
| PORTING_LARAVEL_TO_GORAVEL.md | Обзор процесса и запуск работы |
| instructions/README.md | Порядок этапов, переходы, статусы и возобновление |
| PORTING_PROMPT.md | Полный prompt для одного обращения к агенту |
| instructions/reference | Технические требования по контракту, данным, интеграциям, тестам и эксплуатации |
Для конкретного переноса зафиксируйте commit шаблона и читайте инструкции из этого checkout-а. Ссылки выше ведут к развиваемой версии; результаты проверок должны быть привязаны к использованным исходникам и инструментам.
Источники требований и расхождения
| Источник | Что из него определять |
|---|---|
| Laravel-код, конфигурация и тесты | Бизнес-правила, валидация, casts, observers, транзакции, ошибки и эффекты |
| OpenAPI и зарегистрированные маршруты | Метод и URI, параметры, security, форматы запросов и ответов, статусы, пагинация |
| Реальные данные и сообщения из изолированных копий | Представления JSON, nullable-значений, дат, идентификаторов и payload, которые нужно продолжать читать |
| Общие guidelines Ensi и раздел Go/Goravel | Правила платформы и принятые способы реализации на Go |
Шаблон и .agents/skills | Рабочий каркас, порядок действий и проверки конкретной задачи |
| Уже перенесённые сервисы | Примеры реализации после проверки применимости к исходному поведению |
Если OpenAPI, фактическое Laravel-поведение или общая рекомендация расходятся, зафиксируйте конкретные метод/URI, исходные файлы, наблюдаемый результат и затронутых потребителей. Согласуйте ожидаемое поведение с владельцем API. Изменение URI, статуса, nullable или формата события при портировании требует отдельного решения.
Утверждённые отличия исходного OpenAPI учитываются механизмом PORTING_CONTRACT_EXCEPTIONS.json: file, reason, reviewed_by, source_sha256, target_sha256. Хеши привязаны к полному содержимому исходного и целевого файлов; изменение файла требует пересмотра исключения. Подробности — в требованиях к контракту.
Подготовить исходные данные
Определите три отдельные директории: Laravel-источник, checkout Go-шаблона и целевой Go-сервис. Зафиксируйте commit и состояние рабочего дерева источника, версию инструкций, исходный diff цели, версии генератора и зависимостей. Прочитайте применимые AGENTS.md и AGENTS.local.md, затем соответствующие задаче статьи Go-раздела.
Laravel и референсные сервисы во время переноса используются для чтения. Для проверок перехода БД нужны отдельные базы и обезличенная копия данных. Секретные значения env, ключи и исходные пользовательские данные не включают в планы и отчёты.
Переносите подсистемы, которыми пользуется исходный сервис. Отсутствие Kafka, очередей, scheduler, auth или собственной БД подтверждайте ссылками на изученные исходники. Для используемой подсистемы отдельно учитывайте поддерживаемые альтернативные драйверы: локальное CACHE_DRIVER=file само по себе не означает, что можно удалить поддержку Redis из другого профиля.
Инвентаризировать поведение
На этапе 02 скрипт scripts/inventory-laravel.sh создаёт исходные PORTING_PLAN.md, PORTING_ENV.json и PORTING_COVERAGE.json. Он требует Python 3 и PyYAML из scripts/requirements-porting.txt; настройка отдельного Python-окружения описана в инструкции инвентаризации.
Список операций из OpenAPI дополните чтением маршрутов, providers, actions, requests, resources, queries, моделей, observers, listeners и тестов. Отдельно проверьте web/callback-маршруты и команды. Результатом должны стать следующие матрицы:
| Область | Что учитывать |
|---|---|
| HTTP | Все метод/URI, middleware, нормализация, ошибки, авторизация, JSON и другие media types |
| Search | Каждый filter, sort, include, defaults, pagination и граничные значения |
| Изменения данных | Отсутствующее поле, null, false, 0, пустые значения, вложенная валидация, переходы статусов и no-op |
| БД | Итоговая схема, обязательные данные, связи, scopes, casts, sequences и существующие JSON/JSONB-представления |
| Observers и события | Триггер, изменённые поля, payload, ключ, headers, порядок, момент публикации и ошибки |
| HTTP-интеграции | Версия клиента, URL, метод, параметры, headers, таймауты, повторы и обработка отказов |
| Файлы | Диски, ключи и права, metadata, upload/replacement/delete и состояние после частичной ошибки |
| Фоновые роли | Consumer, job, command и расписание: вход, регистрация, locks, retry/backoff и повторная обработка |
| Эксплуатация | HTTP/worker/consumer/cron, логи, метрики, health, shutdown, поиск и настройки сборки/деплоя |
Каждому элементу назначьте будущий Go-пакет, зависимости и проверку результата. Скрипт формирует заготовку: пустые ссылки на тесты и pending на этом этапе означают предстоящую работу. Повторный запуск с --force перезаписывает все три артефакта; при возобновлении обновляйте заполненные записи выборочно.
PORTING_COVERAGE.json проверяется относительно исходного OpenAPI. Маршруты вне OpenAPI учитывайте отдельно в плане и очереди, с эквивалентными ссылками на проверки.
Выполнять перенос по этапам
Одно обращение к агенту выполняет один этап; на этапах 05, 06, 07 — одну заранее определённую единицу работы (UNIT). После проверок агент сохраняет результат и параметры следующего обращения. Переход к следующему этапу или unit выполняется новым запросом.
| Этап | Результат и граница проверки |
|---|---|
| 01. Каркас | Целевой проект, Git, локальное окружение и исходные результаты сборки и тестов |
| 02. Инвентаризация | Полная карта исходного поведения и очередь задач с зависимостями и критериями |
| 03. Контракт и общий HTTP-слой | Совместимые OpenAPI, генерация, маршрутизация, общая валидация и ошибки |
| 04. Конфигурация | Карта env и регистрация используемых подключений и опубликованных клиентов |
| 05. Данные, повторяемый | Одна группа таблиц, моделей, фабрик и обязательных данных: чистая БД, копия Laravel-БД, повтор и rollback |
| 06. HTTP-ресурс, повторяемый | Один ресурс или заранее выделенный сценарий вместе с валидацией, эффектами и компонентными тестами |
| 07. Фоновый процесс, повторяемый | Один consumer, job, command или сценарий расписания, включая ошибки и повторную обработку |
| 08. Система и эксплуатация | Полные цепочки, переход всей БД, smoke, shutdown и репетиция runbook в выделенном окружении |
| 09. Приёмка | Полные проверки без пропусков, сверка доказательств и итоговый REPORT.md |
Точные предпосылки и допустимые возвраты определяет файл этапа. Например, зависимость auth из этапа 03 может потребовать отдельного обращения к этапу 04, после которого нужно вернуться к проверкам HTTP-слоя.
Единица работы должна иметь устойчивый slug и проверяемую границу: группа связанных таблиц, один ресурс, PATCH со статусными переходами, отдельный consumer. Большой ресурс разделяют до начала реализации, сохраняя общий список его операций. Родительский ресурс остаётся незавершённым до проверки всей матрицы.
Ресурс переносится вместе с эффектами его операций: публикация producer, удаление файлов, внешние вызовы и observers проверяются на этапе 06. Этап 07 нужен для обработчиков фоновых входов и не заменяет проверки исходящих событий HTTP-операции.
Этапы 05–07 остаются in_progress, пока есть незавершённые units. При подтверждённом отсутствии собственной БД этап 05 отмечают not_applicable; при отсутствии фоновых ролей так отмечают этап 07. В остальных этапах неприменимые пункты обосновывают отдельно. Если обязательная зависимость не готова, запишите blocker, действие для его устранения и возврат к зависимым проверкам.
Запуск и возобновление агента
Скопируйте полный PORTING_PROMPT.md из выбранного checkout-а шаблона и заполните параметры. Пример блока параметров:
LARAVEL_SOURCE: /path/to/laravel-service
TEMPLATE_SOURCE: /path/to/backend-service-skeleton-go
DESTINATION: /path/to/service-go
DOMAIN: example
SERVICE: service
APP_URL: http://example-service-go.ensi.127.0.0.1.nip.io
STAGE: next
UNIT:
REFERENCE_SERVICES: /path/to/cms-go, /path/to/catalog-cache-go, /path/to/oms-go
Это параметры prompt, а не shell-команда или отдельный CLI. Укажите только применимые референсы; целевой сервис исключите из REFERENCE_SERVICES.
Для нового переноса без сохранённого состояния STAGE: next выбирает 01. Затем он выбирает первый незавершённый этап с выполненными предпосылками, а для повторяемого этапа — первую готовую unit из очереди. Для явного выбора используйте, например:
STAGE: 06
UNIT: pages-patch
pages-patch — пример slug: он должен существовать в очереди и иметь выполненные зависимости. Явный номер этапа не разрешает пропустить его предпосылки.
Перед каждым продолжением агент читает git status, PORTING_PROGRESS.md, план, env/coverage и необходимые checkpoints. Существующий частичный порт оценивается по критериям этапов с сохранением кода, истории и решений; отсутствие старого checkpoint не доказывает успешного завершения этапа.
После неуспешной проверки продолжайте ту же unit с указанием причины и результата повторной проверки. Если для неё нужна отдельная зависимость, запишите задачу на соответствующий этап и выполните разрешённую подготовку отдельным обращением. Независимая работа возможна только в явно выбранных границах.
Изменение Laravel-исходника, шаблона, генератора или общего кода требует определить затронутые units и повторить их проверки. Историю checkpoints сохраняйте; прежний успех относится к прежнему состоянию исходников.
Артефакты целевого сервиса
Результаты конкретного переноса сохраняются в DESTINATION, инструкции читаются из TEMPLATE_SOURCE. Локальные env-файлы содержат настройки окружения, а документы ниже — описания и доказательства без секретных значений.
| Артефакт | Назначение |
|---|---|
PORTING_PROGRESS.md | Версии исходников, этапы, очередь units, зависимости, blockers и параметры следующего обращения |
PORTING_PLAN.md | Инвентаризация и матрицы исходное поведение → реализация → проверка |
PORTING_ENV.json | Переменные и поддерживаемые профили, target, файлы и режимы сравнения; формат version: 1 |
PORTING_COVERAGE.json | Операции OpenAPI и ссылки на конкретные успешные и ошибочные тесты с описанием assertions |
PORTING_CONTRACT_EXCEPTIONS.json | Согласованные отличия OpenAPI, если они есть; причина, автор решения и хеши |
porting/checkpoints/<stage>[-<unit>].md | Объём обращения, выполненные действия, проверки, доказательства и незавершённые пункты |
porting/evidence/ | Обезличенные логи, схемы и сравнения, если нужны отдельные файлы |
porting/RUNBOOK.md | Проверенный на этапе 08 порядок переключения и возврата HTTP и фоновых ролей |
REPORT.md | Итоговый отчёт этапа 09, черновик которого создают после успешного check:generated |
Progress и checkpoint оформляют по шаблонам. Их заполняет агент: скрипты не выбирают этапы и не проверяют автоматически каждый пункт Markdown-чек-листа.
Статусы и результаты проверок
В PORTING_PROGRESS.md используются:
| Статус этапа или unit | Значение |
|---|---|
not_started | Работа не начата |
in_progress | Часть действий или units выполнена |
blocked | Названная зависимость не позволяет выполнить обязательный пункт |
done | Все применимые критерии выполнены и подтверждены |
not_applicable | Поведение отсутствует; изученные исходники и причина указаны |
Результаты отдельных проверок записываются отдельно: passed, failed, not_run, not_applicable. Для последних трёх нужны причины. Эти статусы не следует переносить в JSON произвольным образом: например, карта env использует used, optional, unused, а первоначальный pending требует разрешения до приёмки.
Для команды указывайте рабочий каталог, дату, параметры без секретов, exit code и ссылку на лог при его наличии. Для теста — точный файл, Test...-символ и проверяемые поля, состояние БД и эффекты. Ручное сравнение обозначайте как ручное.
В PORTING_COVERAGE.json у каждой операции есть method, path, operation_id, массивы success и errors. Элемент массива содержит file, symbol, assertions. Текущий validator требует оба массива непустыми и не поддерживает исключение для пустого errors. Если содержательного ошибочного сценария нет, требуется отдельное решение о политике проверки; фиктивная ссылка на тест не подтверждает покрытие. Формат и пример — в требованиях к тестам.
Проверять совместимость в границах этапа
Все Go- и Artisan-команды выполняются через elc, тесты — с .env.testing. Пакеты выбираются по проверяемой unit. Полный запуск обязателен на системном этапе; промежуточные generated-заготовки других ресурсов учитываются как предстоящая работа.
| Проверка | Что она подтверждает |
|---|---|
check:openapi | Текущая спецификация проходит настроенный lint |
| Семантическое сравнение контракта с Laravel | Отличия деревьев исходной и целевой спецификаций учтены |
check:generated | Повторная генерация не меняет файлы |
| Компонентный тест HTTP/action/consumer/command | Наблюдаемое поведение сценария при заданных данных и контролируемых зависимостях |
| Переход БД на изолированной копии | Применение миграций и сохранность проверенных схем и данных |
| Smoke, системные сценарии и репетиция runbook | Работа проверенных цепочек и эксплуатационных шагов в выделенном окружении |
Lint и компиляция не доказывают совместимость с Laravel. Fake подтверждает взаимодействие приложения с подменой; доставку через реальный брокер или совместимость живого upstream проверяют отдельно, когда это требуется сценарием. Тестовые сообщения и изменяющие запросы направляйте в выделенные окружения.
Особенно проверяйте:
- исходные method/URI, включая
/{id}:action, формат и статусы ошибок, middleware и доступ; - missing/null/false/zero, очистку ранее заполненных nullable-полей и вложенные FormRequest rules;
- полный набор filters/sorts/includes, точный порядок, pagination и границы дат;
- фактические Laravel casts/resources, legacy JSONB, даты, enum и пустые массивы;
- транзакции, observers, каскадные эффекты, no-op и поведение после частичного отказа;
- payload, key, headers, момент публикации, повторную доставку и восстановление;
- подключение общего Kafka teardown во всех component-suites: базовый вызов из переопределённых hooks,
t.Cleanup(s.TearDownTest)после ручного setup и отсутствие непроверенных сообщений; - файлы, входные/исходящие HTTP-вызовы, их количество, параметры и ошибки.
Для SQL-фикстур используйте CreateQuietly, если не проверяются model events подготовки. События операции проверяйте через AssertKafkaMessages и assertions содержимого; ConfirmKafkaMessages не заменяет проверки, а ResetKafka допустим после проверки завершённой фазы. У gateway без собственной БД сохраняйте его вариант ComponentSuite без PostgreSQL runner. Подробности — в автотестах.
Файлы, полностью принадлежащие генератору, меняют через resources/openapi, конфигурацию и elc ensi-gog server. Handler и тестовые заготовки дополняют в предусмотренных точках расширения. Удаление тестов с TODO не завершает перенос. SQL-транзакция не делает публикацию Kafka, внешний HTTP-вызов или операцию storage атомарными; сохраняйте исходные гарантии и проверяйте состояния после отказов.
Выбирать скилл по задаче
Скиллы находятся в .agents/skills шаблона и целевого сервиса. Перед применением читайте его локальный SKILL.md вместе с правилами репозитория; проверьте наличие нужных helper-ов и версий. Ссылки ниже ведут к реализации в Go-шаблоне.
| Скилл | Когда применять |
|---|---|
| goravel-development | Bootstrap, providers, facades, ORM, команды и особенности установленного Goravel |
| add-sql-search-resource | Добавление SQL-ресурса и :search через миграцию, модель, query, action, mapper и handler |
| review-search-contract | Аудит согласованности OpenAPI, схемы, ORM, query, mapper и тестов существующего search |
| test-search-endpoint | Полная матрица HTTP search-тестов с PostgreSQL: filter, sort, include, pagination и изоляция |
| implement-api-mutation | Create/PATCH/delete, presence, валидация, переходы состояний, транзакции и эффекты |
| implement-http-client-integration | Исходящий вызов через клиент, конфигурацию и provider, context, ошибки и controlled tests |
| implement-file-storage-operation | Загрузка, привязка, замена и удаление файлов, metadata, ownership и компенсация |
| implement-kafka-model-event | Типизированный consumer события модели, синхронизация либо обновление существующей записи |
| publish-kafka-model-event | Исходящие model events, dirty, сериализация, ключи, headers и граница SQL/доставки |
| test-component-behavior | Компонентные проверки mutations, actions, команд и событий с реальной БД и контролируемыми эффектами |
Скилл дополняет выбранный этап и не расширяет его объём. Например, при переносе PATCH могут понадобиться mutation, publication и component-testing скиллы, если этот PATCH действительно публикует событие. Для :search этап 06 требует test-search-endpoint, для реализации на Goravel — goravel-development; остальные выбирайте по фактическим границам сценария.
Критерии готовности и финальная приёмка
Порт готов после этапа 09, когда:
- все действующие операции, таблицы, интеграции и фоновые процессы из инвентаризации реализованы и проверены; неприменимые подсистемы обоснованы по исходникам;
- контракт и согласованные отличия сверены, генерация воспроизводится;
- реальные success/error-тесты проверяют JSON, состояние и эффекты, а сгенерированные заготовки завершены или обоснованно сопоставлены с заменой;
- переход используемой БД проверен на чистой базе и Laravel-копии, включая повтор и rollback на одноразовой копии;
- полный набор тестов, стандартные проверки, системные сценарии и smoke пройдены;
- роли процесса, наблюдаемость и восстановление подтверждены,
porting/RUNBOOK.mdпрошёл репетицию; - итоговые progress, coverage, checkpoints и
REPORT.mdсоответствуют текущему коду.
В этапе 09 после успешного elc mise run check:generated подготовьте черновик REPORT.md, затем выполните финальный контроль. Для shell-команд задайте реальные абсолютные пути — параметры prompt автоматически не создают переменные окружения:
LARAVEL_SOURCE=/path/to/laravel-service
DESTINATION=/path/to/service-go
cd "$DESTINATION"
LARAVEL_SOURCE="$LARAVEL_SOURCE" ./scripts/check-porting.sh --with-db
--with-db требует исполняемый scripts/check-porting-db.sh, реализованный для этого сервиса. Он проверяет изолированные базы; шаблонная проверка регистрации миграций его не заменяет. Для сервиса без собственной БД вместо последней команды используйте подтверждённое обоснование:
LARAVEL_SOURCE="$LARAVEL_SOURCE" ./scripts/check-porting.sh \
--no-database "Service has no database; source references are recorded in PORTING_PLAN.md"
Exit code check-porting.sh | Результат |
|---|---|
0 | Автоматические обязательные проверки прошли без пропусков; требуется также сверка ручных пунктов и assertions |
1 | Проверка завершилась ошибкой |
2 | Некорректные аргументы |
3 | Выполнена только часть проверок из-за пропусков |
Без явного режима проверки БД скрипт по умолчанию пропускает переход БД и возвращает частичный результат при успехе остальных проверок. Флаги --skip-go, --skip-tests, --skip-db пригодны для диагностики, но не подтверждают завершение. Полный check-porting.sh предназначен для этапа 09; на промежуточном этапе он ожидаемо обнаруживает незавершённую работу.
После контроля дополните REPORT.md фактическими результатами: что сохранено, какие изменения согласованы, что потребовало ручного переноса, какие подсистемы не используются, какие проверки выполнены и какие ограничения остались. Невыполненный обязательный критерий оставляет этап незавершённым.
Сосуществование Laravel и Go
На этапе 08 подготовьте и отрепетируйте runbook переключения: HTTP-трафик и общая БД, Kafka groups/offsets, накопленные PHP jobs, Redis formats/prefixes/TTL/locks, файлы и поисковые индексы. Определите порядок остановки старых фоновых ролей, исключение двойного выполнения, условия возврата к Laravel и ответственных за действия.