在OOP设计模式中,存储库模式和服务层之间的区别是什么?

我正在做ASP。NET MVC 3应用程序,我试图理解这些设计模式,但我的大脑只是没有得到它…


当前回答

公认的答案(被点赞了数百次)有一个重大缺陷。我想在评论中指出这一点,但它会被淹没在30多条评论中,所以在这里指出。

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!!

在你犯同样的错误之前,除了我上面的故事,这里还有更多的原因:

从服务层返回ViewModel是一个巨大的错误。这就像是在说:

If you want to use these services you better be using MVVM and here is the ViewModel you need to use. Ouch! 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? 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.) The consuming application better be a presentation layer (ViewModels are used by this layer) and it better understand C#. Another Ouch!

求你了,别这样!

其他回答

通常,存储库被用作填充实体的脚手架——服务层将出去并提供请求源。您可能会在服务层下面放置一个存储库。

存储库层为数据访问提供了额外的抽象级别。而不是写作

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

要从数据库中获取单个项,可以使用存储库接口

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

并调用Get(id)。存储库层公开基本的CRUD操作。

服务层公开使用存储库的业务逻辑。示例服务如下所示:

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);
}

存储库的List()方法返回所有用户,而IUserService的ListUsers()方法只能返回用户可以访问的用户。

在ASP。NET MVC + EF + SQL SERVER,我有这样的通信流程:

视图<-控制器->业务层->存储库层-> EF -> SQL Server 业务层->存储库层-> EF对模型进行操作。 视图<-控制器->业务层该部分对视图模型进行操作。

编辑:

/Orders/ByClient/5的流程示例(我们想要查看特定客户端的订单):

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); 
    }
}

这是订单服务的接口:

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

这个接口返回视图模型:

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

这是接口实现。它使用模型类和存储库来创建视图模型:

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...}     
             }
         );
     }
}

正如Carnotaurus所说,存储库负责将数据从存储格式映射到业务对象。它应该处理如何从存储中读取和写入数据(删除,更新)。

另一方面,服务层的目的是将业务逻辑封装到单个位置,以促进代码重用和关注点分离。在构建Asp.net MVC网站时,这对我来说通常意味着我有这个结构

[控制器]调用[服务],服务调用[存储库]

我发现一个有用的原则是将控制器和存储库中的逻辑保持在最小。

在控制器中,这是因为它可以帮助我保持干燥。这是非常常见的,我需要在其他地方使用相同的过滤或逻辑,如果我把它放在控制器中,我就不能重用它。

在存储库中,这是因为当更好的存储出现时,我希望能够替换我的存储(或ORM)。如果我在存储库中有逻辑,当我改变存储库时,我需要重写这个逻辑。如果我的存储库只返回IQueryable,而服务在另一方面进行过滤,那么我只需要替换映射。

例如,我最近用EF4替换了我的几个Linq-To-Sql存储库,那些我一直坚持这一原则的存储库可以在几分钟内被替换掉。我的逻辑是,这只是几个小时的问题。

公认的答案(被点赞了数百次)有一个重大缺陷。我想在评论中指出这一点,但它会被淹没在30多条评论中,所以在这里指出。

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!!

在你犯同样的错误之前,除了我上面的故事,这里还有更多的原因:

从服务层返回ViewModel是一个巨大的错误。这就像是在说:

If you want to use these services you better be using MVVM and here is the ViewModel you need to use. Ouch! 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? 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.) The consuming application better be a presentation layer (ViewModels are used by this layer) and it better understand C#. Another Ouch!

求你了,别这样!

实现存储库层是为了访问数据库,并有助于扩展数据库上的CRUD操作。而服务层由应用程序的业务逻辑组成,并可能使用存储库层来实现涉及数据库的某些逻辑。在应用程序中,最好有单独的存储库层和服务层。拥有独立的存储库和服务层使代码更加模块化,并将数据库与业务逻辑分离。