Кэширование — это очень сложно. Если у вас вносятся изменения в БД, то появляется масса проблем с инвалидацией. Инвалидацию очень легко забыть где-нибудь вставить, даже если с этим помогает фреймворк/ORM.
Все «полностью автоматизированные» решения по кэшированию, создавали гигантские проблемы, потому что в каких-то случаях не инвалидировали кэш, когда нужно. Допустим, есть объект Profile со свойством balance. Пользователь покупает услуги, генерируется invoice, и либо баланс показывается закэшированный, либо инвойс не появляется в списке закэшированных инвойсов. Оба случая вызывают неистовое негодование пользователей, поверьте моему опыту.
Вручную тоже сложно все предусмотреть. В сложной системе оказывается на удивление много мест, где кэш необходимо инвалидировать, и предусмотреть их все очень сложно (не превратив при этом код в спагетти).
К тому же, возникает вопрос эффективности. Инвалидировать слишком часто — теряется эффективность, приходится слишком часто обновлять данные из базы. Инвалидировать слишком редко — пользователи жалуются на устаревшие данные.
С тяжелыми запросами тоже все сложно. Допустим, есть очень тяжелый запрос, который необязательно инвалидировать, следовательно можно легко и просто закэшировать. Но первому пользователю все равно придется ждать, пока он завершится. А если результат выполнения разный для разных пользователей, то каждому придется подождать как минимум один раз. Никому это тоже не понравится.
В общем, готовых шаблонов на все случаи жизни нет. Только для read-only данных, и то с ограничениями. Задумываться о кэшировании желательно с самого начала реализации проекта, потом будет сложнее.