Design – business logic code and what is data access code, and what’s the difference

designdomain-driven-design

I ask this because it seems people usually consider the code that goes into a DAO or Repository implementation as "data access code", while the code that directly uses these DAOs/Repositories as "business logic code".

However, this doesn't seem to make logical sense.

Consider a business service method with code like the following (using Java, but it would be similar in C#, etc.):

public BigDecimal computeCustomerTotal(Customer customer) {
    List<Order> orders = orderRepository.findByCustomer(customer);

    BigDecimal total = orders.stream()
        .filter(Order::isActive).map(Order::getTotal).reduce(ZERO, BigDecimal::add);

    return total;
}

Sure, the above implementation doesn't make "proper" use of a database's capabilities, but bear with me. The point here is that, I assume, everyone agrees it contains only business logic code, with Customer and Order being domain entities (ie, they are not "data" objects).

So, what if I want to make it right, by moving the whole computation to the database, instead of executing in the client program? If I follow "conventional wisdom", I would create a new method in the repository:

public BigDecimal getCustomerTotal(Customer customer) {
    BigDecimal total = performQuery(
        "select sum(o.total) from Order o where o.customer = ?1 and o.active",
        customer);

    return total;
}

(Assume performQuery is a JPA-based utility method which takes a JPA-QL
sentence with optional arguments.)

The second implementation is obviously better than the first, as it probably performs better, and makes proper use of the underlying technologies (JPA/Hibernate, and a relational database).

The problem, though, is that business logic (the rule that only active orders should be considered) has been moved from a business service method to a (supposed) data access method, through a simple implementation change.
Also, this code which is now in the DAO/Repository does not actually know about data access concerns, such as the mapping from entity types and attributes to tables and columns, or about the actual SQL code that gets generated and sent to the DB engine.

Wouldn't it be more logical to regard both methods as containing only business logic code, and just get rid of the DAO/Repository/DAL altogether?

Personally, I don't see any data access code there, and to me things like DAOs, Repositories, or "data access layers" (DAL) are in reality anti-patterns. Note that even when using JDBC + SQL in a DAO/Repository implementation, it will inevitably contain business logic, typically in the form of "where" clauses.

Best Answer

The database layer is intended to isolate the rest of the application from the details of the database -- how to make a connection, the syntax used to talk to the db engine, etc.

The .Net Entity Framework version of your code would be:

var id = customer.id;
var customerOrdersTotal = db.Orders.
                          Where(o => o.CustomerId == id && o.Active).
                          Sum(o => o.Total);

Note that this would get translated into the sql your JPA version would use, but the exact same thing could be done without involving sql at all.

If you have a Customer that has Orders, then it would be:

var customerOrdersTotal = customer.Orders.
                          Where(o => o.Active).Sum(o => o.Total);

And this version could be written regardless of whether Orders is a virtual list or an actual list of orders.

The business doesn't care where the data is kept or how it is retrieved. The point of a DAL is to abstract away the "how is this bit of data retrieved" and turn it into just the necessary (i.e. unavoidable) "how to request the data I want". Without a DAL you have to mix the two functions together, which means you can't change one without changing the other.

Note that about half the time, you don't ever end up making any changes...but when you do it's a real pain if they are mixed together.

Edit: I'll add that your implication that "where" clauses in the data access code is a business concern is incorrect. The data layer exists to serve business needs. The data can be carved up and served in an infinite number of ways, some more useful than others. Moving a condition from the business layer to the data layer simply means that the request is common enough to be included in the ways that are usefully provided out of the box. Preventing new orders when existing active orders exceeds $X is a business decision.

Related Topic