Where to define the interfaces for a repository in an layered architecture?
Asked Answered
D

2

19

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:

  1. SimpleOrder.ConsoleClient (presentation) -- I want to inject the specific implementation of the domain from here as the application

  2. SimpleOrder.ApplicationFacade (application services) -- chunky higher-level, coarser-grained methods orchestrating lower-level methods in the domain

  3. SimpleOrder.Contracts -- DTO classes used for communication between presentation and application services

  4. SimpleOrder.Domain (domain / bll) -- domain model classes Customer, Order, OrderItem, Product

  5. 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?

Denominational answered 12/12, 2012 at 18:25 Comment(1)
Upon closer examination ... options 3 & 4 are basically the same. Oops. I should have proof-read more closely.Denominational
M
18

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.

Montemontefiascone answered 12/12, 2012 at 18:45 Comment(2)
Based on your answer to my previous question and now this one, looks like I'm going to owe you a coffee or a steak or something. :) I do have one quick follow-up ... you wrote "From what I understand of your question" ... admittedly, it was long winded. Was there something terminology or thought-process-wise that sounded askew as well? THANK YOU for your great answer ... again.Denominational
What I meant is that I'm not aware of all the details of your project, so I may have missed a constraint. But a very well stated question nonetheless!Montemontefiascone
L
0

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.

Lippi answered 18/12, 2012 at 18:20 Comment(4)
FYI the onion architecture can be regarded to be essentially the same thing as hexagonal; some consider it a ripoff since hexagonal was outlines some time before onion.Montemontefiascone
I see what you mean, thank you for the information. Why is it that neither architecture model describes the dependency between UI/Application/Infrastructure? Referencing your answer, the Application has the responsibility to choose what Infrastructure implementation to use so is it just because this falls outside the scope of what they are trying to express with these models or is there another reason for leaving this out of the architecture models? I realize that you did not create the models; I am only wondering about your opinion on this.Lippi
In hexagonal, the UI is just another adapter and can be regarded as infrastructure. Perhaps I misunderstood what you asked? What dependency between UI/App/Infrastructure are you referring to? There are a few perspectives on this that I see.Montemontefiascone
Generally, there is an architectural dependency between the client application and the infrastructure decisions. For example, a mobile app may use MySQL while a windows app may use SQL Express and a Web app may use full blown SQL. Even if you're exposing the client interface through an API (just to give an example where the app is not a UI), it always drives the back-end implementation decisions. However, none of these architectural models describe this inherit dependency which causes people to become confused when trying to follow a particular model.Lippi

© 2022 - 2024 — McMap. All rights reserved.