Где определить интерфейсы для репозитория в многоуровневой архитектуре?

Фон

Я пытаюсь создать простое приложение, чтобы действительно понять весь стек DDD + TDD + и т.д. Моя цель - динамически вводить классы репозитория DAL во время выполнения. Это помогает мне Доменные и прикладные службы. Я планирую использовать "бедного человека DI" для достижения это на данный момент... поэтому я бы сделал это в простом консольном приложении около запуска:

    // Poor man DI, injecting DAL repository classes at runtime
    var productRepository = new SimpleOrder.Repository.ProductRespository();
    var customerRepository = new SimpleOrder.Repository.CustomerRepository();
    var orderRepository = new SimpleOrder.Repository.OrderRepository();

    // Constructor injection into this class in the Application Services layer,
    // SimpleOrder.ApplicationFacade
    OrderEntry oe = new OrderEntry(customerRepository, orderRepository, productRepository);

Чтобы выполнить эту инъекцию зависимостей, я создал три интерфейса репозитория:

-- ICustomerRepository
-- IOrderRepository
-- IProductRespository

Типичная реализация:

    namespace SimpleOrder.Domain.Interfaces
    {
        public interface ICustomerRepository
        {
            Customer GetCustomerById(int customerId);
            void SaveCustomer(Customer customer);
        }
    }

** Обратите внимание, что SaveCustomer ссылается на класс модели клиента, определенный на уровне домена. Это типично для других репозиториев.

ОДНАКО Я не уверен, какой проект/слой они должны быть реализованы. У меня есть 5 проектов в решении:

  • SimpleOrder.ConsoleClient(презентация) - Я хочу ввести конкретную реализацию домена отсюда как приложение

  • SimpleOrder.ApplicationFacade(службы приложений) - мелкие более высокоуровневые, более грубые методы, организовывающие методы нижнего уровня в домене

  • SimpleOrder.Contracts - классы DTO, используемые для связи между презентационными и прикладными службами

  • SimpleOrder.Domain(домен/bll) - классы модели домена Customer, Order, OrderItem, Product

  • SimpleOrder.Repository(dal) - реализует интерфейсы репозитория

Вот мои варианты, как я вижу:

Вариант 1: Определите интерфейсы репозитория в SimpleOrder.Contracts...

PRO: здесь я думаю, что они должны принадлежать, потому что я создал это для обмена контрактами между различными проблемами/слоями. например, DTO.

CON: однако метод signatures in каждого интерфейса ссылается на классы модели домена.
Это означает, что мне пришлось бы добавить ссылку на SimpleOrder.Domain, но когда SimpleOrder.Contracts ссылается в другом проекте, ему придется нести SimpleOrder.Domain для езды. Это не так.

Вариант 2: Тот же сценарий, что и выше, но я также определяю интерфейсы для каждой модели домена класса в SimpleOrder.Contracts, поэтому я могу разбить связь интерфейсов репозитория с фактическими классами моделей.

Пример:

    namespace SimpleOrder.Domain.Interfaces
    {
        public interface ICustomerRepository
        {
            ICustomer** GetCustomerById(int customerId);
            void SaveCustomer(ICustomer customer);
        }

        public interface ICustomer
        {
            int CustomerId { get; set; }
            string Name { get; set; }
            System.Collections.Generic.List Orders { get; }
        }
    }

IMPACT: каждому классу модели домена придется реализовать свой связанный интерфейс. то есть.,

    public class Customer : SimpleOrder.Domain.Interfaces.ICustomer
    {
        public Customer()
        {
            _orders = new List();
        }

        public int CustomerId { get; set; }
        public string Name { get; set; }

        private List _orders;
        public virtual List Orders {
            get { return _orders; }
        }
    }

PRO: Исправлена ​​проблема с параметром 1.

CON: Это взрывает количество файлов (и воспринимаемую сложность) в проекте, потому что каждый класс домена теперь имеет связанный интерфейс.

Вариант 3: Определите интервал хранилища в SimpleOrder.Domain

IMPACT: Чтобы внедрить конкретные классы репозитория в уровень сервисов приложений (SimpleOrder.ApplicationFacade project) из SimpleOrder.ConsoleClient во время выполнения, SimpleOder.ConsoleClient также потребуется ссылка на SimpleOrder.Domain.

PRO: Это ТАКЖЕ решает вариант 1

CON: Я пытался избежать ссылки на доменный уровень из уровня представления непосредственно, потому что теперь уровень представления может слишком много знать о домене. Когда я буду заменять консольное приложение в будущем с помощью WPF или ASP.NET MVC-приложения в будущем, я рискую реализовать второй и последующие реализации уровня представления, пытаясь вызвать методы в модели вместо уровня приложений. (Однако я рассматриваю это в Варианте 4.)

Вариант 4: Поместите интерфейсы в SimpleOrder.Domain, затем обратитесь к SimpleOrder.Domain из SimpleOrder.ConsoleClient.

PRO: Исправлены все вышеперечисленные проблемы.

CON: Это не очень хорошо, потому что я предоставляю доступ к уровню презентации непосредственно к методам нижнего уровня в доменном слое, когда я должен предоставлять только  доступ к более сложным методам более высокого уровня в SimpleOrder.ApplicationFacade.

Вопрос Я пробовал каждый из них, но решил, что на Варианте 4, ОДНАКО, это оставляет мне неприятный вкус. Есть ли лучший вариант? Я на правильном пути здесь?

Ответ 1

Из того, что я понимаю по вашему вопросу, я бы согласился, что вариант 4 является лучшим. Интерфейсы репозитория должны быть объявлены на уровне домена рядом со всеми объектами домена. Реализация указанных интерфейсов должна быть частью уровня инфраструктуры - уровня, который соединяет ваш доменный мир с миром. Взгляните на шестиугольную архитектуру, чтобы увидеть некоторые из причин для этого.

Чтобы решить проблему с опцией 4, вы не должны думать о том, что консольное приложение является исключительно уровнем презентации. У него также есть другие обязанности, такие как назначение приложения и корня состава в терминах DI. В консольном приложении может быть компонент презентации, который взаимодействует только с приложениями. Вы также можете инкапсулировать сервис приложения за службой открытого хоста, реализованной с помощью ASP.NET WebAPI. Затем слой презентации будет ссылаться только на эту службу и быть скрытым от базового уровня домена.

Ответ 2

Я согласен с eulerfx. Единственное, что я хотел бы добавить к этому, это то, что он не чувствует себя хорошо, потому что традиционная архитектура N-уровня может заставить вас зависеть от базы данных и использовать абстракции для разрыва этих зависимостей.

Вы должны посмотреть Theion Architecture, который является примером того, как вы организуете свои зависимости в варианте 4. Я лично считаю, что это является наиболее масштабируемой моделью архитектуры. Если вы соедините эту архитектуру с практикой DDD, вы получите максимальную гибкость с минимальными затратами усилий.

С другой стороны, многие реализации, которые я видел, вызывают контракты для репозиториев и доменных служб в свой собственный проект. Однако это чисто организационное решение. Совершенно верно, чтобы они были включены в проект вашей модели домена.