DDD - 表示层和域层之间的通信

时间:2014-02-17 14:05:58

标签: c# asp.net-mvc entity-framework domain-driven-design

我有连接从很多帖子点本人已阅读(并逐渐认识到我可能需要购买而不是依靠互联网材料的负荷Eric Evans的书)我想坚持到领域驱动设计的麻烦,但我在将数据保存回数据库之前,我遇到了从表示层到域的最佳通信方法。为了简单起见,我现在有一个可以有一个作业列表的用户类。当用户打开应用程序时,将查询其活动目录信息,并且只要他们打开应用程序,就会使用其数据填充此类。然后,用户可以编辑现有作业(如果他们拥有作业或创建新作业)。我开始创建一个使用我的UnitOfWork类的服务类,认为这可以作为我的沟通,但这就是我被困住的地方。

当前设置:

DAL - EF 6生成POCO和DbContext,存储库,工作单元

域名 - 实体,存储库&我在这篇文章中提到的工作单元接口,域接口服务?

演示文稿 - MVC(内联网),具体服务?

问题:

  1. 是服务类最好的类来实现这种类型的通信(以及用于创建我的领域类的新实例(例如,创建一个新的工作方法)?我知道我可以使用工厂模式,但我不想太复杂了)?服务是驻留在域层中还是我为应用程序层创建单独的项目?接口是否会进入域层,具体实现是否进入应用层?我觉得这可能会使应用程序变得复杂。另外,是服务WCF还是我可以创建自己的类?

  2. 如何在不将表示层泄漏到服务中的情况下,如何使用此服务将我的MVC层中的ViewModel映射回域(如果这是最佳方式)?我已经阅读了DTO,但那么服务层是否有点过分?或者可以将我的域实体公开为ViewModel吗?我一定在想错了。我无法想象这种交互在没有泄漏的情况下如何在控制器中看起来。对不起,所有的问题,谢谢。

    public class User
    {
        public int Id{ get; set; }
        public string WindowsId{ get; private set; }
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public string FullName { get { return FirstName + " " + LastName; } }
        public string Email { get; set; }
        public string WorkPhone { get; set; }
    
        public List<Job> Jobs { get; private set; }
    
        public void AddJob(Job job)
        {
            if(job == null)
            throw new Exception("Job is null");
    
            var newJob = new Job(this) //tell the job that this user is creating the job
            {
                Description = job.Description,
                DueDate = job.DueDate,
                Name = job.Name,
                Reason = job.Reason
            };
    
        Jobs.Add(newJob);
        }
    
    }
    
  3. 更新:我的解决方案

    我最后从每个人的答案中得到了一点点。

    @granadaCoder - 我使用我的EF POCO作为DTO传递的类,因为大部分时间是我在ViewModel中向用户显示的数据。所以它允许我使用DTO作为ViewModels

    @ChrisPratt - 你是对的。这样做可以避免大量的额外工作。我所做的就是创建一个包含我需要的所有查询的服务。如果我不得不改变EF,它仍然不会打扰我的其他层。我所要做的就是创建存储库和UOW

    @MilivojMilani - 我确实在控制器中重复了逻辑,因此服务层使我与DRY主体保持一致

    @Yorro - 我使用你的积分和参考来加强设计,因为我仍然不确定服务层设置。由于这是一个较小的项目,我设置了另一个文件夹,其中包含我创建的服务和界面,没有WCF

3 个答案:

答案 0 :(得分:3)

如果您正在使用Entity Framework,请远离存储库/工作单元模式。 EF本身就是这种模式的一种实现。如果要抽象实体框架,则实现一种服务模式,该模式可以准确地返回完全烘焙的数据。最好用一个例子来说明:

使用EF / Repository

IQueryable<Post> posts = db.Posts.Where(m => m.BlogId == blog.Id && m.Status == PostStatuses.Published && m.PublishDate <= DateTime.Now).OrderBy(m => m.PublishDate);

有服务

List<Post> posts = service.GetPublishedPostsForBlog(blog);

这里的两个主要区别是:

  1. 您正在使用该服务从数据库中提取已经的数据;存储库/ EF将返回可能已经或可能尚未针对数据库执行的可查询。

  2. 您的所有逻辑都使用服务进入服务方法。然而,使用存储库/ EF可以构建您的查询。

  3. 也就是说,不要在应用程序的各个层面上如此挂起。这不是关于你拥有多少层以及你称之为什么层,而是关于从应用程序中抽象逻辑而不需要知道如何构造逻辑。例如,用于返回已发布博客帖子列表的控制器操作不应该知道将帖子限定为“已发布”的内容。这是应该去其他地方的域逻辑(比如返回此数据集的服务方法)。这个想法只是遵循单一责任原则:类/方法/等。应该做一个的事情,做得好。您的控制器操作应该只关注返回视图(并且只需要进行少量工作来获取视图所需的内容)。

    就映射而言,这个术语完全解释了你的所作所为,所以我不确定这里是否存在混淆。如果您想将Post映射到PostViewModel,例如:

    var model = new PostViewModel
    {
        Title = post.Title,
        Content = post.Content,
        ...
    }
    

    或者使用对象列表,您可以使用LINQ:

    var model = posts.Select(m => new PostViewModel
    {
        Title = m.Title,
        Content = m.Content,
        ...
    }
    

    如果您提出的问题很简单:我该怎样做更容易?然后,您可以查看第三方映射库,如AutoMapper。

答案 1 :(得分:2)

服务层

服务层充当应用程序的boundery,它封装了您的域实体,换句话说,它保护您的域。与域的所有通信都必须通过服务层。但域模型(最纯粹的形式)不应该对服务层或任何其他基础设施层(DAL / Presentation)有任何引用。

enter image description here http://martinfowler.com/eaaCatalog/serviceLayer.html

  

服务是否驻留在域层中,还是为应用层创建单独的项目?接口是否会进入域层,具体实现是否进入应用层?

如果您已经将图层分隔到自己的项目中,那么服务层应该有自己的项目。

  

是服务WCF还是我可以创建自己的类?

最简单的服务只是一个类,直接从Microsoft教程中查看服务层的示例: http://www.asp.net/mvc/tutorials/older-versions/models-(data)/validating-with-a-service-layer-cs

这是另一个关于如何使用服务层重构MVC的示例 http://www.arrangeactassert.com/asp-net-mvc-controller-best-practices-%E2%80%93-skinny-controllers/

  

如何在不将表示层泄漏到服务中的情况下,如何使用此服务将我的MVC层中的ViewModel映射回域(如果这是最佳方式)?我已经阅读了DTO,但那么服务层是否有点过分?或者可以将我的域实体公开为ViewModel吗?我一定在想错了。我无法想象这种交互在没有泄漏的情况下如何在控制器中显示。

这个问题完全是另一个话题。 DTO是服务层与外层通信的好方法。

你的问题非常好,事实上它是最常被问到的。

  • 在服务层使用DTO是否过度?
  • 将应用程序的内核(域)暴露给外层是否可以?
  • 服务层如何在没有泄漏的情况下与外层交互?
  • DTO应该在哪里?

我在这里回答了同样的问题:https://stackoverflow.com/a/21569720/1027250

This image提供了有关服务层如何适应多层体系结构的开销视图。请注意,这对于每个项目都不是绝对的或完美的,您应该根据项目的需要来处理模式。附加层增加了复杂性,这是对大型团队的长期项目的长期投资。

答案 2 :(得分:0)

1.如果您使用MVC,您可能有也可能没有服务层。问自己一个以下问题 - 您是否在多个地方使用此功能?如果答案是肯定的 - 请使用应用程序服务(不是域名服务)。如果不是,请将其放入MVC控制器本身。

不要在域图层中放置任何水合逻辑。 DDD中的域层不应该知道有关持久性的任何信息。控制器/应用程序服务应使用存储库接口获取数据,并使用相同的接口保存数据。在您的情况下,您应该只提取并保存User类,而Job应该低于它作为聚合根子。使用UnitOfWork和Repository非常好。这是一篇很好的文章=&gt;

http://www.asp.net/mvc/tutorials/getting-started-with-ef-5-using-mvc-4/implementing-the-repository-and-unit-of-work-patterns-in-an-asp-net-mvc-application

2.在某些情况下,使用域对象作为ViewModel类是完全正常的。在其他更复杂的情况下,您必须手动映射属性。映射通常最好作为ViewModel类的扩展方法完成。有些人使用AutoMapper,虽然我在将表示类映射到域时反对使用它。所有这些映射工作都不应该在域层中完成。您可能有很多viewmodel类,它会使表示层逻辑膨胀域层。

希望它有所帮助。