Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Who's responsibility should be to paginate controller/domail service/repository?

My question might seem strange for pros but please take to account that I am coming from ruby on rails world =)

So, I am learning ASP.NET Core. And I like what I am seeing in it compared to rails. But there is always that but... Let me describe the theoretical problem.

Let's say I have a Product model. And there are over 9000 records in the database. It is obvious that I have to paginate them. I've read this article, but it seems to me that something is wrong here since the controller shouldn't use context directly. It has to use some repository (but that example might be provided in such a way only for simplicity).

So my question is: who should be responsible for pagination? Should it be the controller which will receive some queryable object from the repository and take only those records it needs? Or should it be my own business service which does the same? Or should the repository has a method like public IEnumerable<Product> ListProducts(int offset, int page)?

like image 202
hasrthur Avatar asked Feb 13 '17 09:02

hasrthur


People also ask

What is pagination used for?

Pagination is the process of separating print or digital content into discrete pages. For print documents and some online content, pagination also refers to the automated process of adding consecutive numbers to identify the sequential order of pages.

Is repository a service layer?

Repository layer is implemented to access the database and helps to extend the CRUD operations on the database. Whereas a service layer consists of the business logic of the application and may use the repository layer to implement certain logic involving the database.

What repository pattern should return?

Your repositories should return domain objects and the client of the repository can decide if it needs to do the mapping. By mapping the domain objects to view models (or something else) inside a repository, you prevent the client of your repositories from getting access to the underlying domain object.

What is service layer C#?

A service layer is an additional layer in an ASP.NET MVC application that mediates communication between a controller and repository layer. The service layer contains business logic. In particular, it contains validation logic.


2 Answers

One Domain-Driven-Design solution to this problem is to use a Specification. The Specification design pattern describes a query in an object. So you might create a PagedProduct specification which would take in any necessary parameters (pageSize, pageNumber, filter). Then one of your repository methods (usually a List() overload) would accept an ISpecification and would be able to produce the expected result given the specification. There are several benefits to this approach. The specification has a name (as opposed to just a bunch of LINQ) that you can reason about and discuss. It can be unit tested in isolation to ensure correctness. And it can easily be reused if you need the same behavior (say on an MVC View action and a Web API action).

I cover the Specification pattern in the Pluralsight Design Patterns Library.

like image 116
ssmith Avatar answered Nov 11 '22 20:11

ssmith


For first, I would like to remind you that all such examples you linked are overly simplified, so it shouldn't drive you to believe that that is the correct way. Simple things, with fewer abstraction layers are easier to oversee and understand (at least in the case of simple examples for beginners when the reader may not know where to look for what) and that's why they are presented like that.

Regarding the question: I would say none of the above. If I had to decide between them then I would say the service and/or the repository, but that depends on how you define your storage layer, etc.

"None of the above", then what? My preference is to implement an intermediary layer between the service layer and the Web UI layer. The service layer exposes manipulation functionality but for read operations, exposes the whole collection as an IQueryable, and not as an IEnumerable, so that you can utilize LINQ-to-whatever-storage.

Why am I doing this, many may ask. Because almost all the time you will use specialized viewmodels. To display the list of products on an admin page, for example, you would need to display values of columns in the products table, but you are very likely to need to display its category as well. Very rarely is it the case that you need data only from one table and by exposing the items as an IQueryable<T> you get the benefit of being able to do Selects like this:

public IEnumerable<ProductAdminTableViewModel> GetProducts(int page, int pageSize)
{
    backingQueryable.Select(prod => new ProductAdminTableViewModel
    {
        Id = prod.Id,
        Category = prod.Category.Name, // your provider will likely resolve this to a Join
        Name = prod.Name
    }).Skip((page - 1) * pageSize).Take(pageSize).ToList();
}

As commented, by using the backing store as an IQueryable you will be able to do projections before your query hits the DB and thus you can avoid any nasty Select N+1s.

The reason that this sits in an intermediary layer is simply you do not want to add references to your web project neither in your repo nor in your service layer (project) but because of this you cannot implement the viewmodel-specific queries in your service layer simply because the viewmodels cannot be resolved there. This implies that the viewmodels reside in this same project as well, and to this end, the MVC project only contains views, controllers and the ASP.NET MVC-related guttings of your app. I usually call this intermediate layer as 'SolutionName.Web.Core' and it references the service layer to be able to access the IQueryable<T>-returning method.

like image 30
Balázs Avatar answered Nov 11 '22 19:11

Balázs