10

HintergrundWo definieren Sie die Schnittstellen für ein Repository in einer mehrschichtigen Architektur?

Ich versuche, eine einfache Anwendung zu erstellen, wirklich den ganzen Stapel von DDD zu verstehen + TDD + etc. Mein Ziel ist es, die DAL-Repository-Klassen zur Laufzeit dynamisch zu injizieren. Dies hält meine Domänen- und Anwendungsdienste-Ebenen testbar. Ich plane „armen Mannes DI“ über die Verwendung diese jetzt zu tun ... so würde ich dies in einer einfachen Konsolenanwendung in der Nähe von Startup tun:

 

    // 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); 

diese Abhängigkeit Injektion zu erreichen, habe ich drei Repository erstellt haben Schnittstellen:

 
-- ICustomerRepository 
-- IOrderRepository 
-- IProductRespository 

Eine typische Implementierung:

 

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

** Beachten Sie, dass SaveCustomer die Kundenmodellklasse in der Doma definierten Referenzen in der Schicht. Dies ist typisch für die anderen Repositories.

JEDOCH bin ich nicht sicher, welches Projekt/Schicht sollten sie in realisiert werden Ich habe 5 Projekte in einer Lösung:

  1. SimpleOrder.ConsoleClient (Präsentation) - Ich möchte injizieren. die konkrete Umsetzung der Domain, von hier wie die Anwendung

  2. SimpleOrder.ApplicationFacade (Anwendungsdienste) - klobig auf höherer Ebene, grobkörnig Methoden o rchestrating niedrigere Ebene Methoden in der Domäne

  3. SimpleOrder.Contracts - DTO verwendete Klassen für die Kommunikation zwischen Präsentations- und Anwendungsdienste

  4. SimpleOrder.Domain (Domain/BLL) - domain Modellklassen Kunden, Ordnung, OrderItem, Produkt

  5. SimpleOrder.Repository (dal) - implementiert die repo sitory Schnittstellen

Hier sind meine Optionen, wie ich es sehe:

Option 1: Definieren Sie die Repository-Schnittstellen in SimpleOrder.Contracts ...

PRO: das ist, wo ich denke, sie sollten gehören, weil ich dies erstellt, um Verträge zwischen verschiedenen Anliegen/Schichten zu teilen. zB DTOs sind hier definiert.

CON: aber die Methodensignaturen in jeder Schnittstelle verweist auf Domänenmodellklassen.
Dies bedeutet, dass ich einen Verweis auf die SimpleOrder.Domain hinzufügen müsste, aber wenn die SimpleOrder.Auf Verträge wird in einem anderen Projekt verwiesen, es muss SimpleOrder.Domain für die Fahrt mit sich führen. Das fühlt sich nicht richtig an.

Option 2: gleiches Szenario wie oben, aber ich habe auch definieren Schnittstellen für jede Domain Modell Klasse in dem SimpleOrder.Contracts so kann ich die Kopplung der Repository-Schnittstellen zu den aktuellen Modellklassen brechen.

Beispiel:

 

    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: Jede Domain Modellklasse müsste seine zugehörige Schnittstelle implementieren. d. h.

 

    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: Behebt das Problem der Option 1.

CON: Dies explodiert die Anzahl der Dateien (und die wahrgenommene Komplexität) im Projekt, weil jede Domain-Klasse jetzt eine zugehörige Schnittstelle hat.

Option 3: Definieren der Repository intefaces im SimpleOrder.Domain

IMPACT: Um die konkreten Klassen-Repository in die Anwendungsdiensteschicht (SimpleOrder.ApplicationFacade Projekt) von der SimpleOrder.ConsoleClient zur Laufzeit zu injizieren SimpleOder.ConsoleClient benötigt außerdem einen Verweis auf SimpleOrder.Domain.

PRO: Dies löst auch Option 1

CON: Ich habe versucht, die Domain-Schicht von der Präsentationsschicht direkt zu vermeiden verweisen, weil jetzt die Präsentationsschicht über die Domänenschicht zu viel wissen kann. Wenn ich in Zukunft die Konsolenanwendung durch eine WPF- oder ASP.NET MVC-App ersetze, riskiere ich, dass die zweite und nachfolgende Implementierungen der Darstellungsschicht versuchen, Methoden im Modell anstelle der Ebene der Anwendungsdienste aufzurufen. (Allerdings berücksichtige ich dies in Option 4.)

Option 4: Setzen Sie die Schnittstellen in SimpleOrder.Domain, und verweisen Sie dann auf SimpleOrder.Domain von SimpleOrder.ConsoleClient.

PRO: Behebt alle oben genannten Probleme.

CON: Das fühlt sich nicht richtig, weil ich Zugang von der Präsentationsschicht zu den untergeordneten Methoden in der Domänenschicht direkt bereitstellt würde, wenn ich soll nur Zugang zum höheren Ebene klumpigen werden, vorausgesetzt, Methoden in der SimpleOrder.ApplicationFacade.

FRAGE ich jede dieser versucht haben, haben aber auf Option 4 JEDOCH ständiger hinterlässt einen schlechten Geschmack im Mund darüber. Gibt es eine bessere Option? Bin ich hier richtig?

+0

Bei näherer Betrachtung ... sind die Optionen 3 und 4 im Wesentlichen gleich. Hoppla. Ich hätte besser nachlesen sollen. –

Antwort

9

Soweit ich Ihre Frage verstehe, würde ich zustimmen, dass Option 4 die beste ist. Die Repository-Schnittstellen sollten in der Domänenebene neben allen Domänenobjekten deklariert sein. Die Implementierung dieser Schnittstellen sollte Teil der Infrastrukturschicht sein - der Schicht, die Ihre Domänenebene mit der Welt verbindet. Werfen Sie einen Blick auf die Hexagonal Architecture, um einige der Motivation dafür zu sehen.

Um das Problem von Option 4 zu lösen, sollten Sie nicht die Konsolen-App als reine Präsentationsschicht betrachten. Es hat auch andere Verantwortlichkeiten, wie zum Beispiel der Host für die Anwendung und die composition root in DI-Begriffe. Es könnte eine Präsentationskomponente der Konsolen-App geben, die nur mit Anwendungsdiensten kommuniziert. Sie können den Anwendungsdienst auch hinter einem mit ASP.NET-WebAPI implementierten open host service kapseln. Dann würde die Präsentationsschicht nur auf diesen Dienst verweisen und von der darunter liegenden Domänenebene ausgeblendet werden.

+0

Basierend auf Ihrer Antwort auf meine vorherige Frage und jetzt diese, sieht aus wie ich Ihnen einen Kaffee oder ein Steak oder etwas schulden werde. :) Ich habe eine schnelle Folge ... Sie haben geschrieben "Von dem, was ich von Ihrer Frage verstehe" ... zugegebenermaßen war es langatmig. Gab es eine Terminologie oder Gedanken-Prozess-weise, die ebenso schief klang? DANKE für deine großartige Antwort ... schon wieder. –

+0

Was ich damit meinte ist, dass mir nicht alle Details Ihres Projekts bekannt sind, so dass ich eine Einschränkung übersehen habe. Aber trotzdem eine sehr gut gestellte Frage! – eulerfx

0

Ich stimme mit eulerfx überein. Die einzige Sache, die ich hinzufügen würde, ist, dass es sich nicht richtig anfühlt, weil traditionelle N-Tier-Architektur Sie dazu bringen würde, alles von der Datenbank abhängig zu machen und Abstraktionen zu verwenden, um diese Abhängigkeiten zu brechen.

Sie sollten sich Onion Architecture ansehen, ein Beispiel für die Art und Weise, wie Sie Ihre Abhängigkeiten in Option 4 organisieren. Ich persönlich glaube, dass dies das am besten skalierbare Architekturmodell ist. Wenn Sie diese Architektur mit DDD-Praktiken kombinieren, erhalten Sie mit geringstem Aufwand die größtmögliche Flexibilität.

Nebenbei bemerkt, viele Implementierungen, die ich gesehen habe, brechen die Verträge für die Repositories und Domain-Dienste in ein eigenes Projekt. Dies ist jedoch eine rein organisatorische Entscheidung. Es ist absolut zulässig, sie in Ihrem Domänenmodellprojekt zu haben.

+0

FYI die Zwiebelarchitektur kann im Wesentlichen als die gleiche hexagonale betrachtet werden; einige halten es für eine Abzocke, da hexagonale Umrisse einige Zeit vor der Zwiebel waren. – eulerfx

+0

Ich sehe was du meinst, danke für die Information. Warum beschreibt kein Architekturmodell die Abhängigkeit zwischen UI/Anwendung/Infrastruktur? Wenn Sie auf Ihre Antwort Bezug nehmen, liegt es in der Verantwortung der Anwendung zu wählen, welche Infrastruk- turimplementierung zu verwenden ist, nur weil dies nicht in den Bereich der Ergebnisse dieser Modelle fällt oder gibt es einen anderen Grund, dies aus den Architekturmodellen herauszulassen. Ich erkenne, dass Sie die Modelle nicht erstellt haben; Ich wundere mich nur über Ihre Meinung dazu. –

+0

Im Sechseck ist die UI nur ein weiterer Adapter und kann als Infrastruktur angesehen werden. Vielleicht habe ich missverstanden, was du gefragt hast? Auf welche Abhängigkeit zwischen UI/App/Infrastruktur beziehen Sie sich? Es gibt ein paar Perspektiven dazu, die ich sehe. – eulerfx