CloudNotes之领域建模篇:领域模型简介
CloudNotes领域模型还是相对简单的,并不一定需要采用面向领域驱动的设计方法来解决CloudNotes的领域问题。但出于以下几个方面的原因,我还是采用了面向领域驱动的方式来开发CloudNotes:
- 领域驱动是企业级应用开发的一种指导性模型,以领域模型作为软件开发的中心,符合解决问题的基本思路
- 现有的企业级应用开发框架对面向领域的开发模式支持得越来越好,如果选用这种方式,可以在CloudNotes中更好地利用这些框架的最新功能,为系统开发寻求新的机遇
- 自己对领域驱动设计相对比较熟悉,而且也维护了一套自己研发的DDD开发框架Apworks。在CloudNotes中直接复用该框架,可以大大减小开发投入,缩短开发周期
- 温故而知新,选用DDD来指导CloudNotes开发,可以获取到有关DDD的更多信息
接下来,让我们一起对CloudNote的领域模型作些简单的了解。
基本模型
如果你使用的是Visual Studio 2013/2015旗舰版(Ultimate Edition),那么在打开CloudNotes解决方案后,你可以在CloudNotes.Design项目下,找到CloudNotesModel.classdiagram类图设计文件:
双击该文件,可以打开CloudNotes的领域模型设计视图。到目前为止,CloudNotes的领域模型如下:
咱们暂且不考虑C# class、aggregate root等这些UML构造型,这些内容我会在接下来的文章中详细介绍(顺带会介绍Visual Studio对于模型设计与自动化代码产生的支持)。从该图可以看出,CloudNotes的领域模型还是相对简单的。基本上可以分为三个大部分:笔记、用户以及客户包(ClientPackage)。或许你会考虑,是否可以将这些部分看成是DDD的界定上下文(Bounded Context)呢?
界定上下文(Bounded Context)
是的,我们可以考虑将CloudNotes的领域模型划分为三个界定上下文:
- 笔记界定上下文:管理系统中所有的笔记内容
- 用户认证与授权上下文:管理系统账户、角色以及权限
- 客户包管理上下文:管理针对所有客户端平台的升级包
从DDD角度看,由于模型被划分为多个上下文,而且上下文中的子模型也都是高内聚的(界定的),因此有可能会产生概念或语义上的二义性,而这又是通用语言(Ubiquitous Language)所不能容忍的。比如,一个经典的例子就是银行系统里“Account” 的概念:一个提供在线服务的银行系统,简单地说可以粗略地分为两个界定上下文:银行业务以及在线服务。对于银行业务而言,Account表示客户的银行账户,而对于在线服务而言,Account则又表示客户的在线登录账号,而且一个在线登录账号下可以承载多个银行账户,好比招商银行一网通账号可以有多个银行卡账户和信用卡账户等。那么在做系统设计的时候,遇到Account概念时,如何保证团队交流的准确性呢?因此,需要在领域模型中有一个能够解除这种二义性的“组件”,负责帮助团队人员在整个领域模型的理解与交流上保持一致。这种“组件”就是平时常说的“上下文映射(Context Map)”。有关界定上下文以及上下文映射的详细介绍,可以参考这篇文章:Strategic Domain Driven Design with Context Mapping。
当应用程序所需处理的领域变得很大时,引入界定上下文是很有必要的,从实践角度考虑,使用界定上下文不仅可以在一个相对封闭的范围内,使用一个相对较小的子领域模型,而且还可以从一定程度上提升应用程序的性能:比如某个操作只会发生在系统的某个子领域内部,又如较小的领域模型能够减少系统的处理开销。在Entity Framework 代码先行(Code-First)的开发过程中,可以很好地引入界定上下文的概念,以将复杂的领域模型划分成多个相对较小的简单的领域模型,不仅在模型设计还是在性能上,都有着很好的表现。有关这个话题的更多内容,可以参考我之前翻译的一篇文章:Entity Framework模型在领域驱动设计界定上下文中的应用。
CloudNotes的领域模型相对简单,而且虽然目前后台采用的是Entity Framework,但使用Entity Framework并不是必须的,今后有可能会换成其它的数据持久化系统(比如MongoDB),因此也没有按照上面所述的方式应用界定上下文的相关概念。
实体键
由于CloudNotes使用了Apworks框架,因此CloudNotes领域模型中的实体都是使用Guid作为实体键的。这一问题就要引入对Apworks框架的讨论。在Apworks框架中,我们可以看到以下代码:
public interface IEntity
{
/// <summary>
/// Gets or sets the identifier of the entity.
/// </summary>
Guid ID { get; set; }
}
在Apworks中,实体键是Guid类型的。有关实体键的类型选择,在Vaughn Vernon的《实现领域驱动设计》一书中,有相关的描述。概括起来,大致有以下几种情况:
- 用户指定实体键:这种方式最为直观:用户在界面上直接提供某个值,作为实体的键值。但通常而言,实体键值应该是不可变(immutable)的,于是,当用户为某个实体选定了实体键值后,一般是不允许修改的。但这样做又显得跟标准的用户操作流程不相符:我自己输入的东西,凭什么不让我改?另一种情况就是,用户可以修改实体键的值,但这种修改或许还会带来更多的影响,例如某个实体键发生变化时,由该实体发起的所有事件数据中针对该实体的引用也要发生改变。当然,如果应用程序需要保证实体键的可读性时,我们就不得不设计一套合理的机制,来确保可读性和可行性不会发生冲突
- 应用程序产生实体键: 很多应用程序会通过应用程序来产生实体键,比如通过应用程序开发框架提供的Guid/UUID生成算法来产生实体键。使用Guid不仅简单而且相对比较高效,更重要的是,它不需要依赖于应用程序以外的任何外部机制,比如持久化系统等。此外,正如《实现领域驱动设计》一书中所述,有些对性能要求很高的系统,会在一个独立的服务器上(或者有可能是一个后台的服务上)缓存一定数量的ID值,使用Guid就不会产生因ID服务器崩溃或重启而出现ID值重复或不连续的情况,因为Guid的生成程序始终能够保证ID值的唯一性。另一方面,如果需要通过应用程序来产生实体键值,那么我们就需要完美地解决ID值的同步问题,而且还需要考虑性能问题,更进一步,企业级应用系统往往都是由多个应用程序组成的,是否,以及如何保证实体键在这些应用之间的统一,也是一件不容易的事情
- 借助持久化机制产生实体键:这种情况非常常见,例如ORM系统会借助关系型数据库为实体设置键值。这也是一种简单方便的ID值获取方式,因为可以借用持久化机制来获得ID值缓存的功能,而且还能保证ID值的唯一性。但这往往也并不高效,毕竟需要借助外部系统,持久化机制的性能表现,也将对应用程序本身的性能造成影响甚至成为性能瓶颈。对于大型分布式企业级应用解决方案而言,也存在ID值的同步和统一的问题,例如应用程序A和B采用了不同的持久化机制,然而却无法保证原本应该保持一致的实体键值
- 外部界定上下文指定实体键:这种方式并不常见,在大型的企业级应用系统解决方案中可以用到,但也并不一定会采用这种方式。它是通过领域事件的方式,在界定上下文之间进行信息传递,比如本地界定上下文(Local Bounded Context)通过订阅来自外部界定上下文的领域事件,并通过事件中的信息,以在本地界定上下文中还原实体,于是ID值也一并被还原过来,以便表示当前实体与外部界定上下文中另一对象是同一实体
很显然,Apworks采用的就是上述Guid的方式,这样做实现起来比较简单高效,而且Guid值类型的特性,在绝大多数存储系统中都能很好地支持。当然它也有一些弊端,比如可读性差,以及在某些存储系统中性能并不算太好等。无论如何,就Apworks框架本身而言,应该提供一套实体键产生的框架,以便开发人员能够扩展ID值的产生机制,而不应该将ID值类型限定在Guid之上。这在老版本的Apworks中是有类似机制的,只是在考虑到装箱、拆箱带来的性能问题后,就在新的版本中,把这种机制给取消了,也就演变到目前的这种情形。今后我还是会继续改进这部分的设计的。
总结
CloudNotes的领域模型相对简单,实现上采用了Apworks框架,并借用了Visual Studio Ultimate的Architecture功能,对领域模型进行可视化设计和自动化代码生成。或许在今后领域模型会进一步修改,或者逐渐变大,或许也会由此而采用一些新的技术,并产生新的解决方案。到目前为止,CloudNotes的领域模型规模还是很小的,本系列文章也会首先针对当前的这个模型来进行介绍。
CloudNotes之领域建模篇:领域模型简介的更多相关文章
- iOS开发UI篇—CALayer简介
iOS开发UI篇—CALayer简介 一.简单介绍 在iOS中,你能看得见摸得着的东西基本上都是UIView,比如一个按钮.一个文本标签.一个文本输入框.一个图标等等,这些都是UIView. 其实 ...
- iOS开发多线程篇—多线程简介
iOS开发多线程篇-多线程简介 一.进程和线程 1.什么是进程 进程是指在系统中正在执行的一个应用程序 每一个进程之间是独立的.每一个进程均执行在其专用且受保护的内存空间内 比方同一时候打开QQ.Xc ...
- SAP-ABAP系列 第一篇SAP简介
第一篇 SAP简介 SAP全名为System Application and Products in Data Processing.SAP目前是全世界排名第一的RP软件,号称“全球最大的企业管理解决 ...
- Hadoop入门第五篇:Hive简介以及部署
标签(空格分隔): Hadoop Hive hwi 1.Hive简介 之前我一直在Maxcompute上进行大数据开发,所以对数仓这块还算比较了解,在接受Hive的时候基本上没什么大的障碍.所以, ...
- Swift入门篇-swift简介
潜水博客园很多年,闲来无事,聊一下自己的经历,语文不好(如有什么错别字,请您在下评论)望您谅解,没有上过什么学的 在前期 ios入门篇 -hello Word(1) 文章中介绍我这半年准备写一些ios ...
- 《Genesis-3D开源游戏引擎完整实例教程-2D射击游戏篇:简介及目录》(附上完整工程文件)
G-3D引擎2D射击类游戏制作教程 游戏类型: 打飞机游戏属于射击类游戏中的一种,可以划分为卷轴射击类游戏. 视觉表现类型为:2D 框架简介: Genesis-3D引擎不仅为开发者提供一个3D游戏制作 ...
- 《Genesis-3D开源游戏引擎完整实例教程-跑酷游戏篇:简介及目录》(附上完整工程文件)
跑酷游戏制作 游戏类型: 此游戏Demo,为跑酷类游戏. 框架简介: 游戏通常由程序代码和资源组成.如果说模型.贴图.声音之类的可以给游戏环境提供一个物理描述和设置,那么脚本和代码块会给游戏赋予生命, ...
- mysql基础篇-----mysql简介
2017-04-19 一.mysql简介 MySQL是一个关系型数据库管理系统,由瑞典MySQL AB 公司开发,目前属于 Oracle 旗下产品.MySQL 是最流行的关系型数据库管理系统之一,在 ...
- Python【初识篇】简介
python是什么? 为什么学python? python在权威语言排序网站上的热度 python历史排名 python应用领域 哪些公司在用python python官方简介 上面的话简单的总结来说 ...
随机推荐
- HTML kbd键盘元素
1. 说明 kbd :即Keyboard Input Element(键盘输入元素).表示键盘按键的语义元素,常用于网页上对快捷键.按键说明的场景. 样式规格:内联样式. 为了在页面上突出显示,可以给 ...
- android键盘
在应用的开发过程中有不少的情况下会用到自定义键盘,例如支付宝的支付密码的输入,以及类似的场景.android系统给开发者们提供了系统键盘,KeyboardView,其实并不复杂,只是有些开发者不知道罢 ...
- 一起学 Java(二)面向对象
一.方法函数 函数也称为方法,就是定义在类中的具有特定功能的一段独立代码.用于定义功能,提高代码的复用性. 函数的特点1> 定义函数可以将功能代码进行封装,便于对该功能进行复用:2> 函数 ...
- iOS有关横向TableView的东西
之前看到Apple store里面有横向的tableview,当然也有可能是collectionview啦. 尤其是项目中只有一条那么需要横向滑动的东西,就没有必要使用庞大的collectionvie ...
- C#项目中文件的具体含义
1.Bin 目录 用来存放编译的结果,bin是二进制binary的英文缩写,因为最初C编译的程序文件都是二进制文件,它有Debug和Release两个版本,分别对应的文件夹为bin/Debug和bin ...
- error C4430:missing type specifier 解决错误
错误 3 error C4430: missing type specifier - int assumed. Note: C++ does not support default-int ...
- 你所能用到的BMP格式介绍
原理篇: 一.编码的意义. 让我们从一个简单的问题开始,-2&-255(中间的操作符表示and的意思)的结果是多少,这个很简单的问题,但是能够写出解答过程的人并不 多.这个看起来和图片格式没有 ...
- ABP创建数据库操作步骤
1 ABP创建数据库操作步骤 1.1 SimpleTaskSystem.Web项目中的Web.config文件修改数据库配置. <add name="Default" pro ...
- JavaScript事件代理和委托(Delegation)
JavaScript事件代理 首先介绍一下JavaScript的事件代理.事件代理在JS世界中一个非常有用也很有趣的功能.当我们需要对很多元素添加事件的时候,可以通过将事件添加到它们的父节点而将事件委 ...
- es6小白学习笔记(一)
1.let和const命令 1.es6新增了let和const命令,与var用法类似,但它声明的变量只在let所在的代码块内有效(块级作用域,es5只有全局和函数作用域) { let a = 1; v ...