Хорошая архитектура - это очень понятная архитектура, когда для каждого типа задачи созданы логичные и понятные средства. Когда приложение построено таким образом, то его получится и тестировать, и изменять/расширять. Если нужно добавить новый класс, то в хорошей архитектуре понятно, куда его нужно добавить.
Всего существует не так много типов задач:
1) хранение данных приложения - модель. Модель ничего не должна знать о базе данных.
2) слой доступа к БД - репозиторий. Вся работа с БД - здесь, и всё, что связано с одной сущностью - в репозитории этой сущности. Если нужно взаимодействие нескольких сущностей, то, скорее всего, репозиторий одной из сущностей не подойдёт, эта задача пойдёт в сервис.
3) бизнес-логика - сервисы. Сервисы умеют получать данные (модели), обращаясь к репозиториям или лучше к другим сервисам (служебным).
4) служебные задачи - сервисы. Например, кэширование данных реализуется в специальном сервисе.
5) отображение данных - шаблоны. Весь html - только в шаблоне, а также логика отображения тоже в нём (вывод списков, некоторые фильтры)
6) подготовка данных к отображению - контроллер. Запрос пользователя приходит в контроллер, контроллер же обращается к сервису (или в простом случае к репозиторию) за данными. Возможно, для обработки запроса нужно получить данные из нескольких сервисов/репозиториев, а скорее, правильный сервис сам подготовит все данные для запроса.
Получается, контроллер вызывает сервис, который предоставит готовые данные, и будет очень простым - "тонким". Основной код, отвечающий за работу задач приложения - в сервисах. Один и тот же сервис часто будет вызываться из разных контроллеров. Работа с БД - не в сервисе и уж точно не в контроллере, для этого нужен репозиторий.
Если структура БД изменится, то в хорошей архитектуре поменять придётся только репозиторий, но не сервисы/контроллеры. С другой стороны, изменение схемы БД чаще всего связаны с новыми фичами, поэтому придётся добавить новый сервис или изменить существующий.
Модель же умеет не так много - выводить свои данные, возможно, в разных форматах (данные, возвращаемые моделью не обязаны совпадать с полями сущности - см. пример в комментарии
keltanas в ответе
Владимир Балин).
Модели, репозитории, контроллеры, шаблоны обычно расположены в особых специальных папках проекта. А вот сервисы могут быть расположены в совершенно разных папках проекта, созданных специально для них. Как пример, сервисы кэширования можно поместить в папку Persistance - отдельный сервис для получения сущностей и отдельные сервисы для получения агрегированных данных (полученных из разных сущностей). При изменении способа кэширования поменяются только сервисы, связанные с кэшированием, но не остальные части проекта.
Получается, контроллеры "тонкие", модели "тонкие", а репозитории гораздо толще. Сервисы тоже "толстые", но их хорошо разбивать на отдельные сервисы, чтобы не было вся логика проекта в одном классе, и тогда каждый сервис уже не такой уж "толстый".
P.S. Я вот написал, что модель не должна ничего не знать о базе данных, получается, что неправильно использовать аннотации для маппинга полей сущности на поля таблиц. После ответа на этот вопрос я, скорее всего, пересмотрю свои взгляды на @ОRМ-аннотации в моделях.