Archive | September 2010

Does Queryability and Lazy Loading in C# blur the lines of Data Access vs Business Logic?


I am experiencing a mid-career philosophical architectural crisis. I see the very clear lines between what is considered client code (UI, Web Services, MVC, MVP, etc) and the Service Layer. The lines from the Service layer back, though, are getting more blurred by the minute. And it all started with the ability to query code with Linq and the concept of Lazy loading.

I have created a Business Layer that consists of Contracts and Implementations. The Implementations then could have dependencies to other Contracts and so on. This is handled via an IoC Container with DI. There is one service that handles the DataAccess and all it does is return a UnitOfWork. This UnitOfWork creates a transaction when extantiated and commits the data on the Commit method. [View this Article (Testability and Entity Framework 4.0)]:

    public interface IUnitOfWork : IDisposable {
        IRepository<T> GetRepository<T>() where T : class;
        void Commit();
    }

The Repository is generic and works against two implementations (EF4 and an in-memory DataStore). T is made up of POCOs that get generated from the database schema or some EF4 object mapped from multiple DB objects. Testability is built into the Repository design. We can leverage the in-memory implementation to assert results with expectations since we are abstracting the data access query generator.

    public interface IRepository<T> where T : class {
        IQueryable<T> Table { get; }
        void Add(T entity);
        void Remove(T entity);
    }

While the data source is abstracted, IQueryable still gives me the ability to create queries anywhere I want within the business logic layer. Here is an example.

    public interface IFoo {
        Bar[] GetAll();
    }

    public class FooImpl : IFoo {
        IDataAccess _dataAccess;
        public FooImpl(IDataAccess dataAccess) {
            _dataAccess = dataAccess;
        }

        public Bar[] GetAll() {
            Bar[] output;
            using (var work = _dataAccess.DoWork()) {
                output = work.GetRepository<Bar>().Table.Where(b => b.IsActive == true).ToArray();
            }
            return output;
        }
    }

Now you can see how the queries could get even more complex as you perform joins with complex filters.

Therefore, my questions is:

  1. Is queryability considered data access or business logic when behind a repository layer that acts like an in-memory abstraction?

This article was originally a question posted on StackOverflow.