Где определить интерфейсы для репозитория в многоуровневой архитектуре?
Фон
Я пытаюсь создать простое приложение, чтобы действительно понять весь стек 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, вы получите максимальную гибкость с минимальными затратами усилий.
С другой стороны, многие реализации, которые я видел, вызывают контракты для репозиториев и доменных служб в свой собственный проект. Однако это чисто организационное решение. Совершенно верно, чтобы они были включены в проект вашей модели домена.