Porto SAP
- title
- Porto SAP
- type
- concept
- summary
- Слоёный паттерн для PHP/Laravel: Containers (бизнес-логика) + Ship (инфраструктура), Actions над Tasks с одним
run(), каждый артефакт в отдельном классе - tags
- software-architecture, php, laravel, ddd, clean-architecture
- sources
- porto-readme
- created
- 2026-05-13
- updated
- 2026-09-14
- lang
- ru
- translation_of
- porto-sap
- source_updated
- 2026-09-14
- translated
- 2026-09-14
- translator
- lllm/antigravity/gemini-3.7-flash-medium
Porto - архитектурный паттерн, опубликованный Махмудом Зальтом (Mahmoud Zalt; MIT, ~1.6k★ на github.com/Mahmoudz/Porto) для организации серверного кода приложений. В первую очередь он ориентирован на PHP/Laravel, но в теории не привязан к конкретному фреймворку. Фреймворк, реализующий Porto для Laravel, называется Apiato (apiato.io); сам репозиторий Porto - это спецификация паттерна и документация, а не среда исполнения.
Текущий README - это лаконичная посадочная страница, всё основное содержимое находится на сайте с документацией. В основе паттерна лежат две идеи.
Два слоя: Containers и Ship
Код приложения делится на две части:
app/
├── Containers/
│ └── {Section}/
│ └── {Container}/
│ ├── Actions/
│ ├── Tasks/
│ ├── Models/
│ ├── Controllers/
│ ├── Requests/
│ ├── Routes/
│ ├── Transformers/
│ ├── Events/
│ ├── Listeners/
│ ├── Jobs/
│ ├── Commands/
│ ├── Migrations/
│ ├── Tests/
│ ├── Configs/
│ └── Exceptions/
└── Ship/
├── Parents/ # base classes (Controller, Model, Request, ...)
├── Providers/
├── Middleware/
├── Exceptions/
└── Configs/
- Containers содержат бизнес-логику одной ограниченной предметной области (
User,Billing,Order, ...). Секции (Sections) группируют связанные Containers (например, секцияAuthenticationможет содержатьUser,Login,Roles). - Ship содержит всё сквозное: базовые классы фреймворка, от которых наследуются Containers, глобальные middleware, общие service provider'ы, конфигурации всего приложения.
Заявленная выгода - путь миграции из монолита в микросервисы: предполагается, что Container достаточно самодостаточен, поэтому его вынос в отдельный сервис сводится в основном к настройке сборки и развёртывания, а не к переписыванию кода. Насколько реальные кодовые базы на Porto сохраняют такую чистоту Containers - отдельный вопрос: это тот же разрыв между теорией и практикой, с которым сталкивается концепция "ограниченный контекст как микросервис" в литературе по DDD. В what-even-are-microservices утверждается, что разделение кода вообще не стоит считать сложной частью: извлечение Container - это лишь изменение в сборке и deploy'е, но организации делят сервисы по организационным причинам, и чёткая граница не снимает издержек на согласование между командами.
Actions и Tasks
Внутри Container'а бизнес-логика разделена на два типа компонентов:
- Action - оркестрирует один сценарий использования (например,
RegisterUserAction). Он выстраивает Tasks в последовательность; именно его вызывают Controllers, Jobs или Commands. - Task - делает ровно одну вещь, содержит единственный публичный метод
run(), переиспользуется между разными Actions (например,CreateUserTask,SendWelcomeEmailTask,AssignRoleTask). - Sub-Action - именованная группа Tasks, используемая сразу несколькими Actions, чтобы не дублировать цепочки вызовов.
Это та же идея, что и классы сценариев использования (use cases) в Clean Architecture или интеракторы/сервисы в кодовых базах с элементами DDD, только с ужесточённым правилом: один класс - один метод - одна задача. Контроллеры остаются тонкими: разобрать запрос, вызвать Action, вернуть результат.
Свой класс для каждой сущности
Самая характерная черта паттерна - не слои, а требование раскладывать каждый концептуально обособленный артефакт в собственный класс в предсказуемой папке. Маршруты (web / api / cli) живут в отдельных файлах; валидация запросов - в классах Form Request; формирование ответов - в Transformers; сквозная функциональность - в Middleware; доменные события - в Events + Listeners; работа с очередями - в Jobs; CLI - в Commands; доступ к данным - в Repositories; персистентное состояние - в Models; DTO - для передачи данных между Containers; Exceptions - под каждую отдельную ошибку.
В итоге получается широкое и неглубокое дерево: множество мелких файлов, каждый из которых назван строго по выполняемой функции. Авторы классических кодовых баз на Laravel иногда возражают, что это over-engineering. Ответ Porto: накладные расходы на ещё один файл малы, а цена поиска того, "где именно происходит X", в монолитном Controller'е - высока. Паттерн делает ставку на то, что инструменты (переход к символу в IDE, поиск) работают дёшево, а значит, предельно подробное дробление на классы даёт структурный выигрыш.
Аргумент об удобстве для ИИ
Текущий README начинается со следующего тезиса:
Porto's strict adherence to the single responsibility principle enhances its compatibility with AI tools like GitHub Copilot, which thrive on clear, well-defined classes.
Этот довод перекликается с clean-code-coding-agents (и лежащими в его основе рассуждениями из книги Дядюшки Боба Clean Architecture): контекстные окна LLM ограничены, и кодовая база, где каждый файл решает одну именованную задачу, обходится дешевле по токенам при навигации, чем код, где нужно прочитать 600 строк Controller'а, чтобы понять происходящее при POST /users. Структура Porto с небольшими классами в предсказуемых папках идеально подходит для цикла grep-and-read у агентов: файл с именем RegisterUserAction.php в app/Containers/Authentication/User/Actions/ сразу сообщает агенту, что он делает и где искать следующий шаг, ещё до открытия файла.
В реалиях 2026 года это самый убедительный аргумент в пользу паттерна. Заявление о переходе от монолита к микросервисам проверить сложнее (обычно это работает в первый день, а затем незаметно деградирует по мере размытия границ Containers), но тезис об удобстве для агентов проверяется на каждом конкретном файле: откройте файл и проверьте, говорит ли его имя о том, что он делает. В Porto ответ положителен по определению структуры; в традиционном MVC - "может быть, в зависимости от разработчика".
Родословная
Porto объединяет в себе:
- MVC - сохраняет Models/Views/Controllers в привычном для Laravel виде, но низводит Controllers до роли тонких адаптеров.
- DDD-lite - Containers по сути являются ограниченными контекстами (bounded contexts). Sections служат для более крупной группировки.
- Clean Architecture - Actions выступают сценариями использования (use cases); Tasks - атомарными операциями внутри них.
- Гексагональная архитектура / порты и адаптеры - разделение на Ship/Container повторяет правило "ядро домена не зависит от инфраструктуры", хотя Porto обеспечивает его через соглашение о структуре папок, а не линтингом правил зависимостей.
Чего паттерн не берёт из DDD: агрегаты, value objects, доменные события как инварианты первого уровня, проработку единого языка (ubiquitous language). Porto - структурный паттерн, а не философия моделирования.
Когда подходит
- Приложениям на Laravel, переросшим плоскую структуру по умолчанию
app/Http/Controllers/и требующим места для сквозных механизмов. - Командам, желающим закрепить структуру соглашениями, а не проверками на code review.
- Кодовым базам, над которыми наряду с людьми работают coding agents (см. clean-code-coding-agents).
Когда не подходит
- Небольшим приложениям, где накладные расходы на обилие файлов создают реальные издержки, а не окупаются в будущем.
- Командам, не готовым придерживаться принципа "один класс - одна задача": без этой дисциплины Porto деградирует в обычный Laravel с увеличенным числом папок.
- Языкам без хуков жизненного цикла в стиле Laravel (Providers, Middleware, Form Requests, Events) - концепцию паттерна можно перенести, но детали реализации не совпадут.
Место в вики
- clean-code-coding-agents - более общий тезис о том, что при участии агентов структура становится важнее, а не второстепеннее.
- programming-as-theory-building - концепция Наура о том, почему фиксация структуры через соглашения о папках - слабая замена общей ментальной модели команды, но лучшее, что может предложить паттерн.
- no-silver-bullet - Брукс о случайной и сущностной сложности: Porto борется со случайной сложностью ("куда положить X?"), оставляя сущностную ("что должен делать X?") предметной области.
- metapatterns - компендиум Дениса Полторака, классифицирующий сотни архитектурных паттернов (включая Layers и Hexagonal Architecture) менее чем в 20 метапаттернов; Porto относится к тем конкретным паттернам, которые он описывает.
- scotty - сторона развёртывания того же мира Laravel: SSH task runner в традициях Envoy для момента, когда приложение уже структурировано и его нужно доставить на сервер.