Привет, Хабр. Хочу рассказать о том куда, на рисунке выше, поместить репозиторий. Еще немного о DIP, IoC и структуре проекта. Есть стандартный подход согласно которому интерфейсы IRepository надо помешать в доменный слой к DomainServices а реализацию в Infrastructure. Тут речь именно о стандартных Repository которые просто абстрактная коллекция объектов не знающая про транзакции и UnitOfWork и прочее (например IList и его генерик версия типичный интерфейс абстрактного репозитория). Я лично с ним не согласен потому что обычно репозитории обрастают поведением характерным для конкретного приложения поэтому я помещают их интерфейсы к ApplicationServices. Реализация конечно остается в слое Infrastructure. Если вам интересно, то добро пожаловать под кат.

Часто многих путает старая картинка от Роберта Мартина:

У него же в статьи есть слова
Entities encapsulate Enterprise wide business rules. An entity can be an object with methods, or it can be a set of data structures and functions. It doesn’t matter so long as the entities could be used by many different applications in the enterprise.

Поэтому выходит что он просто относит DomainServices к слою Entities. Ну а UseCases == ApplicationServices. Gateways == UserPepository, EmailService, Logger. Подробнее можно почитать тут.

  1. Serivice это любой класс без состояния. InfrastructureService это анти коррапшн лаер который изолирует вас от файловой системы, библиотеки для работы c SMTP ну и конечно от ORM или библиотеки для работы с Базой Данных и т. п.
  2. Repository это паттерн. Его теоретически может реализовать любой класс в любом слое. Например типичный List это Repository который использует в качестве хранилища массив T[] и по сути является ValueObject поэтому относиться к слою Entities. Не каждый класс реализующий паттерн Repository является InfrastructureService и не все InfrastructureService реализуют паттерн Repository. Например: Logger, EmailSender.
  3. Repository работающий с БД это InfrastructureService так же как какой-нибудь EmailSender или EmailService, FileService (обертка над файловой системой который внутри вызывает File.Open(… ) и т. п.) и прочее в этом духе.


Чтобы понять куда помещать абстракции надо вспомнить про Dependency Inversion и Inversion of Control. Согласно им наш ApplicationService может использовать Infrastructure не знаю про том какая у него реализация такими способами как:
  1. Определять интерфейс, который будет реализовать Infrastructure и взаимодействовать с ним.
  2. Просто принимать делегаты вроде Action и Func.


Через интерфейсы:

    interface IRepository
    {
        int Get();
    }

    class ApplicationService
    {
        private readonly IRepository _repository;

        public ApplicationService(IRepository repository)
        {
            _repository = repository;
        }

        public int GetInt() => _repository.Get();
    }

Через делегаты:

    class ApplicationService
    {
        private readonly Func<int> _getIntFromDb;

        public ApplicationService(Func<int> getIntFromDb)
        {
            _getIntFromDb = getIntFromDb;
        }

        public int GetInt() => _getIntFromDb();
    }

Теперь о том как структурировать код. Тут надо рассмотреть два случая:
  1. Все разбито по папкам.
  2. Все разбито по библиотекам.

Подробно о терминологии можно почитать тут.

По папкам в пределах одного приложения


Пример кода SnakeGame


По библиотекам


Помещаем интерфейсы InfrastructureServices (IRepository) в библиотеку ApplicationServices, а реализацию (Repository) в библиотеку Infrastructure.



Конечная картинка из статьи человека с намного большим опытом разработки чем у меня и так же считающим что IRepository нужно помещать в Application Layer к ApplicationServices (сами реализации Repository у него тоже в Infrastructure).


Выводы


Интерфейс инфраструктурных сервисов (IRepository, IEmailSender) можно поместить в библиотеку ApplicationServices.dll, а их конкретную реализацию (Repository, EMailSender) в библиотеку Infrastructure.dll.