R – How to avoid circular data-access object dependencies when you need to do lazy loading (and using an IOC Container)

Architecturedependency-injection

Note: The examples below are C# but this problem should not be specific to any language in particular.

So I am building an object domain using a variant of the S# Architecture. For those unfamiliar with it, and to save you some reading time the idea is simply that you have a Data Access Object Interface for each of your domain objects that is responsible for loading to/from the persistence layer. Everything that might ever need to load/save a given object then accepts that object's data access interface as a dependency. So for example we can have the following where a product will lazy load the customer that purchased it as needed:

public class Product {
  private ICustomerDao _customerDao;
  private Customer _customer;
  public Product(ICustomerDao customerDao) {_customerDao = customerDao;}
  public int ProductId {get; set;}
  public int CustomerId {get; set;}
  public Customer Customer {
        get{
            if(_customer == null) _customer = _customerDao.GetById(CustomerId);
            return _customer;
        }
}
public interface ICustomerDao {
   public Customer GetById(int id);
}

This is all well and good until you reach a situation where two objects need to be able to load each other. For example a many-to-one relationship where, as above, a product needs to be able to lazy load its customer, but also customer needs to be able to get a list of his products.

public class Customer {
  private IProductDao _productDao;
  private Product[] _products;
  public Customer(IProductDao  productDao) {_productDao = productDao;}
  public int CustomerId {get; set;}
  public Product[] Products {
        get{
            if(_products == null) _products = _productDao. GetAllForCustomer(this);
            return _products;
        }
}


public interface IProductDao {
   public Product[] GetAllForCustomer(Customer customer);
}

I know that this is a really common situation but I am relatively new at this. My stumbling block is what to do when implementing the Data Access Objects. Because a Customer has a dependency on IProductDao, the CustomerDao implementation must also, however the vice versa is also true and ProductDao must take a dependency on ICustomerDao.

public class CustomerDao : ICustomerDao {
      private IProductDao _productDao;
      public CustomerDao(IProductDao  productDao) {_productDao = productDao;}
      public Customer GetById(int id) {
          Customer c = new Customer(_customerDao);
          // Query the database and fill out CustomerId
          return c;
      }
 }
public class ProductDao : IProductDao {
      private ICustomerDao _customerDao;
      public ProductDao (ICustomerDao customerDao) {_customerDao = customerDao;}
      public Product[] GetAllForCustomer(Customer customer) {
          // you get the idea
      }
 }

And here we have the problem. You cannot instantiate CustomerDao without an IProductDao and vice versa. My inversion of control container (Castle Windsor) hits the circular dependency and chokes.

I have come up with a for-the-time-being solution which involves lazy loading the DAO objects themselves (I will post this as an answer) but I don't like it. What are the time-tested solutions to this problem?

EDIT: The above is a simplification of the architecture I'm actually using and I am not recommending someone actually pass DAOs to an object. A better implementation closer to what I am actually doing is similar to the way NHibernate works where the actual objects are very simple and the above are actually proxy objects which inherit and override the appropriate fields.

Best Answer

As the other posters have suggested, you might want to rethink your architecture - it looks like you are making things hard on yourself.

Also note:

By changing the data access object dependencies to properties rather than constructor dependencies Windsor will autofill them after instantiating each object fully.

Be careful. In doing this, you've basically told Windsor that these dependencies are optional (unlike dependencies injected via the constructor). This seems like a bad idea, unless these dependencies are truly optional. If Windsor can't fulfill a required dependency, you want it to puke.

Related Topic