Difference between Repository and Service Layer?
Asked Answered
E

5

236

In OOP Design Patterns, what is the difference between the Repository Pattern and a Service Layer?

I am working on an ASP.NET MVC 3 app, and am trying to understand these design patterns, but my brain is just not getting it...yet!!

Endoblast answered 19/2, 2011 at 6:51 Comment(0)
F
376

Repository Layer gives you additional level of abstraction over data access. Instead of writing

var context = new DatabaseContext();
return CreateObjectQuery<Type>().Where(t => t.ID == param).First();

to get a single item from database, you use repository interface

public interface IRepository<T>
{
    IQueryable<T> List();
    bool Create(T item);
    bool Delete(int id);
    T Get(int id);
    bool SaveChanges();
}

and call Get(id). Repository layer exposes basic CRUD operations.

Service layer exposes business logic, which uses repository. Example service could look like:

public interface IUserService
{
    User GetByUserName(string userName);
    string GetUserNameByEmail(string email);
    bool EditBasicUserData(User user);
    User GetUserByID(int id);
    bool DeleteUser(int id);
    IQueryable<User> ListUsers();
    bool ChangePassword(string userName, string newPassword);
    bool SendPasswordReminder(string userName);
    bool RegisterNewUser(RegisterNewUserModel model);
}

While List() method of repository returns all users, ListUsers() of IUserService could return only ones, user has access to.

In ASP.NET MVC + EF + SQL SERVER, I have this flow of communication:

Views <- Controllers -> Service layer -> Repository layer -> EF -> SQL Server

Service layer -> Repository layer -> EF This part operates on models.

Views <- Controllers -> Service layer This part operates on view models.

EDIT:

Example of flow for /Orders/ByClient/5 (we want to see order for specific client):

public class OrderController
{
    private IOrderService _orderService;

    public OrderController(IOrderService orderService)
    {
        _orderService = orderService; // injected by IOC container
    }

    public ActionResult ByClient(int id)
    {
        var model = _orderService.GetByClient(id);
        return View(model); 
    }
}

This is interface for order service:

public interface IOrderService
{
    OrdersByClientViewModel GetByClient(int id);
}

This interface returns view model:

public class OrdersByClientViewModel
{
     CientViewModel Client { get; set; } //instead of ClientView, in simple project EF Client class could be used
     IEnumerable<OrderViewModel> Orders { get; set; }
}

This is interface implementation. It uses model classes and repository to create view model:

public class OrderService : IOrderService
{
     IRepository<Client> _clientRepository;
     public OrderService(IRepository<Client> clientRepository)
     {
         _clientRepository = clientRepository; //injected
     }

     public OrdersByClientViewModel GetByClient(int id)
     {
         return _clientRepository.Get(id).Select(c => 
             new OrdersByClientViewModel 
             {
                 Cient = new ClientViewModel { ...init with values from c...}
                 Orders = c.Orders.Select(o => new OrderViewModel { ...init with values from o...}     
             }
         );
     }
}
Fiorin answered 19/2, 2011 at 7:16 Comment(36)
@Fiorin - Where do your View Models work at? I am thinking Controller serves the View Model to the View. The View Model get's it's data from the Service Layer, which gets it's data from the Repository, which quires the EF objects/POCO's. Does that sound correct?Endoblast
@Fiorin - Couple more questions. 1) What type should I return for collections from the repository? 2) How do you organize your assemblies/projects? Do you keep the repositories in the same assembly as the EF objects? If you use POCO's do you put them into another assembly? Where does the services live at?Endoblast
@Endoblast Striano: As you can see above, my IRepository returns IQueryable. This allows adding additional where conditions and deferred execution in service layer, not later. Yes, I use one assembly, but all these classes are placed in different namespaces. There is no reason to create many assemblies in small projects. Namespace and folder separation works nice.Fiorin
@Fiorin - So does your controller load the model from the service layer or does the model have methods to do this?Endoblast
@Fiorin - Also, say a customer has a collection of orders, when the repository returns the customer, how does it get the orders if you call Customer.Orders later on?Endoblast
+1 Excellent example! The only change I would make is putting the view model initialization into the constructor. return _clientRepository.Get(id).Select(c => new OrdersByClientViewModel (c));Starvation
Why return view models in the service? isn't the service suppose to emulate if you were to have multiple clients (mobile/web)? If thats the case then the viewmodel may differ from different platformsStarvation
Agreed with @Ryan, service layer should return entity object or collection of entity objects (not IQueryable). Then on ui entity maps to SomeViewModel by Automapper for example.Andro
@eldar: I never wrote about returning IQueryable. In my example service returns object with a list as IEnumerable. But yes, it should be mapped to view model class, not passed directly. I'll update my answer soon.Fiorin
Great example. I think var model = _prderService.GetByClient(id); should be: var model = _orderService.GetByClient(id);Cene
@LukLLu - How do you handle cross service calls? E.g. You have a product service that needs to use a method from the order service and vice versa? I'm assuming you would just inject that service in and start using it. But I also wonder how the validation would work as well....Starvation
@Ryan: Yes, I use injection. Read this article about validation in service layer: asp.net/mvc/tutorials/older-versions/models-%28data%29/…Fiorin
@Fiorin - Actually I just read that article and it was very helpful. It doesn't demonstrate the cross service calls but I think it would be pretty easy.Starvation
@Fiorin I am also looking to create a respository layer in a WPF MVVM Entity Frameowrk application. Should I create a separate respository class (that inherits from IRespository) for each entity generated by EF?Sanorasans
@AnkurSharma: You don't have to create class for every entity. You can use one generic implementation and create unit of work class, that gathers all repositories.Fiorin
@Fiorin thx for response. Can you redirect me to an example or walkthrough of this 'generic implementation, unit of work' approach. I am new to this.Sanorasans
@Fiorin - awesome response and example! I supposed assuming this is an old post that you don't still have the project you created to demonstrate this, correct? I am trying to figure out how to implement the repository. Do I essentially have to have a repository for every entity in my EF model? Or do I use ViewModels for that and create repositories for view models? Sorry for the confusion..Agc
@Duffp: You don't have to create repository for every entity. You can use generic implementation and bind IRepository<> to GenericRepository<> in your IOC library. This answer is very old. I think the best solution is to combine all repositories in one class called UnitOfWork. It should contain repository of every type and one method called SaveChanges. All repositories should share one EF context.Fiorin
@AnkurSharma: Here is one tutorial of how to implement generic repository (and other stuff): asp.net/mvc/tutorials/getting-started-with-ef-using-mvc/…Lenticularis
instead of returning viewmodel from the service layer, you should return DTO and convert that into viewModels using automapper.. sometimes they're the same, when they're not you'll be thankful you've implemented YGTNI "You're Going To Need It"Gardant
@hanzolo: You are right and that is all for now, because I have been planning to update this answer for a long time.Fiorin
@Fiorin I think the best solution is to combine all repositories in one class called UnitOfWork Why do this? EF's DbContext is already a UOW.Lati
@Lati DbContext is unit of work, but I create additional layer to simplify access. Take Get(int id) as an example. DbContext doen't give you this method. You have to apply Where and then take FirstOrDefault. While it is not a huge problem to write context.Collection.Where(item => item.Id == id).FirstOrDefault(), I prefer repository.Get(id). DbContext has a lof of functions and I've seen developers doing crazy thing with it and rarely it was justified. Limited access to DbContext using IRepository is enough in 99% of cases and keeps code simpler.Fiorin
@Fiorin I understand exactly where you're coming from, but I'm torn because so many recommend not using a repository pattern with EF as it already has one (DbContext). So would you recommend using a generic Repository pattern to make EF easier to use and then a service layer for your clients to interact with? I definitely like the ease of use I get out of my generic repository pattern.Lati
@Lati - As I said earlier - DbContext is UOW, but with too broad and complicated interface. So I wrap it up with generic repository to make interface simple and add some useful features (like Get(id)). There is no harm done. Exposing generic repository as a service is wrong, buit using it as a helper is completely ok.Fiorin
@Lati - Another thing is Your implementation of generic repository. It is really bad. Too much new, too much ToList, too much virtual, too much try catch and too much IEnumerable.Fiorin
I thought _clientRepository.Get(id) return single entity won't have Select() method.Latvian
@lukled Can you clarify two things please? First you said the service layer uses the repo but I see no mention of IRepository in IUserService. Second, you said “While List() method of repository returns all users, ListUsers() of IUserService could return only ones, user has access to.”. How? I don’t understand the connection.Boathouse
@Howiecamp: IUserService implementation uses IRepository internally. IUserService interface doesn't inherit from repository. There is no inheritance between repository and service. ListUsers() can return filtered users. Example: return _repository.List().Where(u => u.BossId == currentUserId).Fiorin
@Fiorin Gotcha - that makes sense. In order to do any logic like this in my services I need be returning IQueryables from my repo right? Otherwise I can return individual objects or IEnumerables or whatever, get the entire set of items into the service layer and then run linq queries there.Boathouse
why do you return model, not viewModel in this line var model = _orderService.GetByClient(id);. However, actually service returns viewModel in _orderService .Doxia
@ryan Totally agree with you. Returning ViewModels from services is a big no. See my elaboration in answer below.Intracutaneous
I also agree, that ViewModels shouldn't be returned by Service.Nympha
How do you pass the repository with the actually data connection in the "public OrderController(IOrderService orderService)" call (since IOrderService is expecting a IRepository in its constructor). Using the code as is, I'm getting a "No parameterless constructor defined for this object. " error.Sholem
@JosephNorris You have to configure dependency injection. It is out of the scope of this question, but you'll find a lot of materials on StackOverflow.Fiorin
IMHO, if your service methods are 1-1 mapping to the repository methods then you are not doing it right. It's just another layer of nothing.Pleat
L
54

As Carnotaurus said the repository is responsible for mapping your data from the storage format to you business objects. It should handle both how to read and write data(delete, update too) from and to the storage.

The purpose of service layer on the other hand is to encapsulate business logic into a single place to promote code reuse and separations of concerns. What this typically means for me in practice when building Asp.net MVC sites is that I have this structure

[Controller] calls [Service(s)] who calls [repository(ies)]

One principle I have found useful is to keep logic to a minimum in controllers and repositories.

In controllers it is because it helps keeping me DRY. It's very common that I need to use the same filtering or logic somewhere else and if I placed it in the controller I can't reuse it.

In repositories it is because I want to be able to replace my storage(or ORM) when something better comes along. And if I have logic in the repository I need to rewrite this logic when I change the repository. If my repository only returns IQueryable and the service does the filtering on the other hand, I will only need to replace the mappings.

For example I recently replaced several of my Linq-To-Sql repositories with EF4 and those where I had stayed true to this principle could replaced in a matter of minutes. Where I had some logic it was a matter of hours instead.

Laise answered 19/2, 2011 at 7:28 Comment(3)
I agree with you Mikael. In fact, i have applied the same scenario in my tech blog freecodebase.com and I used code first approach in this implementation. The source code can be downloaded here as well.Delagarza
I've been researching the general subject of applying a repository pattern in an existing MVC app. It is a bespoke framework with an Active Record-like ORM and other Rails/Laravel conventions, and has some architectural problems for work I'm doing now. One thing I came across is that repositories "should not return ViewModels, DTO's, or query objects", but rather should return repository objects. I'm thinking through where services interact with repository objects via methods like onBeforeBuildBrowseQuery and can use the query builder to alter the query.Dyak
@Toffee, your link is broken, can you please update it, I need the source code for this implementation.Holley
I
46

The accepted answer (and upvoted hundreds of time) has a major flaw. I wanted to point this out in the comment but it will just get buried down there in 30 something comments so pointing out here.

I took over an enterprise application which was built that way and my initial reaction was WTH? ViewModels in service layer? I did not want to change the convention because years of development had gone into it so I continued with returning ViewModels. Boy it turned into a nightmare when we started using WPF. We (the team of devs) were always saying: which ViewModel? The real one (the one we wrote for the WPF) or the services one? They were written for a web application and even had IsReadOnly flag to disable edit in the UI. Major, major flaw and all because of one word: ViewModel!!

Before you make the same mistake, here are some more reasons in addition to my story above:

Returning a ViewModel from the service layer is a huge no no. That's like saying:

  1. If you want to use these services you better be using MVVM and here is the ViewModel you need to use. Ouch!

  2. The services are making the assumption they will be displayed in a UI somewhere. What if it is used by a non UI application such as web services or windows services?

  3. That is not even a real ViewModel. A real ViewModel has observability, commands etc. That is just a POCO with a bad name. (See my story above for why names matter.)

  4. The consuming application better be a presentation layer (ViewModels are used by this layer) and it better understand C#. Another Ouch!

Please, don't do that!

Intracutaneous answered 15/5, 2018 at 4:13 Comment(1)
I just had to comment on this although I know that it doesn't add to the discussion: "That is just a POCO with a bad name." <<- That'd look nice on a T-Shirt! :) :)Biparietal
T
14

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. In an application, it is better to have a separate repository layer and service layer. Having separate repository and service layers make the code more modular and decouple the database from business logic.

Towboat answered 30/9, 2018 at 15:46 Comment(0)
K
9

Usually a repository is used as scaffolding to populate your entities - a service layer would go out and source a request. It is likely that you would put a repository under your service layer.

Kennithkennon answered 19/2, 2011 at 6:59 Comment(3)
So in an ASP.NET MVC app using EF4, maybe something like this: SQL Server --> EF4 --> Repository --> Service Layer --> Model --> Controller and vice a versa?Endoblast
Yeah, your repository could be used to get lightweight entities from EF4; and your service layer could be used to send these back to a specialised model manager (Model in your scenario). The controller would call down to your specialised model manager to do this... Take a quick look at my blog for Mvc 2 / 3. I have diagrams.Kennithkennon
Just for clarification: EF4 in your scenario is where Model is on my diagrams and Model in your scenario are specialised model managers in my diagramsKennithkennon

© 2022 - 2024 — McMap. All rights reserved.