Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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?

like image 761
Bob Tabor Avatar asked Dec 12 '12 18:12

Bob Tabor


1 Answers

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.

like image 82
eulerfx Avatar answered Oct 30 '22 12:10

eulerfx