从零开始使用CodeArt实践最佳领域驱动开发(五)
本章内容还在整理上传中,你可以等全部更新完毕后再查阅也可以先预览已上传的内容。。。。。。
7. 应用层的命令模式
在上个章节里我们设计并编码了领域对象Permission,但是目前Permission并没有任何行为上的设计。这是因为我们不建议“凭空去制造行为”,而是在领域对象第一个版本的代码实现之后就立即使用它。在使用过程中观察外界(应用层或其他领域对象)对它的需求,这些需求往往暗藏了进一步揭露对象本质特征的提示。我们可以根据这些提示逐渐挖掘出该对象更多的行为特征,结合CA里相关的设计原则,最终我们可以确认领域对象该如何提供哪些行为方法。所以,大家不要在意领域对象在设计初期有点类似“贫血模型”,因为这不是它的最终形态,这只是它成长的起点。那如果经过一番尝试后,我们还是没有为对象添加任何方法呢?这种情况的确存在,有些对象存在的意义只是被别的对象引用,自身并没有提供任何行为方法,但是如果你的项目里大量充斥着没有行为的领域对象,这就值得你警惕了,肯定是设计上出现了重大的错误。不过只要大家依照教程里的设计原则实施项目,基本上不会出现这种情况,所以也不必太过担心。
因此,我们暂且放下对领域模型的思考,将思路转移到应用层,考虑应用层对Permission有什么需求。涉及到应用层我们又不得不考虑表现层对应用层会有哪些需求,最终我们会发现要思考的问题其实是终端用户会有什么需求。所以,我们考虑第一个问题:用户怎么使用权限机制?这个话题在前文也讨论过,这里我们只用考虑用户使用权限的第一个必须操作是什么?
当然是为项目创建权限描述了,我们需要定义整个项目提供了哪些权限,只有定义了权限信息后系统才能以此为参考识别登录者可以使用哪些功能。因此,创建权限信息是用户的第一个操作需要。但是我们要从使用者的角度对这个需求点进一步修正:“系统管理员可以定义整个项目提供了哪些功能”。系统管理员指的是项目搭建好后,设置项目初始参数的人,这类人比普通用户更加了解系统(往往是我们自己)。从UI界面操作体验上来讲,提示使用者“定义整个项目提供了哪些功能”比“定义整个项目提供了哪些权限”要更容易理解。
分析到这里,我们就知道不论UI界面怎么描述,应用层都要提供“创建权限”的功能。所以我们以创建权限为示例讲述如何建立应用层的命令。
在CA的4层架构里,应用层由服务和子系统提供的命令组成。也就是说,AccountSubsystem里除了定义领域模型的内容外还会提供一组命令给外界使用,这组命令我们将其划分为应用层的范围。请大家注意,CA里的层次结构是一个概念而不是真实的物理布局。虽然不同的层次往往物理布局不一样,但是也有例外的时候,这里的子系统提供的命令就是和领域模型在一个程序集里,但是所处不同的层次结构,命令属于应用层,领域对象属于领域模型层。(当然,你就算不理解这点也无所谓,对开发项目没什么影响。)
下面贴出创建权限的命令代码并作出说明:
using System; using System.Linq; using CodeArt; using CodeArt.DomainDriven; namespace AccountSubsystem { public sealed class CreatePermission : Command<Permission> { private string _name; public string Description { get; set; } public string MarkedCode { get; set; } public CreatePermission(string name) { _name = name; } protected override Permission ExecuteProcedure() { Permission permission = new Permission(Guid.NewGuid()) { Name = _name, MarkedCode = this.MarkedCode ?? string.Empty, Description = this.Description ?? string.Empty }; var repository = Repository.Create<IPermissionRepository>(); repository.Add(permission); return permission; } } }
1)public sealed class CreatePermission : Command<Permission>,类型CreatePermission继承自命令的泛型版本Command<T>,泛型参数表示该命令需要返回类型为T的结果,这里表示的是执行CreatePermission命令后,需要返回一个Permission权限对象。Command<T>来自CodeArt.DomainDriven命名空间,由程序集CodeArt.DomainDriven提供。CA规定除了查询操作外所有命令对象都需要继承自Command或Command<T>。
2) private string _name; 私有成员 _name用于接收外界传递的权限名称的参数,请注意,因为name是必填项,所以以构造函数参数的形式强制要求外界必须传递name。名称为什么必填? 这跟传统开发里表单必填项没有任何关系,而是因为领域模型Permission的属性Name定义的固定规则包含了不能为空的限制。
3)Description和MarekdCode以属性的形式并公开了GET和SET方法,表示外界可以为权限选择性填写描述和标识码。请注意这不是领域属性,这是命令为了接收参数而编写的.NET属性,事实上你可以为这两个属性任意命名(比如 desp或mc,不过考虑可阅读性我们没有这样命名)。这两个属性之所以不必填也是由于Permission对应的属性规则导致的,不再复述。
4)