Search code examples
architecturedomain-driven-designcqrsbusiness-logic

CQRS: business logic on the query side


Following the concept of CQRS (Command Query Responsibility Segregation), I am directly referring the DAL in my MVC application and doing all reads via the ViewModels. However a colleague of mine is asking me what will you do when any business logic has to be applied when doing a read. For e.g. if you need to compute a percentage value in scenario like below:

//Employee domain object
class Employee
{
    string EmpName;
    Single Wages;
}

//Constant declared in some utility class. This could be stored in DB also.
const Single Tax = 15;

//View Model for the Employee Screen
class EmployeeViewModel
{
    string EmpName;
    Single GrossWages;
    Single NetWages;
}


// Read Facade defined in the DAL
class ReadModel
{
    List<EmployeeViewModel> GetEmployeeList()
    {
        List<EmployeeViewModel> empList = new List<EmployeeViewModel>;
        string query = "SELECT EMP_NAME, WAGES FROM EMPLOYEE";      
        ...
        ..
        while(reader.Read())
        {
            empList.Add(
                new EmployeeViewModel 
                {
                    EmpName = reader["EMP_NAME"],
                    GrossWages = reader["WAGES"],
                    NetWages = reader["WAGES"] - (reader["WAGES"]*Tax)/100 /*We could call a function here but since we are not using the business layer, the function will be defined in the DAL layer*/
                }
            );
        }
    }   
}

In above example, there is a calcuation occuring during the read which is occuring in the DAL layer. We could have created a function to do the calculation but again since we have bypassed the business layer for our read, the function will be located in the DAL. Even worse, someone might do it directly in the DB in a stored proc if the value of Tax is stored in the DB. So we have a potential leakage of business logic here in other layers.

You might say why don't you store the computed value in a column while doing the command. So let us change the scenario a bit. Let us say you are showing the potential Net Wages for the employee in a report with the current Tax rate and the Wages are yet to be paid.
How would you handle this in CQRS ?


Solution

  • Please take into consideration that reporting can be a whole Bounded Context in its own right. Therefore its architecture can be completely different from the one you chose for your Core Domain.

    Maybe CQRS is a good fit for the Core Domain but not for the domain of reporting. Especially when you want to apply various calculations based on different scenarios prior to report generation. Think BI.

    Please remember that CQRS probably shouldn't be applied across your whole application. Once your application is complex enough you should identify its Bounded Contexts and apply an appropriate architectural pattern to each individually, even if they're using the same data source.