1. Приложение необходимо разбивать на модули. Модуль включает в себя какую-то сущность и связанные с ней действия. Например, это может быть пользователь и контроллеры, отвечающие за вход/регистрацию, личный кабинет (настройки) и управление пользователями. Раньше я, разумеется, использовал модульность, но, как правило, модуль был достаточно весомым, он включал хоть и независимую (относительно) часть приложения, но все же там могли оказаться, например, и пользователи, и форум. Предложенная модель модулей в ZF2 предполагает соблюдения принципа единственной ответственности на более высоком уровне (по сравнения с классом или методом).

2. Событийная модель — очень мощная штука. Она помогает следовать принципу единственной ответственности на более низком уровне. Простой пример: при регистрации пользователя надо отправить ему письмо (может, и что-то еще). В этом случае вы сохраняете пользователя и генерируете событие, например, “регистрация”. Все подписанные на это событие выполнят свое предназначение.

3. Dependency Injection — об этом много и давно говорится на просторах интернета. Я рассматриваю DI с точки зрения удобства модульного тестирования, когда любую зависимость можно легко подменить моком. Например, при тестировании класса, взаимодействующего с базой данных, вам не хочется делать реальные запросы, так как вы тестируете в данный момент вовсе не это.  С другой стороны DI делает код более гибким, когда изменить используемый алгоритм не составит труда — нужно лишь передать новый объект, который будет реализовывать тот же интерфейс. Например, для создания пароля вам захочется изменить алгоритм на более надежный.