0
votes

I assigned a POC project to someone where I asked to implement both Command Query separation, 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-Separation 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-Separation 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 ?

Any suggestions ?

1
There is a great 4-hour CQRS course on Pluralsight.com. You can get a free Pluralsight day pass here and watch the video.Steven
This will have to wait till week-off days since I will be highly occupied during the week. i.e. if I am not working on off days. But a big thanks to @Steven for the link!devanalyst
You are missing two words of the pattern: Command and Query. You got two words right though: Responsibility Segregation ;)jgauffin

1 Answers

0
votes

I think the general concept would be to have "Commands" and "Queries", and within those you can take dependencies on Repositories.

I have actually refactored an ASP.NET MVC project where each Controller depended on one or more repositories to get it's work done. When I refactored it, I created "Command" and "Query" objects for each specific action that needed to happen which greatly simplified each controller. Then in those commands and queries I used the existing repositories to perform the logic.

It may seem like extra effort, but the reason I did it was because the solution also had caching. The caching code was a mess, there were cache keys being added to and removed from all over the place. By separating all data access into commands and queries, then raising domain events when data was accessed or updated the cache management code could be simplified and separated out.

For an overview of how I implement a simple command/query separation please see my recent blog post on the topic which also has sample code and NuGet packages to help you get setup quicker: http://www.nootn.com.au/2013/03/command-query-separation-to-better.html

In particular to answer your question "What happens when there is some common Read requirement across all business objects": just imagine that the Query and Command classes I have there take some common "IRepository" dependency instead of a "DBContext" as my example does and that is how you can "share" that common read requirement across business objects.