M — модели… если упростить — это просто проекция вашего хранилища данных (будь то ORM/ODM или обычные файлы) на объекты. И все… Бизнес логику они содержать не должны. Так же модель никак не должна знать как именно она будет сохраняться в хранилище, ибо тогда при смене хранилища начнутся приключения. В этом смысле концепция AR в том виде, в котором она реализована в Yii мне не нравится. Какой-то класс CActiveRecord который знает кучу всего о том что и как он будет сохранять, и от него надо наследовать нашу простенькую модельку содержащую в себе всего пару полей. Опять же есть разные реализации паттерна ActiveRecord, хотя мне больше по душе DataMapper (например Doctrine), при котором у вас есть отдельный компонент, знающий о том что и куда пихать. Логика по выборкам в этом случае выносится в репозитории, эдакие менеджеры записей. Плюсы этого способа — очень легко в системе заменить тип хранилища, расширять и поддерживать за счет слабой связанности компонентов. Контроллеры знают только о том что есть репозитории, которые имеют для каждой записи свой интерфейс (аля getPendingPayments()), и они не знают откуда эти модели выходят, из базы, висят в памяти или еще чего.
V — тут все просто… шаблоны призваны отвязать представление от логики работы с данными, что бы изменение первого не вызывало редактирование какого-то сервиса/контроллера.
C — тут сложнее. Есть две концепции, толстые и тонкие контроллеры. Последние наиболее распространены, и включают в себя два слоя: сами контроллеры и сервисный слой. Контроллеры разруливают какое действие нужно произвести с данными в зависимости от пришедших параметров, прав пользователя и т.д. Они же разруливают ситуации когда нету исхомых данных, различные ошибки и т.д. Вся бизнес логика же выносится в сервисный слой. Сервисы должны минимально знать о контексте использования и заниматься только тем, для чего они были написанны. Скажем, сервис валидации данных ничего не должен знать о сервисе для обработки платежей, или о правах доступа и т.д. Ему на вход приходят данные, и он их проверяет по правилам, описанным для этой модели.
Так же есть некоторые рекомендации по поводу использования тонких контроллеров: дублирование кода контроллеров — это нормально. Если у вас есть несколько контроллеров, реализующих примерно одно и тоже. То есть если отличия минимальны, скажем права пользователей разные, разные вьюшки, разные модели (актуально для простых CRUD операций), то лучше уж пусть будет много очень похожих методов, чем городить какой-то один базовый контроллер и внутри городить логику для обработки всего этого. Так как вся бизнес логика вынесена в сервисы, дублирования именно ее нету. А за время жизни проекта многое может и поменяться, и намного удобнее вносить изменения в отдельные методы а не в один здоровенный супер класс.
Так же при работе с сервисами очень помогает использование контейнера зависимостей. Благо готовых реализаций (как простых, как Pimple так и сложных, как например DiC в Symfony2 или Zend2) достаточно много.
p.s. Мое мнение не претендует на истину. Если кто-то увидел что-то в таком подходе что-то не корректное, буду рад если укажите.