概念模型
您可以使用Entity Framework和EF Core从贫血域模型转换为更为丰详情
当前位置:主页 > 秒速牛牛作品 > 概念模型 >
您可以使用Entity Framework和EF Core从贫血域模型转换为更为丰

   

  在使用ORM中(比如Entity Framework)贫血领域模型十分常见 。本篇文章将先探讨贫血模型的问题,再去探究在EF Core中使用Code First时如何使用简单的方法来避免贫血模型。

  在对领域建模后,输出一系列类中仅包含一些简单属性声明而不包含业务逻辑的模型,就属于贫血模型。当使用Entity Framework时,它们不仅仅是简单的数据持有者而且包含有一堆public getter和public setters:

  由于其完全缺乏面向对象编程的原则,因此贫血模型通常被描述为反模式。他们需要调用者来完善验证和其他业务逻辑。由于缺乏相应的抽象,就会导致代码重复、较差的数据完整性,以及增加高层模块的复杂性。

  贫血模型是十分常见的。从我的经验来看,EF中超过80%的领域模型都是贫血模型。这并不奇怪。几乎所有的文档和其他博客文章都以最简单的方式展示了EF。他们专注于尽可能快地开始工作,而不是主张最佳实践。

  在大多数情况下,这是没有意义的。领域对象通常至少需要一些数据才能使其有效。创建没有任何数据(如标题或URL)的BlogPost实例是没有意义的,因为其仅仅是一个实例化对象,但对象却不包含状态和行为,不满足数据有效性。有些人不同意,但是DDD社区普遍认为确保领域对象始终有效是有意义的。为了解决这个问题,我们可以像处理其他OO类一样对待我们的域类,并引入一个参数化的构造函数:

  EF需要一个无参数的构造函数来查询该做什么?幸运的是,尽管EF确实需要无参数构造函数,但它并不要求构造函数必须为public,所以我们可以为EF增加一个无参private构造函数,同时强制调用代码使用参数化构造函数。拥有额外的构造函数显然并不理想,但这些妥协通常可以时ORM与OO代码更好地配合。

  第二种方式更受欢迎的主要原因在于它更接近地模拟了现实世界中发生的事情。用户不是孤立地更新单个属性,而是倾向于执行一组已知操作(由UI或API接口确定)。这些操作可能会导致一个或多个属性被更新,但通常情况下更多。业务逻辑依赖于上下文的场景是非常普遍的,这将会导致对属性进行赋值的set中的验证逻辑变得复杂而难以理解。作为基本示例,请考虑以下博客文章发布流程:

  通过删除无参数构造函数和公共属性设置器并添加动作类型的方法,我们现在拥有了始终有效的领域对象,并包含了与所讨论的实体直接相关的所有业务逻辑,这是一个很大的改进。我们已经使我们的代码同时更加健壮和简单。

  值对象的经典示例包括货币,地址和坐标,但也可以使用值类型替换单个属性,而不是使用字符串或整型。例如,不是将电话号码存储为字符串,而是可以创建一个带有内置验证的PhoneNumber值类型以及提取拨号代码的方法等。

  货币和金额是内在联系的。为了使数据有效,这两条信息都是必需的。因此,对它们进行建模是有道理的。请注意,参数化的构造函数和私有属性设置器的使用方式与我们在建模领域对象时所使用的完全相同。实体框架也需要一个私有无参数构造函数。

  使用值对象的好处与向富领域模型的转变非常相似。丰富的领域模型不需要调用代码来验证领域模型,并提供了一个定义良好的抽象来进行编程。一个值对象进行自我验证,因此包含值对象属性的领域模型本身不需要知道如何验证值类型。所有非常清晰和简单。

  当您打算从贫血域模型转移到更丰富的领域模型时,您将立即体会到将领域级的业务逻辑封装在领域对象中的好处。请注意,尽管如此,尝试并不是件容易的事。在您的领域对象上创建一个方法来执行验证,然后更新多个属性无疑是件好事。但从领域对象发送电子邮件或保存到数据库并不是您可能想要做的事情。重要的是要意识到,拥有丰富的领域模型并不否定另一层的需求来安排这些更高层次的关注。这是应用服务或命令处理程序的工作,具体取决于您的体系结构。

  一个丰富的、自我验证的领域模型的一个负面影响是它可以使测试变得更加困难。通过public setter,您可以简单地将各个值分配给任何领域对象的属性。这使您可以直接指定您需要的确切值,以便将对象置于特定状态以进行测试。如果你锁定你的属性和构造函数,那么这种方法是不可能的。但这也不是一件坏事,它使单元测试变得稍微困难一点,但你所做的是确保你的测试是有效的。

  另一方面,它也使得测试领域对象本身的逻辑非常简单。尽管你的应用服务/命令处理程序的单元测试几乎肯定会需要一定程度的模拟,但你应该发现大部分领域对象测试的构建要简单得多,并且通常不需要依赖模拟。

  本文介绍了三种非常简单的技术,您可以使用Entity Framework和EF Core从贫血域模型转换为更为丰富的领域模型。使用参数化的构造函数可以确保我们的领域模型在实例化时有效。清除公共属性setter确保我们的模型在其整个生命周期内保持有效状态。在领域模型上内部执行验证和引入更改状态的方法使我们能够集中业务逻辑并简化调用代码。最后,我们考察了值对象的使用,并解释了他们如何进一步推进了这种简化和逻辑封装。返回,查看更多