Да, всю логику нужно выносить в отдельные классы.
Задача контроллера - определить, какую информацию исходя из полученного запроса показывать. Даже валидацию лучше выносить в отдельный Request, потому что это уже не задача контроллера.
Я обычно делаю сервисы, как вы написали, но еще и:
/app/Repositories - репозитории. Позволяют в контроллере брать информацию из БД, типа такого:
$this->postsRepository->getPostsForHomePageLoop();
А вообще способов много - можете прочитать про паттерны проектирования.
Главное помните про SRP из SOLID. Что каждый класс отвечает за свою функциональность и не может выходить за её рамки.