Implementing Generic Repositories in Quasi-DDD Applications

Over the last several weeks I have been working to build a basic re-usable CRUD type repository interface useful for anemic CRUD domain models but that allows for extensions that can potentially be used with richer domain models like the ones you get while implementing Domain Driven Design (DDD). As per DDD, you ought to have a repository per aggregate and that repository should only expose domain specific use case APIs.

While my design may not be perfect because its still a work in progress, I think it should still form a good starting point for anyone wanting to build a similar architecture considering that there are multitude of ways on the internet people have implemented the repository pattern along with their own implementation of Unit of Work pattern.

The code I show here is in C# using .NET Core 1.1 and the ORM the implementation of this interface will use is Entity Framework Core.

The base repository interface:

 public interface IRepository<T>
 {
     void Add(T entity);
 
     void Update(Guid id, T updatedEntity);
 
     void Delete(Guid id);
 
     IEnumerable<T> Get(Expression<Func<T, bool>> predicate);
 
     T GetOneById(Guid id);
 
     IEnumerable<T> Get(Expression<Func<T, bool>> predicate, 
						int currentPageIndex, 
						int pageSize, 
						out int totalNumberOfRecords, 
						out int totalNumberOfPages);
 }

The last method is an overload providing server side paging functionality which is useful when loading large datasets in thin client application like a web application.

As such you could implement this interface in a generic repository to quickly provide your application basic CRUD. However, since I have already mentioned that I am going to be using Entity Framework as an ORM, I have also created a derived interface specifically to work with EF. This interface supports loading of related entities in an eager loading fashion which is going to be tricky if you only have base implementation of the interface since you won’t know the type of entity until run time.

public interface IEntityFrameworkRepository<T> : IRepository<T>
{        
    IEnumerable<T> Get(Expression<Func<T, bool>> predicate, 
                       NavigationPropertyCollection navigationPropertyCollection);
 
    IEnumerable<T> Get(Expression<Func<T, bool>> predicate, 
		       int currentPageIndex, 
		       int pageSize, 
		       out int totalNumberOfRecords, 
		       out int totalNumberOfPages, 
		       NavigationPropertyCollection navigationPropertyCollection);
    
    T GetOneById(Guid id, 
                 NavigationPropertyCollection navigationPropertyCollection);
}

NavigationPropertyCollection and NavigationProperty classes are my own hacky inventions that allow you specify which related entities to load along with the aggregate root. For e.g. If I want to load all the Posts and Comments associated with a Blog, I will construct the NavigationPropertyCollection like so:

NavigationPropertyCollection navPropCollection = new NavigationPropertyCollection();
NavigationProperty navProp = new NavigationProperty(nameof(Blog.Posts));
navProp.NestProperty(nameof(Post.Comments));

And pass this collection to the corresponding Get overload of the IEntityFrameworkRepository instance. I have also created a helper class that will then internally translate this NavigationPropertyCollection to IQueryable with appropriately nested “.Include(…)” calls. So in the above example the IQueryable returned would look like

.Include("Posts.Comments")

Basically, this is just a hack to inject the eager loadable property names into my generic repository class at runtime without saying anything about the type of entity I am loading or using any magic strings for property names. I can decide which properties to load based on my use case, in my application services layer which is privy to the repository without leaking that concern to the controllers or the views, if we use ASP.NET MVC. This just helps keep the Separation of Concerns.

This is what the NavigationPropertyCollection class looks like (doesn’t inherit from a collection, rather composes a collection within it):

public class NavigationPropertyCollection
{
    private List<NavigationProperty> properties;

    public NavigationPropertyCollection()
    {
        if (properties == null)
        {
            properties = new List<NavigationProperty>();
        }
    }
    
    public void AddProperty(string name)
    {
        if (string.IsNullOrWhiteSpace(name))
        {
            throw new ArgumentNullException("Navigation property must have a name");
        }

        this.AddProperty(new NavigationProperty(name));
    }
    
    public void AddProperty(NavigationProperty navigationProperty)
    {
        if (navigationProperty == null)
        {
            throw new ArgumentNullException("Navigation property cannot be null!");
        }

        this.properties.Add(navigationProperty);
    }
    
    public IReadOnlyCollection<NavigationProperty> Properties { get => properties.AsReadOnly(); }
}

NavigationProperty looks like this:

public class NavigationProperty
{
    private NavigationProperty nestedProperty;
    
    public NavigationProperty(string name)
    {
        if (string.IsNullOrWhiteSpace(name))
        {
            throw new ArgumentNullException("Navigation property name cannot be null!");
        }

        this.Name = name;
    }

    /// <summary>
    /// Nest a <see cref="NavigationProperty"/> with a given name under another <see cref="NavigationProperty"/>.
    /// For e.g. if Post entity contains Comments as a navigation property then,
    /// NavigationProperty navProp = new NavigationProperty("Posts");
    /// navProp.NestProperty("Comments");
    /// The <see cref="NestProperty(string)"/> calls be chained together to add multiple levels of nesting.
    /// navProp.NestProperty("A").NestProperty("B").NestProperty("C")...
    /// </summary>
    /// <param name="name">The <see cref="string"/> name of the property to be nested.</param>
    /// <returns><see cref="NavigationProperty"/> so more properties can be chained together if needed.</returns>
    public NavigationProperty NestProperty(string name)
    {
        nestedProperty = new NavigationProperty(name);

        return nestedProperty;
    }

    public string Name { get; }

    public NavigationProperty NestedProperty { get => nestedProperty; }
}

A generic implementation of this derived repo interface that can be used across all entity types looks like this (IEntity just makes sure every entity has an id):

public class Repository<T> : IEntityFrameworkRepository<T> where T : class, IEntity
{
    private DbContext context;

    public Repository(DbContext context)
    {
        this.context = context;
    }

    public void Add(T entity)
    {
        this.context.Set<T>().Add(entity);            
        this.context.SaveChanges();
    }

    public void Update(Guid id, T updatedEntity)
    {
        this.InjectIdIntoUpdatedEntity(id, updatedEntity);
        this.context.Update(updatedEntity);
        this.context.SaveChanges();
    }

    public void Delete(Guid id)
    {
        var entityToBeRemoved = this.GetOneById(id);
        this.context.Set<T>().Remove(entityToBeRemoved);
        this.context.SaveChanges();
    }

    public IEnumerable<T> Get(Expression<Func<T, bool>> predicate)
    {
        return this.Get(predicate, null);
    }

    public IEnumerable<T> Get(Expression<Func<T, bool>> predicate, NavigationPropertyCollection navigationPropertyCollection)
    {
        IQueryable<T> query = this.GetPredicatedIQueryable(predicate);
        query = QueryableHelper<T>.BuildQueryFromNavigationPropertyCollection(query, navigationPropertyCollection);

        return query.ToList();
    }

    public IEnumerable<T> Get(Expression<Func<T, bool>> predicate, 
                              int currentPageIndex, 
                              int pageSize, 
                              out int totalNumberOfRecords, 
                              out int totalNumberOfPages)
    {            
        return this.Get(predicate, currentPageIndex, pageSize, out totalNumberOfRecords, out totalNumberOfPages, null);
    }

    public IEnumerable<T> Get(Expression<Func<T, bool>> predicate, 
                              int currentPageIndex, 
                              int pageSize, 
                              out int totalNumberOfRecords, 
                              out int totalNumberOfPages, 
                              NavigationPropertyCollection navigationPropertyCollection)
    {
        totalNumberOfRecords = this.context.Set<T>().AsNoTracking().Count(predicate);
        totalNumberOfPages = (int)Decimal.Ceiling((decimal)totalNumberOfRecords / pageSize);
        var predicatedQuery = this.GetPredicatedIQueryable(predicate);
        predicatedQuery = QueryableHelper<T>.BuildQueryFromNavigationPropertyCollection(predicatedQuery, navigationPropertyCollection);

        return predicatedQuery.Skip(currentPageIndex * pageSize).Take(pageSize).ToList();
    }

    public T GetOneById(Guid id)
    {
        return this.GetOneById(id, null);
    }

    public T GetOneById(Guid id, NavigationPropertyCollection navigationPropertyCollection)
    {
        return this.Get(x => x.Id == id, navigationPropertyCollection).FirstOrDefault();
    }        

    private IQueryable<T> GetPredicatedIQueryable(Expression<Func<T, bool>> predicate)
    {
        return this.context.Set<T>().Where(predicate);
    }

    private void InjectIdIntoUpdatedEntity(Guid id, T updatedEntity)
    {
        if (updatedEntity.Id == Guid.Empty)
        {
            typeof(T).GetProperty(nameof(IEntity.Id)).SetMethod.Invoke(updatedEntity, new object[] { id });
        }            
    }
}

One thing to note here, is that I don’t like to make my Id properties in my domain classes publicly settable so I mark them internal and then in the repository I use reflection to inject id into the entity during an update operation if it wasn’t already set by application services. I make the domain internals visible to my application services layer because they are what I like to call the “chaos coordinator”. I am sure DDD purists will have a lot to say on this and I agree with most of it anyway, its just a matter of how pure I want to be with my domain model and still be able to protect my invariants. In the case of CRUD application there usually won’t be many true invariants to protect anyway but other applications probably will. I have built a simple home expense tracking application using the principles of DDD and aggregates and I much rather prefer going that way but for CRUD applications it just seems a bit too much.

And finally, my QueryableHelper class looks like so:

public class QueryableHelper<T> where T : class
{
    public static IQueryable<T> BuildQueryFromNavigationPropertyCollection(IQueryable<T> query, NavigationPropertyCollection navigationPropertyCollection)
    {
        if (query != null)
        {
            if (navigationPropertyCollection != null)
            {
                foreach (var navProp in navigationPropertyCollection.Properties)
                {
                    if (navProp.NestedProperty != null)
                    {
                        string nestedNavigationPropertyNames = navProp.Name + ".";
                        var currentNestedNavProperty = navProp.NestedProperty;

                        while (currentNestedNavProperty != null)
                        {
                            nestedNavigationPropertyNames += currentNestedNavProperty.Name + ".";
                            currentNestedNavProperty = currentNestedNavProperty.NestedProperty;
                        }

                        // remove the last "." character
                        query = query.Include(nestedNavigationPropertyNames.Remove(nestedNavigationPropertyNames.Length - 1, 1));
                    }
                    else
                    {
                        query = query.Include(navProp.Name);
                    }
                }
            }
        }

        return query;
    }
}

Now I am very certain that there are holes in this design and I would appreciate any ideas to improve upon it, but this might be just one way to implement a CRUD based repository pattern with provision for extension for richer domain models. For e.g. I could create a domain object specific repository interface like so:

public interface IPostRepo
    {
        Dictionary<string, int> GetNumberofCommentsByPosts(Guid blogId);
    }

And then implement it in a class that also derives from Repository:

public class PostRepo : Repository<Post>, IPostRepo
{
    private readonly CrudContext context;

    public PostRepo(CrudContext context) 
        : base(context)
    {
        this.context = context;
    }

    public Dictionary<string, int> GetNumberofCommentsByPosts(Guid blogId)
    {
        Dictionary<string, int> commentsByPost = new Dictionary<string, int>();
        NavigationPropertyCollection navPropCollection = new NavigationPropertyCollection();
        navPropCollection.AddProperty(nameof(Post.Comments));
        var postsByBlogId = base.Get(x => x.BlogId == blogId, navPropCollection);

        foreach(var post in postsByBlogId)
        {
            commentsByPost.Add(post.Title, post.Comments.Count());
        }

        return commentsByPost;
    }

The obvious advantage is that I still retain all the CRUD operations which I can still call into from my application services abstraction layer. The application service layer can then take a dependency on IPostRepo instead of IRepository and get all the base benefits of CRUD operations but also provide domain use case specific methods that do not fit in a generic reusable repository class.

Notice that I haven’t bothered implementing an explicit UoW class, IMO that’s over engineering because the DbContext in Entity Framework already implements proper UoW pattern with change tracking and transaction management. Also, note that I haven’t made any effort to make this thread safe, infact, its not thread safe if you create a singleton instance of the DbContext which by design is not thread safe itself. In a web application scenario to avoid problems with concurrent updates and deadlocking, the scope of the DbContext should only span a single web request which would ideally execute a single use case/transaction in a request thus keeping the scope relatively short lived enough not to cause thread contentions. If you use ASP.NET Core, this is set-up by default when you register your services with the Dependency Injection container in the Startup class.

Aren’t repositories fun? 🙂

Tagged with: , , , , , , , , , , , , , , , , , , , ,
Posted in API, architecture, design, ASP.NET Core, best practices, domain driven design, Over Engineering, refactoring, software architecture, SOLID, SOLID principles, Uncategorized

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Categories
Follow Artineering on WordPress.com
Links
%d bloggers like this: