Gdzie zdefiniować interfejsy dla repozytorium w architekturze warstwowej?
tło
Próbuję stworzyć prostą aplikację, aby naprawdę zrozumieć cały stos DDD + TDD + itp. Moim celem jest dynamiczne wstrzykiwanie klas repozytorium DAL w czasie wykonywania. Dzięki temu moje warstwy domen i usług aplikacji są testowalne. Planuję teraz używać „DI człowieka biednego”, aby to osiągnąć ... więc zrobiłbym to w prostej aplikacji konsoli w pobliżu uruchamiania:
// 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);
Aby wykonać ten zastrzyk zależności, stworzyłem trzy interfejsy repozytorium:
-- ICustomerRepository -- IOrderRepository -- IProductRespository
Typowa implementacja:
namespace SimpleOrder.Domain.Interfaces { public interface ICustomerRepository { Customer GetCustomerById(int customerId); void SaveCustomer(Customer customer); } }
** Zauważ, że SaveCustomer odwołuje się do klasy modelu klienta zdefiniowanej w warstwie domeny. Jest to typowe dla innych repozytoriów.
JEDNAKŻE nie jestem pewien, który projekt / warstwę należy wdrożyć. Mam 5 projektów w rozwiązaniu:
SimpleOrder.ConsoleClient (prezentacja) - Chcę wprowadzić konkretną implementację domeny z poziomu aplikacji
SimpleOrder.ApplicationFacade (usługi aplikacji) - masywne metody wyższego poziomu, gruboziarniste orkiestrujące metody niższego poziomu w domenie
SimpleOrder.Contracts - Klasy DTO używane do komunikacji między usługami prezentacji i aplikacji
SimpleOrder.Domain (domena / bll) - Klasy modeli domenowych Klient, Zamówienie, Element zamówienia, Produkt
SimpleOrder.Repository (dal) - implementuje interfejsy repozytorium
Oto moje opcje, które widzę:
Opcja 1: Zdefiniuj interfejsy repozytorium w SimpleOrder.Contracts ...
PRO: to jest miejsce gdzie jamyśleć powinny należeć do nich, ponieważ stworzyłem to, aby dzielić umowy między różnymi koncernami / warstwami. np. DTO są tutaj zdefiniowane.
CON: jednak podpisy metod w każdym interfejsie odwołują się do klas modeli domeny.
Oznacza to, że musiałbym dodać odwołanie do domeny SimpleOrder.Domain, ale gdy odwołano się do SimpleOrder.Contracts w innym projekcie, będzie ona musiała przenosić SimpleOrder.Domain razem do jazdy. To nie jest w porządku.
Opcja 2: Taki sam scenariusz jak powyżej, ale określam również interfejsy dla każdej klasy modelu domeny w SimpleOrder.Contracts, dzięki czemu mogę przerwać połączenie interfejsów repozytorium z rzeczywistymi klasami modeli.
Przykład:
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: Każda klasa modelu domeny musiałaby implementować swój powiązany interfejs. to znaczy.,
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: Naprawia problem z opcją 1.
CON: Wybiera liczbę plików (i postrzeganą złożoność) w projekcie, ponieważ każda klasa domeny ma teraz powiązany interfejs.
Opcja 3: Zdefiniuj interfejsy repozytorium w SimpleOrder.Domain
IMPACT: Aby wstrzyknąć konkretne klasy repozytorium do warstwy usług aplikacji (SimpleOrder.ApplicationFacade) z SimpleOrder.ConsoleClient w czasie wykonywania, SimpleOder.ConsoleClient będzie również potrzebował odwołania do SimpleOrder.Domain.
PRO: To RÓWNIEŻ rozwiązuje opcję 1
CON: Starałem się unikać bezpośredniego odwoływania się do warstwy domeny z warstwy prezentacji, ponieważ teraz warstwa prezentacji może wiedzieć zbyt wiele o warstwie domeny. Kiedy w przyszłości zastąpię aplikację konsolową aplikacją WPF lub ASP.NET MVC, ryzykuję drugą i kolejne implementacje warstwy prezentacji od próby wywołania metod w modelu zamiast warstwy usług aplikacji. (Jednak rozważam to w Opcji 4.)
Opcja 4: Umieść interfejsy w SimpleOrder.Domain, a następnie odwołaj się do SimpleOrder.Domain z SimpleOrder.ConsoleClient.
PRO: Naprawia wszystkie powyższe problemy.
CON: To nie jest właściwe, ponieważ zapewniłbym dostęp z warstwy prezentacji bezpośrednio do metod niższego poziomu w warstwie domeny, gdy powinienemtylko zapewniać dostęp do masywnych metod wyższego poziomu w SimpleOrder.ApplicationFacade.
PYTANIE Próbowałem każdego z nich, ale zdecydowałem się na opcję 4, JEDNAKŻE jednak pozostawia to w ustach zły smak. Czy jest lepsza opcja? Czy jestem tutaj na dobrej drodze?