Попробуем разобраться в самом распространенном определении принципа:
Модули верхних уровней не должны зависеть от модулей нижних уровней. Оба типа модулей должны зависеть от абстракций. Абстракции не должны зависеть от деталей. Детали должны зависеть от абстракций.
Со второй частью все понятно, классы реализаций зависят от контрактов, но вот с первой не очень: на каком уровне находится модуль?
Есть так же определения:1
Наиболее гибкими получаются системы, в которых зависимости в исходном коде направлены на абстракции, а не на конкретные реализации.
Код, реализующий высокоуровневую политику, не должен зависеть от кода, реализующего низкоуровневые детали. Напротив, детали должны зависеть от политики.
Вот тут появляется намек на то, что означают модуля и на каком они уровне. Рассмотрим теперь простое решение в котором есть проект с бизнес логикой и проект с репозиториями. В первом описывается бизнес сценарий, а во втором происходит обращение к базе данных. Поток управления идет от первого проекта ко второму, бизнес логика вызывает репозиторий. Если реализовать это так, что зависимость будет направлена от проекта с бизнес логикой на проект с репозиторием, то в принципе все будет работать, направление зависимости при этом совпадает с потоком управления.
Но при таком подходе возникают проблемы: изменения в работе с данными влекут изменения в бизнес логике. Будь то смена сигнатур или смена базы данных или чего то еще, хотя бизнес сценарии никак от этого не должны зависеть.
В данном примере бизнес логика состоит из модулей верхнего уровня, а в репозитории модули нижнего уровня. Если следовать принципу инверсии зависимости то такую зависимость надо инвертировать так чтобы проект с репозиториями зависел от проекта с бизнес логикой, сделать это очень легко и описано тут.
В данном примере абстракции, от которых оба модуля зависят, находятся в проекте с бизнес логикой.
UseCase - это класс с бизнес сценарием.
Возникает вопрос: почему не сделать еще один проект с абстракциями и не направить на него все зависимости? Потому что контракты в данном случае определяются бизнес процессом. Вы можете сперва написать бизнес логику, определить интерфейсы репозиториев и другие необходимые контракты для работы бизнес логики, а потом уже их реализовать.
Если это веб приложение, то проект с контроллерами там же состоит из модулей нижнего уровня несмотря на то, что от него начнется поток исполнения. Этот проект будет зависеть от проекта с бизнес логикой, использовать интерфейсы и реализованные сервисы оттуда.
В итоге модули нижнего уровня находятся в проектах содержащих репозитории, веб методы, взаимодействия с внешними системами и другие детали реализующие контракты с более высокого уровня. А верхние в бизнес логике.
Так же появляется возможность тестировать приложение, можно создавать заглушки и подставки для интерфейсов. Если это будут конкретные классы, то это будет проблемно для большинства фреймворков.
Примечания
1. Мартин Р. Чистая архитектура. Искусство разработки программного обеспечения. — СПб.: Питер, 2018.