Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What Belongs in a Repository and What Doesn't?

In an ASP.NET MVC application implementing the repository pattern I am curious if it is appropriate to place non-data related methods in the repository if they still pertain to the general focus of a given repository. For example, suppose a ProductsRepository that has methods for adding and deleting ProductImages which have a partial representation in the database and in the local file store as well. If a ProductImage needs to be deleted we need to delete a row from the database with a repository method and we also need to delete the files associated with that image from the storage medium. Do the IO operations belong in the repository, or is there a more appropriate location?

One thing I have been doing in a situation like the one I just described is providing static methods in my repository which give me the path to a given ProductImage by using the filename stored in the database and a pre-defined directory pattern to programatically generate it. Is this outside the intended use of a repository?


Edit

If such an operation does not belong in the repository, where should something like that go in an MVC pattern? It seems to me that it might make sense to have another layer between the Controller and the Repository that calls the Repository as needed and can be invoked statically from the Controller.

like image 717
Nathan Taylor Avatar asked Aug 19 '09 15:08

Nathan Taylor


2 Answers

I think the bigger concern over the repository pattern is the fact that you are violating single responsibility principle. Your class should have one responsibility, such as manipulating data in the database. You should have a different class deal with the File IO, and you can group the functions in a class one layer up.

There should only be one reason for a class to change, and a repository class that handles file IO and db calls will have two. A change to the file system layout, or a change to the database.

Edit

To address your edit question, here is how I would implement this in an MVC scenario (this is also assuming you are using some kind of dependency injection to make life easier).

// Controller class
public class ProductsController
{
    private IProductService _productService;

    public ProductsController(IProductService productService)
    {
        _productService = productService
    }

    public void RemoveImage(int productId, int imageId)
    {
        _productService.RemoveImage(productId, imageId)
    }
}

public class ProductService: IProductService
{
    private IProductRepository _productRepository;
    private IProductImageManager _imageManager;

    public ProductService(IProductRepository productRepository, IProductImageManager imageManager)
    {
      _productRepository = productRepository;
      _imageManager = imageManager;
    }

    public void RemoveImage(int productId, int imageId)
    {
        // assume some details about locating the image are in the data store
        var details = _productRepository.GetProductImageDetails(productId, imageId);
        // TODO: error handling, when not found?
        _imageManager.DeleteImage(details.location);
        _productRepository.DeleteImage(productId, imageId)
    }
}

Then you implement the IProductImageManager and the IProductRepository based on whatever interface makes sense with concrete implementations for your specific needs.

like image 67
NerdFury Avatar answered Sep 18 '22 10:09

NerdFury


I recently designed a new repository and struggled with this same question. I ended up including my additional methods in the repository.

Looking back at it now though, I feel that a better solution would have been to keep my repository more focused, and to have put my additional methods into a Service that integrated closely with my repository.

For your example above, you could then have a "DeleteProductImage" method in ProductsService that would call ProductsRepository.DeleteImage & then also handle deleting the images from the storage medium.

This keeps your Repository clean and focused on just the "DeleteImage" logic, while still giving you a single method you need to call ("DeleteProductImage") which takes care of calling the repository to delete the image while also handling interacting with the storage medium and any other things that may need to happen when an Image is deleted that aren't directly related to your repository.

like image 37
Kwen Avatar answered Sep 18 '22 10:09

Kwen