【问题标题】:Using Service layer classes in ViewModels. A Design Flaw?在 ViewModel 中使用服务层类。设计缺陷?
【发布时间】:2012-04-24 07:13:21
【问题描述】:

我想知道我的解决方案是否存在设计缺陷。这是我所拥有的:

  • Entities => 纯 poco。参考:无。
  • Data => 数据访问。参考:Entities.
  • Service => 业务逻辑。参考:EntitiesData
    • 经理类。
    • 视图模型。
  • WebApp => 用户界面。参考:EntitiesService

WebApp ASP.NET MVC 项目作为 UI,Entities 项目没有用于持有纯 POCO 的参考。 Data 用于访问数据库,Service 用于业务逻辑(我的 Manager 类所在的位置)。

基本上,我为每个实体定义了一个Manager 类。例如,我有一个与Recipient 实体列表相关的Message 实体。我有一个MessageManagerRecipientManager 类,负责使用数据层和逻辑结果的CRUD 操作(例如public List<Message> GetAllMessagesWithPermissionForUser(User user, Permission permission)

对于我的 MVC 项目,我在服务层定义了一些 ViewModel 类来为我的视图生成特定的视频模型。由于视图模型使用管理器类,我在我的服务类中定义了它们。例如,我有一个 MessageOperationVM 视图模型,它有一个 PermittedBoxesToSend 属性。此属性使用我的 BoxManager 类来获取指定消息允许的所有框:

// Initialized by Catsle Windsor.
public BoxManager BoxManager {get; set;}

public List<Box> PermittedBoxesToSend 
{
    if(this._premittedBoxesToSend != null)
    {
        this._permittedBoxesToSend = BoxManager.GetPermittedBoxesToSend(this.Message);
    }
}
  • 我不确定在 Viewmodels 中使用管理器类是否是一个好的设计。尽管我已将它们定义为构造函数/属性设置器以填充 DI。我应该在我的控制器中填充我的视图模型的属性而不是定义属性并摆脱我的视图模型中的管理器类吗?

    public ActionResult ShowNewMessageDialog()
    { 
        var messageVM = new MessageOperationVM() { new Message() };
        messageVM = this.BoxManager.GetPermittedBoxesToSend();
    }
    
  • 为每个实体使用管理器类似乎使维护变得困难。 (尽管它们都派生自 BaseManager 类,该类共享它们的共同操作)

  • 上面的设计有什么值得重新考虑的地方吗?

谢谢。

更新
基于 eulerfx 的回答:
我对你的回答的问题是:要构造一个 ViewModel,我必须调用一些服务层的方法。所以我不能仅仅基于我的 poco 实体来构建我的 ViewModel。您是否建议我也应该在控制器中构建这些部分? :

public ActionResult ShowNewMessageDialog()
{
    var message = this.messageRepository.GetMessage();
    var messageVM = new MessageViewModel(message);
    messageVM.CustomProperty = this.messageManager.CallSomeMethod(message);
    return View(messageVM);
}

【问题讨论】:

  • 服务中的业务逻辑敲响警钟:业务逻辑应该在实体中。

标签: design-patterns domain-driven-design viewmodel


【解决方案1】:

我认为从视图模型到服务的引用是设计缺陷。视图模型应该是扁平且简单的 DTO,旨在与视图绑定。它们不应该是 DI 容器图的一部分,因为这会使事情复杂化并使对代码的推理更加困难。您使用的术语的可接受定义如下。

  • 实体是您所描述的实体。它们是简单的 POCO 类。
  • 所谓的“数据”是repository pattern。存储库提供对底层数据库中实体的访问和持久性。
  • 服务是一个重载的术语,但它们通常用于封装域,将其作为 API 公开给其他应用程序层。服务可以引用存储库。我在 DDD here 的上下文中写了一篇关于这些类型服务的博客文章,它们被称为应用程序服务。
  • 视图模型是表示层的一部分或您所称的 WebUI。因此,它们由 MVC 控制器构造并传递给视图。控制器使用从应用程序服务或直接从存储库获得的数据构造视图模型。视图模型可以包含一个构造函数,该构造函数接受服务或存储库返回的实体。

代码可能如下所示:

/// Domain model class that lives in domain/business layer project
public class Message
{
  // properties and behavior go here
}

// View model class that lives in the ASP.NET project
public class MessageViewModel
{
  public MessageViewModel() { }
  public MessageViewModel(Message message)
  {
    // construct the view model based on the provided entity
  }
  // properties specific to the view go here
} 


// ASP.NET MVC controller.
public class MessagesController : Controller
{
 // this repository should be injected by DI container.
 readonly IMessageRepository messageRepository;

 public ActionResult ShowNewMessageDialog()
 {
   var message = this.messageRepository.GetMessage();
   return View(new MessageViewModel(message));
 }
}

【讨论】:

  • 感谢您的详尽回答。我已经在我的数据层中使用了存储库模式。我已经更新了我的问题。请看一下。
  • 抱歉挖旧帖。但是有什么理由不应该在服务或业务逻辑层中处理视图模型。是因为SOC吗?
【解决方案2】:

您可以考虑使用Visitor 设计模式来操作EntitiesData 类,而不是Manager 类。

如果ManagerVisitor 类被定义为不必仅在视图代码中使用,那么这应该不是问题。但是如果它们只能在 View 中使用,而对于其他 View 等,您必须重新定义对 EntitiesData 类的访问权限,或者编写更多/重复的代码,那就有问题了。

【讨论】:

  • 感谢您的回答。但是,由于当前的结构,使用访问者模式对我来说成本太高了。
猜你喜欢
  • 2011-09-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-06-16
  • 1970-01-01
  • 2017-01-31
  • 2013-01-15
  • 1970-01-01
相关资源
最近更新 更多