Где определить интерфейсы для хранилища в многоуровневой архитектуре?
Фон
Я пытаюсь создать простое приложение, чтобы действительно понять весь стек DDD + TDD + и т. Д. Моя цель - динамически внедрить классы хранилища DAL во время выполнения. Это позволяет проверять уровни моего домена и служб приложений. Я планирую использовать "DI бедняков", чтобы сделать это сейчас ... поэтому я бы сделал это в простом консольном приложении рядом с автозагрузкой:
// Poor man's 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 ссылается на класс модели Customer, определенный на уровне домена. Это типично для других хранилищ.
ОДНАКО Я не уверен, в каком проекте / слое они должны быть реализованы. У меня есть 5 проектов в решении:
SimpleOrder.ConsoleClient (презентация) - Я хочу внедрить конкретную реализацию домена отсюда как приложение
SimpleOrder.ApplicationFacade (сервисы приложений) - коренастые высокоуровневые, грубые методы, оркестрирующие низкоуровневые методы в области
SimpleOrder.Contracts - классы DTO, используемые для связи между службами представления и приложений
SimpleOrder.Domain (domain / bll) - доменная модель классов Customer, Order, OrderItem, Product
SimpleOrder.Repository (dal) - реализует интерфейсы репозитория
Вот мои варианты, как я это вижу:
Опция 1: Определите интерфейсы репозитория в SimpleOrder.Contracts ...
PRO: это где ядумать они должны принадлежать, потому что я создал это, чтобы делиться контрактами между различными проблемами / слоями. напр., DTO определены здесь.
CON: однако сигнатуры методов каждого интерфейса ссылаются на классы модели домена.
Это означает, что мне нужно было бы добавить ссылку на 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; } } }
ВОЗДЕЙСТВИЕ: Каждый класс модели предметной области должен был бы реализовать свой связанный интерфейс. т.е.
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
ВЛИЯНИЕ: Чтобы внедрить конкретные классы репозитория в слой служб приложений (проект SimpleOrder.ApplicationFacade) из 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, ОДНАКО, который оставляет неприятный вкус во рту по этому поводу. Есть ли лучший вариант? Я на правильном пути здесь?