问题
Background
I'm trying to create a simple application to really understand the whole stack of DDD+TDD+etc. My goal is to dynamically inject the DAL repository classes at runtime. This keeps my Domain and Application Services layers testable. I plan on using "poor man's DI" to accomplish this for now ... so I would do this in a simple Console application near startup:
// 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);
To accomplish this dependency injection, I've created three repository interfaces:
-- ICustomerRepository -- IOrderRepository -- IProductRespository
A typical implementation:
namespace SimpleOrder.Domain.Interfaces { public interface ICustomerRepository { Customer GetCustomerById(int customerId); void SaveCustomer(Customer customer); } }
** Notice that SaveCustomer references the Customer model class defined in the domain layer. This is typical of the other repositories.
HOWEVER I'm not sure which project / layer they should be implemented in. I have 5 projects in a solution:
SimpleOrder.ConsoleClient (presentation) -- I want to inject the specific implementation of the domain from here as the application
SimpleOrder.ApplicationFacade (application services) -- chunky higher-level, coarser-grained methods orchestrating lower-level methods in the domain
SimpleOrder.Contracts -- DTO classes used for communication between presentation and application services
SimpleOrder.Domain (domain / bll) -- domain model classes Customer, Order, OrderItem, Product
SimpleOrder.Repository (dal) -- implements the repository interfaces
Here are my options as I see it:
Option 1: Define the repository interfaces in SimpleOrder.Contracts ...
PRO: this is where I think they should belong because I created this to share contracts between various concerns / layers. ex., DTOs are defined here.
CON: however the method signatures in of each interface references Domain model classes.
This means that I would have to add a reference to the SimpleOrder.Domain, but when the
SimpleOrder.Contracts is referenced in another project, it will have to carry
SimpleOrder.Domain along for the ride. This doesn't feel right.
Option 2: Same scenario as above, but I ALSO define interfaces for each Domain model class in the SimpleOrder.Contracts so I can break the coupling of the repository interfaces to the actual model classes.
Example:
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: Each domain model class would have to implement his related interface. i.e.,
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: Fixes Option 1's problem.
CON: This explodes the number of files (and the perceived complexity) in the project because each domain class now has an associated interface.
Option 3: Define the repository intefaces in the SimpleOrder.Domain
IMPACT: In order to inject the concrete repository classes into the application services layer (SimpleOrder.ApplicationFacade project) from the SimpleOrder.ConsoleClient at runtime, SimpleOder.ConsoleClient will ALSO need a reference to SimpleOrder.Domain.
PRO: This ALSO solves Option 1
CON: I was trying to avoid referencing the domain layer from the presentation layer directly because now the presentation layer can know too much about the domain layer. When I replace the console application in the future with a WPF or ASP.NET MVC app in the future, I risk second and subsequent presentation layer implementations from attempting to call methods in the Model instead of the Application Services layer. (However I do consider this in Option 4.)
Option 4: Put the interfaces in SimpleOrder.Domain, then reference the SimpleOrder.Domain from the SimpleOrder.ConsoleClient.
PRO: Fixes all the problems above.
CON: This doesn't feel right because I would be providing access from the presentation layer directly to the lower-level methods in the Domain layer when I should only be providing access to the higher-level chunky methods in the SimpleOrder.ApplicationFacade.
QUESTION I've tried each of these, but have settled on Option 4, HOWEVER that leaves a bad taste in my mouth about it. Is there a better option? Am I on the right track here?
回答1:
From what I understand of your question, I would agree that option 4 is the best. The repository interfaces should be declared in the domain layer next to all of the domain objects. The implementation of said interfaces should be part of the infrastructure layer - the layer that connects your domain layer to the world. Take a look at the Hexagonal Architecture to see some of the motivation for this.
To address the con of option 4, you shouldn't think of the console app as being solely the presentation layer. It also has other responsibilities, such as being the host for the application and the composition root in DI terms. There could be a presentation component of the console app which only communicates with application services. You can also encapsulate the application service behind an open host service implemented with ASP.NET WebAPI. Then the presentation layer would only reference this service and be hidden from the underlying domain layer.
回答2:
I agree with eulerfx. The only thing I would add to it is that it doesn't feel right because traditional N-tier architecture would have you make everything depend upon the database and use abstractions to break those dependencies.
You should look at Onion Architecture which is an example of the way you are organizing your dependencies in option 4. I, personally, believe this is the most scalable architecture model available. If you couple this architecture with DDD practices, you gain the most flexibility with the least amount of effort.
On a side note, many implementations I have seen break out the contracts for the repositories and domain services into its own project. This is purely an organizational decision though. It is perfectly valid to have them within your domain model project.
来源:https://stackoverflow.com/questions/13846209/where-to-define-the-interfaces-for-a-repository-in-an-layered-architecture