CQRS with Repository pattern and Inversion of Control (with DI)

cqrsinversion-of-controln-tierrepository-pattern

I assigned a POC project to someone where I asked to implement both Command Query Responsibility Segregation, Inversion of Control (with Dependency Injection) and Repository pattern. “Someone” gave me a POC solution project but I am not sure whether this is the way it is done. I will brief here about the POC project

  • The project is a simple 3-tier application – the Presentation Layer (PL), the Business Logic Layer (BLL) and the Data Access Layer (DAL); each tier being a separate project
  • The Presentation Layer is a Web Application, the BLL and DAL are class library projects
  • In the Business Layer, there are defined Repository Interfaces. The reference of BLL library is added to DAL project and inside the DAL project there are concrete classes that implement the Repository Interfaces. This is how Inversion of Control is applied
  • Since Command-Query-Responsibility-Segregation is done, the repository interfaces in the Business Layer only declare Add/Update and Delete methods. For read, there are “Read” interfaces directly in the DAL and in the DAL there are concrete classes that implement these interfaces.
  • The Presentation Layer contains reference to both the BLL library and the DAL library. Calls to Add/Update/Delete are routed through the BLL to the DAL while any read is done directly from the DAL. I believe this conforms to Command-Query-Responsibility-Segregation concept of bypassing the BLL for doing reads.

Here is an illustration of how this is all setup. There are three projects

  • NW.Web
  • NW.Business
  • NW.DataAccess

Below is a snapshot of code in the different layers.

— NW.Web —

// A class in the Presentation Layer
public class CustomerPage 
{

    // Business layer Interface from  NW.Business namespace
    private ICustomerBusiness ICustB;

    //DAL Read interface from NW.DataAccess.Read namepsace
    private ICustomerRead<Guid> ICustR;

    //Constructor for the Customer Page that uses Constructor Injection
  public CustomerPage(ICustomerBusiness ICustB, ICustomerRead<Guid> ICustR)
    {
        this.ICustB = ICustB;
        this.ICustR = ICustR;
    }
}

— NW.Business —

//Declaration of business interface in the Business Layer
interface ICustomerBusiness
{
    void Persist();
}

// A class in the Business Layer that implements the business interface
public class Customer: ICustomerBusiness 
{
    //Repository interface object that will be injected by Constructor Injection.
    private ICustomerRepository ICustRep;

    public Customer(ICustomerRepository ICustRep)
    {
        this.ICustRep = ICustRep;
    }

    public void Persist()
    {

            ICustRep.AddOrUpdate();

    }
}

//Declaration of Repository interface in the Business Layer
public interface ICustomerRepository
{
    void AddOrUpdate();
    void Delete();
}

— NW.DataAccess–

public class CustomerRepository : ICustomerRepository
{

    public void AddOrUpdate()
    {
        //implementation of Add or Update
    }

    public void Delete()
    {
        //implementation of Delete
    }
}

//A Read interface in the Data Access Layer
interface ICustomerRead<T>
{
   // A read is returned as DTO since in Database this may map to more than 1 table
    CustomerDTO GetCustomerDetails(T id);
}

// An implementation of the Read Interface in the Data Access Layer
namespace NW.DataAccess.Read
{
    public class CustomerRead<T> : ICustomerRead<T> 
    {

        public CustomerDTO GetCustomerDetails(T id)
        {
           //implementation here
        }
    }
}

My gut feeling is that there is something wrong here. It seems CQRS or at least the above implementation does not address some requirements

  • The Customer Business object (Customer class) may need to read from Database for its internal purpose (like initializing variables etc). With read directly defined in DAL layer, the only way to do this would be to reference the DAL dll in the BLL. But this would create circular reference and go against the IOC that is done
  • What happens when there is some common Read requirement across all business objects ?

Best Answer

I think you need to create an interface in the BLL that holds your connection (datacontext, sqlConnection, or whatever). That concrete class would then go into the DAL. On initialization you would include the connection string.

When you do a data read function you would pass along the datacontext or whatever you used for the connection. You could also send the connection along upon initialization.

Does that make sense? You can look at the link below to get an idea on the data access interface.

http://vbsoftwaredeveloper.blogspot.com/2013/05/4-tier-ioc-application-converted-tier.html