SunnyPatelUK
SunnyPatelUK

Reputation: 37

Generic Repository Pattern - DDD Implementation

I am currently trying to implement a simple Blog application for learning purposes. Its within a DDD architecture.
My concern is about how to implement the generic repository pattern.
Could you guys give me your thoughts on if I am implementing the repository correctly.
This is my first time using generic repo's and looks like I'm not using it at all.
Below shows my implementation of my user repository.
Many Thanks

public interface IRepository<TEntity> where TEntity : class
{
    TEntity GetById(int id);
    IEnumerable<TEntity> GetAll();
    IEnumerable<TEntity> Find(Expression<Func<TEntity, bool>> predicate);
    void Add(TEntity entity);
    void Update(TEntity entity);
    void Remove(TEntity entity);
}

//Implementation of Generic Repo

using BA.Infrastructure.Data.Context;
using BA.Infrastructure.Data.Interfaces.Helpers;
using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Linq;
using System.Linq.Expressions;

namespace BA.Infrastructure.Data.Repositories.Helpers
{
    public class Repository<TEntity> : IRepository<TEntity> where TEntity : class
    {
        protected readonly BlogDbContext _context;

        public Repository(BlogDbContext context)
        {
            _context = context;
        }

        public TEntity GetById(int id)
        {
            return _context.Set<TEntity>().Find(id);
        }

        public IEnumerable<TEntity> GetAll()
        {
            return _context.Set<TEntity>().ToList();
        }

        public IEnumerable<TEntity> Find(Expression<Func<TEntity, bool>> predicate)
        {
            return _context.Set<TEntity>().Where(predicate);
        }

        public void Add(TEntity entity)
        {
            _context.Set<TEntity>().Add(entity);
        }

        public void Update(TEntity entity)
        {
            _context.Entry(entity).State = EntityState.Modified;
        }

        public void Remove(TEntity entity)
        {
            _context.Set<TEntity>().Remove(entity);
        }
    }
}

//unit of work/implementation

using System;

namespace BA.Infrastructure.Data.Interfaces.Helpers
{
    public interface IUnitOfWork : IDisposable
    {
        IBlogRepository Blogs { get; }
        ICategoryRepository Categories { get; }
        ICommentRepository Comments { get; }
        IUserRepository Users { get; }
        void Complete();
    }
}

using BA.Infrastructure.Data.Context;
using BA.Infrastructure.Data.Interfaces;
using BA.Infrastructure.Data.Interfaces.Helpers;

namespace BA.Infrastructure.Data.Repositories.Helpers
{
    //use unit of work within my service. 
    public class UnitOfWork : IUnitOfWork
    {
        private readonly BlogDbContext _context;

        public IBlogRepository Blogs { get; }
        public ICategoryRepository Categories { get; }
        public ICommentRepository Comments { get; }
        public IUserRepository Users { get; }


        public UnitOfWork(BlogDbContext context)
        {
            _context = context;
            Blogs = new BlogRepository(_context);
            Categories = new CategoryRepository(_context);
            Comments = new CommentRepository(_context);
            Users = new UserRepository(_context);
        }

        public void Complete()
        {
            _context.SaveChanges();
        }

        public void Dispose()
        {
            _context.Dispose();
        }
    }
}

//IUser Repository
using BA.Domains;
using BA.Infrastructure.Data.Interfaces.Helpers;

namespace BA.Infrastructure.Data.Interfaces
{
    public interface IUserRepository : IRepository<User>
    {
        User GetUser(int userId);
        void AddUser(User user);
        void UpdateUser(User user);
    }
}

//User Repository
using BA.Domains;
using BA.Infrastructure.Data.Context;
using BA.Infrastructure.Data.Interfaces;
using BA.Infrastructure.Data.Repositories.Helpers;
using System.Data.Entity;
using System.Linq;

namespace BA.Infrastructure.Data.Repositories
{
    public class UserRepository : Repository<User>, IUserRepository
    {
        public UserRepository(BlogDbContext context)
            : base(context)
        {
        }

        public User GetUser(int userId)
        {
            return _context.Users.FirstOrDefault(x => x.Id == userId);
        }

        public void AddUser(User user)
        {
            _context.Users.Add(user);
        }

        public void UpdateUser(User user)
        {
            _context.Entry(user).State = EntityState.Modified;
        }
    }
}

Upvotes: 4

Views: 3725

Answers (2)

Adam Vincent
Adam Vincent

Reputation: 3821

So I've been working on sort of the same problem and here's the answer I came up with.

It Depends.

In this specific case you only have four data models which are persisted through simple CRUD operations where no special domain behavior is required, so a generic repository would be applicable.

Now I'll split some hairs:

Are you implementing the Repository Pattern correctly? Yes.

(There's always a but) But, is your implementation 'correct'? I would say no.

"a repository is an in-memory collection of objects that additionally provides a pass-through for executing queries on a data store." (Julie Lerman)

So a DbSet<> is a repository, for entities of a single type. In this case, you only need to encapsulate your DbSet<>s enough to keep your persistence logic out of your domain. I think your current code satisfies this.

With the same idea that a DbSet<> is a repository, also, the DbContext is a UnitOfWork (UoW). Since your BlogDbContext is a single bounded context (Martin Fowler) wrapping the DbContext in a UnitOfWork would be redundant. So I would consider dropping the UoW completely. I would also consider the possibility of further separating the contexts. I know it's only 4 data models, but for argument sake.. Blogs don't care about the Users (assuming you're the only one posting blogs), as well as Users and Comments don't care about Categories. If you want to add more models you would need to start drawing boundaries

Additionally, if you wanted to extend beyond simple CRUD operations and for instance you wanted to filter your Blogs by Categories then your domain would be driving you in a different direction. If you were using the generic repository you might be executing some LINQ on the exposed IQueryable Find() in your delivery mechanism (assuming ASP Controller) which means you now have business logic in what is essentially your UI layer. In which case, you would want to move that logic back into your domain by using something more closely resembling VoiceOfUnreason's solution, where your Controller is calling a GetBlogsByCategory(string Category) method in your repository.

In a nutshell, go ahead and use the Repository Pattern as long as it's applicable, and when your domain's needs change, refactor!

Upvotes: 2

VoiceOfUnreason
VoiceOfUnreason

Reputation: 57249

Could you guys give me your thoughts on if I am implementing the repository correctly.

As noted in the comments, you probably don't want to be implementing the repository in that place.

In DDD, the repository abstraction represents a seam between how your domain model understands entity storage, and how your plumbling understands entity storage.

Greg Young, in 2009, wrote

What exactly was the intent of the repository pattern in the first place? Looking back to [DDD, Evans] one will see that it is to represent a series of objects as if they were a collection in memory so that the domain can be freed of persistence concerns. In other words the goal is to put collection semantics on the objects in persistence....

Simply put the contract of the Repository represents the contract of the domain to the persistence mechanism for the aggregate root that the repository supports.

The main idea is that the repository should be fit for purpose. The contract communicates what data is needed for the current use case, and the underlying plumbling provides it.

Jimmy Bogard used the term Role Specific Repository

Greg again:

the answer here is to still use a generic repository but to use composition instead of inheritance and not expose it to the domain as a contract.

More generally: Repositories are subject to the dependency inversion principle. The domain code defines interface that it needs provided, your implementation figures out the best way to do it, your composition root wires the two together.

So for your specific example, the contract should look like:

public interface IUserRepository
{
    User GetUser(int userId);
    void AddUser(User user);
    void UpdateUser(User user);
}

Notice that we don't share the common repository interface, because that's not something that the model needs.

An implementation that uses your generic repositories might look like

public class UserRepository : IUserRepository
{
    Repository<User> genericRepo;

    public UserRepository(Repository<User> genericRepo)
    {
        this.genericRepo = genericRepo;
    }

    public User GetUser(int userId)
    {
        return genericRepo.getById(userId);
    }

    // ...
 }

Of course, it would be equally correct to just provide an implementation wired directly into your DBContext

public class UserRepository : IUserRepository
{
    BlogDbContext context;

    public UserRepository(BlogDbContext context)
    {
        this.context = context;
    }

    public User GetUser(int userId)
    {
        return _context.Users.FirstOrDefault(x => x.Id == userId);
    }

    // ...
 }

Where the separation of the contract an the implementation tends to become useful is in supporting the different sorts of queries in your model.

Notice that the basic shape in both cases is the same: we've got an interface that defines the contract, an implementation that satisfies some other contract, and a thin adapter in the middle.

Upvotes: 6

Related Questions