关于模型的合法性,Entity.IsValid()合理吗?

背景

见过很多框架(包括我自己的)都会在实体的定义中包含一个IsValid()方法,用来判断实体的合法性,是否应该这样设计呢?本文就这个问题介绍一点想法,希望大家多批评。

实体能否处于“非法”状态?

实体是否应该包含IsValid()方法的深层次问题是:“实体能否处于非法状态?”。我们来定义一些术语,接下来我就引用这些术语:

  • A模式:实体允许处于非法状态,但是实体要包含一个IsValid()方法进行校验。
  • B模式:实体不允许处于非法状态,业务逻辑必须保证这一点。

关于A模式我不想多说了,A模式本身没有问题的,今天重点说说如何实现B模式。

如何实现B模式?

最好的说明就是写一个例子,下面是我们例子的需求:

  • xxx属性不能为空。
  • xxx属性必须唯一。

这个例子非常简单,也具有代表性,可以进一步抽象为:

  • xxx属性不能为空,聚合自身的验证。
  • xxx属性必须唯一,跨聚合验证。

让我们一个一个来。

xxx属性不能为空,聚合自身的验证。

聚合本身应该负责自己状态的完整性,反射可能会绕过这些验证,使用类似AutoMapper的工具需要注意(我已经处理了)。

 1 using System;
2 using System.Collections.Generic;
3 using System.Linq;
4 using System.Text;
5 using System.Threading.Tasks;
6
7 using Happy.Domain;
8 using Happy.Domain.Tree;
9 using Happy.Example.Events.TestGrids;
10
11 using Happy.Infrastructure;
12
13 namespace Happy.Example.Domain.TestGrids
14 {
15 public partial class TestGrid : AggregateRoot<Guid>
16 {
17 public System.Int64? BigIntField { get; set; }
18 public System.Boolean? BitField { get; set; }
19 public System.DateTime? DateField { get; set; }
20 public System.DateTime? DateTimeField { get; set; }
21 public System.Decimal? DecimalField { get; set; }
22 public System.Double? FloatField { get; set; }
23 public System.Int32? IntField { get; set; }
24 public System.Decimal? MoneyField { get; set; }
25 public System.Decimal? NumericField { get; set; }
26 public System.String NVarcharField { get; private set; }
27 public System.Single? RealField { get; set; }
28 public System.TimeSpan? TimeField { get; set; }
29 public System.Byte[] TimestampField { get; set; }
30
31 public void SetNVarcharField(string value)
32 {
33 value.MustNotNullAndNotWhiteSpace(value);
34
35 this.NVarcharField = value;
36 }
37
38 internal void PublishCreatedEvent()
39 {
40 this.PublishEvent(new TestGridCreatedEvent());
41 }
42 }
43 }

xxx属性必须唯一,跨聚合验证。

仓储负责判断唯一性,应用服务负责验证,注意:是先验证,然后修改的实体。

 1 using System;
2 using System.Collections.Generic;
3 using System.Linq;
4 using System.Text;
5 using System.Threading.Tasks;
6
7 using Happy.Command;
8 using Happy.Application;
9 using Happy.Example.Domain.TestGrids;
10 using Happy.Example.Commands.TestGrids;
11
12 namespace Happy.Example.Application.TestGrids
13 {
14 public class TestGridCommandHandler : ApplicationService,
15 ICommandHandler<CreateTestGridComamnd>,
16 ICommandHandler<UpdateTestGridComamnd>,
17 ICommandHandler<DeleteTestGridComamnd>
18 {
19 public void Handle(CreateTestGridComamnd command)
20 {
21 var testGridService = this.Service<TestGridService>();
22
23 testGridService.CheckNVarcharFieldUnique(command.NVarcharField);
24 var testGrid = command.CreateTestGrid();
25
26 testGridService.Create(testGrid);
27 command.Result = testGrid.Id;
28 }
29
30 public void Handle(UpdateTestGridComamnd command)
31 {
32 var testGridService = this.Service<TestGridService>();
33
34 var testGrid = testGridService.LoadAndDetach(command.Id);
35 if (testGrid.NVarcharField != command.NVarcharField)
36 {
37 testGridService.CheckNVarcharFieldUnique(command.NVarcharField);
38 }
39 command.UpdateTestGrid(testGrid);
40
41 testGridService.Update(testGrid);
42 }
43
44 public void Handle(DeleteTestGridComamnd command)
45 {
46 this.Service<TestGridService>().Delete(command.Id);
47 }
48 }
49 }
 1 using System;
2 using System.Collections.Generic;
3 using System.Linq;
4 using System.Text;
5 using System.Threading.Tasks;
6
7 using AutoMapper;
8
9 using Happy.Infrastructure;
10 using Happy.Infrastructure.AutoMapper;
11 using Happy.Command;
12 using Happy.Example.Domain.TestGrids;
13
14 namespace Happy.Example.Commands.TestGrids
15 {
16 public class UpdateTestGridComamnd : ICommand, IHasIdProperty<Guid>
17 {
18 public Guid Id { get; set; }
19 public System.Int64? BigIntField { get; set; }
20 public System.Boolean? BitField { get; set; }
21 public System.DateTime? DateField { get; set; }
22 public System.DateTime? DateTimeField { get; set; }
23 public System.Decimal? DecimalField { get; set; }
24 public System.Double? FloatField { get; set; }
25 public System.Int32? IntField { get; set; }
26 public System.Decimal? MoneyField { get; set; }
27 public System.Decimal? NumericField { get; set; }
28 public System.String NVarcharField { get; set; }
29 public System.Single? RealField { get; set; }
30 public System.TimeSpan? TimeField { get; set; }
31 public System.Byte[] TimestampField { get; set; }
32 public byte[] OptimisticKey { get; set; }
33
34 internal void UpdateTestGrid(TestGrid testGrid)
35 {
36 Mapper.Map(this, testGrid);
37 testGrid.SetNVarcharField(this.NVarcharField);
38 }
39
40 static UpdateTestGridComamnd()
41 {
42 var map = Mapper.CreateMap<UpdateTestGridComamnd, TestGrid>();
43 map.ForMember(x => x.Id, m => m.Ignore());
44 map.IgnoreNotPublicSetter();
45 }
46 }
47 }
 1 using System;
2 using System.Collections.Generic;
3 using System.Linq;
4 using System.Text;
5 using System.Threading.Tasks;
6
7 using Happy.Example.Domain.TestGrids;
8
9 namespace Happy.Example.Application.TestGrids
10 {
11 public partial class TestGridService
12 {
13 protected override void AfterCreate(TestGrid aggregate)
14 {
15 base.AfterCreate(aggregate);
16
17 aggregate.PublishCreatedEvent();
18 }
19
20 internal void CheckNVarcharFieldUnique(string value)
21 {
22 if (!this.Repository.IsNVarcharFieldExist(value))
23 {
24 throw new InvalidOperationException("NVarcharField必须唯一");
25 }
26 }
27 }
28 }

备注

一些好的资源:

 
分类: DDD

关于模型的合法性,Entity.IsValid()合理吗?的更多相关文章

  1. DDD:关于模型的合法性,Entity.IsValid()合理吗?

    背景 见过很多框架(包括我自己的)都会在实体的定义中包含一个IsValid()方法,用来判断实体的合法性,是否应该这样设计呢?本文就这个问题介绍一点想法,希望大家多批评. 实体能否处于“非法”状态? ...

  2. Entity Framework:如果允许模型处于非法状态,在某些场景下,记得清空DbContext

    Entity Framework:如果允许模型处于非法状态,在某些场景下,记得清空DbContext 背景 之前写过两篇文章介绍模型的合法性: DDD:关于模型的合法性,Entity.IsValid( ...

  3. ASP.NET MVC 5 - 添加一个模型

    在本节中,您将添加一些类,这些类用于管理数据库中的电影.这些类是ASP.NET MVC 应用程序中的"模型(Model)". 您将使用.NET Framework 数据访问技术En ...

  4. Asp.Net MVC4入门指南(4):添加一个模型

    在本节中,您将添加一些类,这些类用于管理数据库中的电影.这些类是ASP.NET MVC 应用程序中的"模型(Model)". 您将使用.NET Framework 数据访问技术En ...

  5. 自制C#版3DS文件的解析器并用SharpGL显示3DS模型

    自制C#版3DS文件的解析器并用SharpGL显示3DS模型 我已经重写了3ds解析器,详情在此(http://www.cnblogs.com/bitzhuwei/p/CSharpGL-2-parse ...

  6. backbonejs中的模型篇(二)

    一:模型标识符 每个模型都有一个用作唯一标识符的ID属性,以便在不同模型间进行区分.通过id属性我们可以直接访问模型对象当中用于标识符存放的属性,默认属性名为id,但也可以通过设置idAttribut ...

  7. Entity Framework 全面教程详解(转)

    目录 预备知识    2 LINQ技术 2 LINQ技术的基础 - C#3.0    2 自动属性    2 隐式类型    2 对象初始化器与集合初始化器    3 匿名类    3 扩展方法    ...

  8. Getting Started with Entity Framework 6 Code First using MVC 5--Contoso 大学

    在本教程中使用的软件版本 Visual Studio 2013 年 4.5.NET 实体框架 (EntityFramework 6.1.0 NuGet 包) 6 Windows Azure SDK 2 ...

  9. 转载Entity Framework全面教程

    转载原地址:http://www.cnblogs.com/lsxqw2004/archive/2009/05/31/1495240.html#_Toc228672754 预备知识    2 LINQ技 ...

随机推荐

  1. MVC5+EF6 入门

    MVC5+EF6 入门完整教程九   前一阵子临时有事,这篇文章发布间隔比较长,我们先回顾下之前的内容,每篇文章用一句话总结重点. 文章一 MVC核心概念简介,一个基本MVC项目结构 文章二 通过开发 ...

  2. C# 调用Webservice并传递序列化对象

    原文:C# 调用Webservice并传递序列化对象 C#动态调用WebService注意要点 1.动态调用的url后面注意一定要加上?WSDL   例如:string _url = "ht ...

  3. 日积月累系列之分页控件(js源码)

    最近开发了一款分页控件,分享给大家. 主要功能和界面介绍 cform分页控件支持服务端分页.客户端分页.数据过滤.数据排序等功能. 源码介绍 cform-pager分页控件主要由三部分组成:css.s ...

  4. 安卓CTS官方文档之兼容性方案概览

    兼容性方案概览 安卓的兼容性方案让安卓手机生产商能够很容易就开发中可兼容的安卓设备(天地会珠海分舵注:可兼容什么呢?就是可以兼容标准google提供的安卓系统可以支持的功能,以防手机生产商把开源的安卓 ...

  5. vim打开出现的文档^M什么

    网上公开的一些代码,发现里面多^M符号.这是什么? 我搜索^M没有效果,这应该是一个特殊的控制字符.找换行的结果是不.在每一行的末尾是回车,代替它周围包裹,对于由线定义不同的编码系统是不一样的. li ...

  6. Windows Server 2008安装 百度Wifi \ 360Wifi \ 小米Wifi 失败的解决方法

    服务器管理 -> 功能 -> 安装 [无线LAN]服务

  7. Mvc 导出 Excel

    Mvc 导出 Excel 之前接触过Webform,winfrom 的导出Excel方法 ,优点:省事.缺点:服务器必须安装Office 这几天做项目 和 大牛学习了一下 新的方法,自己加以总结.希望 ...

  8. C#中使用消息队列RabbitMQ

    在C#中使用消息队列RabbitMQ 2014-10-27 14:41 by qy1141, 745 阅读, 2 评论, 收藏, 编辑 1.什么是RabbitMQ.详见 http://www.rabb ...

  9. CSS知识点:选择符

    一.选择符的种类 1)通配选择符 它用来给页面所有的元素设置样式 *{margin:0;padding:0;}.但是实际当中不建议这么用,页面中用到了哪些样式,就统一设置样式,因为*影响性能.也可以给 ...

  10. 证明中序遍历O(n)

    算法导论12.1 什么是二叉搜索树 二叉搜索树应满足的性质: 设x是二叉搜索树中的一个结点.如果y是x左子树中的一个结点,那么y.key <= x.key.如果y是右子树中的一个结点,那么y.k ...