Отвечу с позиции опыта на больших и средних проектах:
Как говорят другие ответчики -
D3lphi и
Александр Шаповал , контроллер должен содержать минимум логики, и нет "единого правильного способа" его реализовать.
Однако, исходя из своей практики, для обеспечения таких качеств проекта как сопровождаемость, тестируемость и изменяемость, я поступаю следующим образом:
Контроллер, каким бы он не был и как бы не организовывался - содержит только обработку входных данных (валидация и преобразование) и преобразование выходных данных в нужный формат
Логика и оркестрация которая в 80% случаев и является предметом спора о том как стоить контроллер (речь всех этих вызовах сервисов, управления потоками данных между ними, последовательности действий и пр), находятся в отдельном классе согласно паттерну Unit of Work - что позволяет легко покрыть все что связано с логикой вменяемыми юниттестами без танцев с бубном.
Т.е. структура в итоге такая:
{view} <--> {controller} <---> {unit_of_work} -->>> {{{..lots of services...}}}
по сути класс семейства UnitOfWork - это полный сценарий обработки входных и выходных данных , с оркестрацией потоков данных и порядком действий, возможно какой то мелкой вспомогательной логикой, эдакий фронтенд над сервисами, заточенный под каждое конкретное действие извне (сам паттерн предполагает что класс создается на каждый отдельных случай и почти один в один описывает UseCase логику как она пришла от бизнеса (заказчика/аналитиков/моей головы/etc).
Сами классы семейства UnitOfWork (имеется ввиду в одном приложении) могут иметь собственные иерархии наследования и композии - в угоду бизнес логики и дабы не дублировать код - главный критерий - это полная независимость бизнес и сервис слоев от контроллера. - что и является на мой взгляд "каноничным" вариантом реализации композии M и C в MVC.
У меня, как у человека который не пишет фронты в принципе (имею ввиду сам UX/UI), но постоянно занимается сервисами, рестом, библиотеками - этот подход выработался по одной просто причине:
взаимодействие с командой, я предоставляю "библиотеку"(не в классическом понятии этого слова) полностью абстрагированную от любых фронтов, оттестированную, полностью функциональную - а люди пишут к ней шаблонные обвязки где только валидация данных и рендер нужного формата под HTTP/Sockets/AMQP/etc типа всяких там CRUD/REST или RPC