В недавнем посте я показывал, как структурирую приложения: всего три слоя - API, Core (business logic) и Infrastructure (взаимодействие с базой данных, cache, external services).
Архитектура, которую я использую в проектах
Сегодня хочу сфокусироваться на ключевом отличии layered architecture от clean architecture - dependency inversion.
В layered setup API вызывает методы Core class (UseCase), а Core вызывает методы Infrastructure class (Repository). Разделение уже делает responsibilities понятнее, но Core все еще знает конкретные классы из Infrastructure, из-за чего domain сложнее изолировать и тестировать.

С dependency inversion вызовы идут через абстракции. Core зависит от interfaces, а concrete classes подключаются во время выполнения. Control flow остается тем же, но тесты становятся проще: мы подставляем mocks и проверяем domain logic изолированно.

Этот принцип - не просто nice-to-have. Он лежит в основе maintainable software, и его часто спрашивают на интервью.
Итого: инвертируя зависимости, вы сохраняете Core чистым и тестируемым, ускоряете разработку и снижаете риск при изменении требований.
Сделайте dependency inversion базовой частью своего design toolbox, чтобы кодовая база оставалась гибкой и устойчивой по мере роста.
Для более глубокого изучения рекомендую: