DDD:关于模型的合法性,Entity.IsValid()合理吗?
背景
见过很多框架(包括我自己的)都会在实体的定义中包含一个IsValid()方法,用来判断实体的合法性,是否应该这样设计呢?本文就这个问题介绍一点想法,希望大家多批评。
实体能否处于“非法”状态?
实体是否应该包含IsValid()方法的深层次问题是:“实体能否处于非法状态?”。我们来定义一些术语,接下来我就引用这些术语:
- A模式:实体允许处于非法状态,但是实体要包含一个IsValid()方法进行校验。
- B模式:实体不允许处于非法状态,业务逻辑必须保证这一点。
关于A模式我不想多说了,A模式本身没有问题的,今天重点说说如何实现B模式。
如何实现B模式?
最好的说明就是写一个例子,下面是我们例子的需求:
- xxx属性不能为空。
- xxx属性必须唯一。
这个例子非常简单,也具有代表性,可以进一步抽象为:
- xxx属性不能为空,聚合自身的验证。
- xxx属性必须唯一,跨聚合验证。
让我们一个一个来。
xxx属性不能为空,聚合自身的验证。
聚合本身应该负责自己状态的完整性,反射可能会绕过这些验证,使用类似AutoMapper的工具需要注意(我已经处理了)。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks; using Happy.Domain;
using Happy.Domain.Tree;
using Happy.Example.Events.TestGrids; using Happy.Infrastructure; namespace Happy.Example.Domain.TestGrids
{
public partial class TestGrid : AggregateRoot<Guid>
{
public System.Int64? BigIntField { get; set; }
public System.Boolean? BitField { get; set; }
public System.DateTime? DateField { get; set; }
public System.DateTime? DateTimeField { get; set; }
public System.Decimal? DecimalField { get; set; }
public System.Double? FloatField { get; set; }
public System.Int32? IntField { get; set; }
public System.Decimal? MoneyField { get; set; }
public System.Decimal? NumericField { get; set; }
public System.String NVarcharField { get; private set; }
public System.Single? RealField { get; set; }
public System.TimeSpan? TimeField { get; set; }
public System.Byte[] TimestampField { get; set; } public void SetNVarcharField(string value)
{
value.MustNotNullAndNotWhiteSpace(value); this.NVarcharField = value;
} internal void PublishCreatedEvent()
{
this.PublishEvent(new TestGridCreatedEvent());
}
}
}
xxx属性必须唯一,跨聚合验证。
仓储负责判断唯一性,应用服务负责验证,注意:是先验证,然后修改的实体。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks; using Happy.Command;
using Happy.Application;
using Happy.Example.Domain.TestGrids;
using Happy.Example.Commands.TestGrids; namespace Happy.Example.Application.TestGrids
{
public class TestGridCommandHandler : ApplicationService,
ICommandHandler<CreateTestGridComamnd>,
ICommandHandler<UpdateTestGridComamnd>,
ICommandHandler<DeleteTestGridComamnd>
{
public void Handle(CreateTestGridComamnd command)
{
var testGridService = this.Service<TestGridService>(); testGridService.CheckNVarcharFieldUnique(command.NVarcharField);
var testGrid = command.CreateTestGrid(); testGridService.Create(testGrid);
command.Result = testGrid.Id;
} public void Handle(UpdateTestGridComamnd command)
{
var testGridService = this.Service<TestGridService>(); var testGrid = testGridService.LoadAndDetach(command.Id);
if (testGrid.NVarcharField != command.NVarcharField)
{
testGridService.CheckNVarcharFieldUnique(command.NVarcharField);
}
command.UpdateTestGrid(testGrid); testGridService.Update(testGrid);
} public void Handle(DeleteTestGridComamnd command)
{
this.Service<TestGridService>().Delete(command.Id);
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks; using AutoMapper; using Happy.Infrastructure;
using Happy.Infrastructure.AutoMapper;
using Happy.Command;
using Happy.Example.Domain.TestGrids; namespace Happy.Example.Commands.TestGrids
{
public class UpdateTestGridComamnd : ICommand, IHasIdProperty<Guid>
{
public Guid Id { get; set; }
public System.Int64? BigIntField { get; set; }
public System.Boolean? BitField { get; set; }
public System.DateTime? DateField { get; set; }
public System.DateTime? DateTimeField { get; set; }
public System.Decimal? DecimalField { get; set; }
public System.Double? FloatField { get; set; }
public System.Int32? IntField { get; set; }
public System.Decimal? MoneyField { get; set; }
public System.Decimal? NumericField { get; set; }
public System.String NVarcharField { get; set; }
public System.Single? RealField { get; set; }
public System.TimeSpan? TimeField { get; set; }
public System.Byte[] TimestampField { get; set; }
public byte[] OptimisticKey { get; set; } internal void UpdateTestGrid(TestGrid testGrid)
{
Mapper.Map(this, testGrid);
testGrid.SetNVarcharField(this.NVarcharField);
} static UpdateTestGridComamnd()
{
var map = Mapper.CreateMap<UpdateTestGridComamnd, TestGrid>();
map.ForMember(x => x.Id, m => m.Ignore());
map.IgnoreNotPublicSetter();
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks; using Happy.Example.Domain.TestGrids; namespace Happy.Example.Application.TestGrids
{
public partial class TestGridService
{
protected override void AfterCreate(TestGrid aggregate)
{
base.AfterCreate(aggregate); aggregate.PublishCreatedEvent();
} internal void CheckNVarcharFieldUnique(string value)
{
if (!this.Repository.IsNVarcharFieldExist(value))
{
throw new InvalidOperationException("NVarcharField必须唯一");
}
}
}
}
备注
一些好的资源:
- http://msdn.microsoft.com/en-us/library/ff664356(v=pandp.50).aspx
- http://gorodinski.com/blog/2012/05/19/validation-in-domain-driven-design-ddd/
- http://lostechies.com/jimmybogard/2009/02/15/validation-in-a-ddd-world/
- http://abdullin.com/journal/2009/1/29/ddd-and-rule-driven-ui-validation-in-net.html
- http://www.iteye.com/topic/413815
- http://devlicio.us/blogs/billy_mccafferty/archive/2009/02/17/a-response-to-validation-in-a-ddd-world.aspx
DDD:关于模型的合法性,Entity.IsValid()合理吗?的更多相关文章
- 关于模型的合法性,Entity.IsValid()合理吗?
关于模型的合法性,Entity.IsValid()合理吗? 背景 见过很多框架(包括我自己的)都会在实体的定义中包含一个IsValid()方法,用来判断实体的合法性,是否应该这样设计呢?本文就这个问题 ...
- Entity Framework:如果允许模型处于非法状态,在某些场景下,记得清空DbContext
Entity Framework:如果允许模型处于非法状态,在某些场景下,记得清空DbContext 背景 之前写过两篇文章介绍模型的合法性: DDD:关于模型的合法性,Entity.IsValid( ...
- DDD 领域驱动设计-在动手之前,先把你的脑袋清理干净
惨不忍睹的翻译 英文原文:http://www.codeproject.com/Articles/339725/Domain-Driven-Design-Clear-Your-Concepts-Bef ...
- 从壹开始微服务 [ DDD ] 之八 ║剪不断理还乱的 值对象和Dto
缘起 哈喽大家周四好,时间是过的真快,这几天一直忙着在公司的项目,然后带带新人,眼看这周要过去了,还是要抽出时间学习学习,这些天看到群里的小伙伴也都在忙着新学习,还是很开心的,至少当时的初衷已经达到了 ...
- 基于DDD的微服务设计和开发实战
你是否还在为微服务应该拆多小而争论不休?到底如何才能设计出收放自如的微服务?怎样才能保证业务领域模型与代码模型的一致性?或许本文能帮你找到答案. 本文是基于 DDD 的微服务设计和开发实战篇,通过借鉴 ...
- 驱动领域DDD的微服务设计和开发实战
你是否还在为微服务应该拆多小而争论不休?到底如何才能设计出收放自如的微服务?怎样才能保证业务领域模型与代码模型的一致性?或许本文能帮你找到答案. 本文是基于 DDD 的微服务设计和开发实战篇,通过借鉴 ...
- Lind.DDD.LindAspects方法拦截的介绍
回到目录 什么是LindAspects 之前写了关于Aspects的文章<Lind.DDD.Aspects通过Plugins实现方法的动态拦截~Lind里的AOP>,今天主要在设计思想上进 ...
- ASP.NET MVC 5 - 添加一个模型
在本节中,您将添加一些类,这些类用于管理数据库中的电影.这些类是ASP.NET MVC 应用程序中的"模型(Model)". 您将使用.NET Framework 数据访问技术En ...
- 我也来说说DDD~大话目录
回到占占推荐博客索引 DDD之前没有接触过,但一但有了接触就一发不可收拾,他会带去进入一个全新的世界! DDD不是新技术,而是新思想,新模式,是软件开发领域的一次突破,它更接近于业务,对于业务的改动它 ...
随机推荐
- QueryTask查询结果最多500条的问题
参考: http://dingtao-wgs.blog.163.com/blog/static/5026071420129813059865/ 上面文章中给出的解决方法:今天在做querytask的时 ...
- MyEclipse xml 手动添加 dtd
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "- ...
- solr课程学习系列-solr服务器配置(2)
本文是solr课程学习系列的第2个课程,对solr基础知识不是很了解的请查看solr课程学习系列-solr的概念与结构(1) 本文以windows的solr6服务器搭建为例. 一.solr的工作环境: ...
- React同构直出原理浅析
通常,当客户端请求一个包含React组件页面的时候,服务端首先响应输出这个页面,客户端和服务端有了第一次交互.然后,如果加载组件的过程需要向服务端发出Ajax请求等,客户端和服务端又进行了一次交互,这 ...
- IrregularGridCollectionView处理不定宽度的标签cell
IrregularGridCollectionView处理不定宽度的标签cell 效果 源码 https://github.com/YouXianMing/UI-Component-Collectio ...
- [算法导论]哈希表 @ Python
直接寻址方式: class HashTable: def __init__(self, length): self.T = [None for i in range(length)] class Da ...
- NCPC 2013: Dance Reconstruction
题目大意 对一个初始矩阵进行置换操作,已知经K次置换后得到的矩阵为,求一组可能的. 样例解释 这里只选取第二组样例进行解释. 4 2 3 4 1 2 2 3 4 1 初始矩阵为,根据Sample Ou ...
- vim 光标按行移动
记录一下: [ H/M/L ] 注意:这几个命令是大写的. 使用H/M/L这三个键,可以让光标跳到当前窗口的顶部.中间.和底部,停留在第一个非空字符上.H命令和L命令前也可以加一个数字,但数字的含义不 ...
- phpStorm无法使用svn1.8的解决办法
1.安装SVN的Command Lines Tools. 2.在phpStorm的SVN属性中,将Use Command Line Client填入:C:\Program Files\Tortoise ...
- goldengate一些参数整理
转自:http://blog.csdn.net/lemontree1123/article/details/46603549 manager参数: AUTOSTART:指定在mgr启动时自动启动那些进 ...