Я в общем нашёл решение. Головной класс Framework наследуется от Singleton. Экземпляры отдельных классов (модулей) создаются в методе init класса Framework через new и сохраняются в свойства класса Framework. Модули общаются друг с другом только через фреймворк из любой части кода, например Framework::i()->file_system->get_contents( $file ). Таким образом, модули ничего не знают друг про друга. Если модуль при каких-либо условиях не должен загружаться, то он оборачивается в условие в методе init и экземпляр этого модуля не создаётся.
Очень странно, что мало кто разбирается в архитектуре и все дают ответы в виде "лозунгов" без конкретных примеров кода.