CloudNotes领域模型还是相对简单的,并不一定需要采用面向领域驱动的设计方法来解决CloudNotes的领域问题。但出于以下几个方面的原因,我还是采用了面向领域驱动的方式来开发CloudNotes:

  1. 领域驱动是企业级应用开发的一种指导性模型,以领域模型作为软件开发的中心,符合解决问题的基本思路
  2. 现有的企业级应用开发框架对面向领域的开发模式支持得越来越好,如果选用这种方式,可以在CloudNotes中更好地利用这些框架的最新功能,为系统开发寻求新的机遇
  3. 自己对领域驱动设计相对比较熟悉,而且也维护了一套自己研发的DDD开发框架Apworks。在CloudNotes中直接复用该框架,可以大大减小开发投入,缩短开发周期
  4. 温故而知新,选用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之领域建模篇:领域模型简介的更多相关文章

  1. iOS开发UI篇—CALayer简介

    iOS开发UI篇—CALayer简介   一.简单介绍 在iOS中,你能看得见摸得着的东西基本上都是UIView,比如一个按钮.一个文本标签.一个文本输入框.一个图标等等,这些都是UIView. 其实 ...

  2. iOS开发多线程篇—多线程简介

    iOS开发多线程篇-多线程简介 一.进程和线程 1.什么是进程 进程是指在系统中正在执行的一个应用程序 每一个进程之间是独立的.每一个进程均执行在其专用且受保护的内存空间内 比方同一时候打开QQ.Xc ...

  3. SAP-ABAP系列 第一篇SAP简介

    第一篇 SAP简介 SAP全名为System Application and Products in Data Processing.SAP目前是全世界排名第一的RP软件,号称“全球最大的企业管理解决 ...

  4. Hadoop入门第五篇:Hive简介以及部署

    标签(空格分隔): Hadoop Hive hwi 1.Hive简介   之前我一直在Maxcompute上进行大数据开发,所以对数仓这块还算比较了解,在接受Hive的时候基本上没什么大的障碍.所以, ...

  5. Swift入门篇-swift简介

    潜水博客园很多年,闲来无事,聊一下自己的经历,语文不好(如有什么错别字,请您在下评论)望您谅解,没有上过什么学的 在前期 ios入门篇 -hello Word(1) 文章中介绍我这半年准备写一些ios ...

  6. 《Genesis-3D开源游戏引擎完整实例教程-2D射击游戏篇:简介及目录》(附上完整工程文件)

    G-3D引擎2D射击类游戏制作教程 游戏类型: 打飞机游戏属于射击类游戏中的一种,可以划分为卷轴射击类游戏. 视觉表现类型为:2D 框架简介: Genesis-3D引擎不仅为开发者提供一个3D游戏制作 ...

  7. 《Genesis-3D开源游戏引擎完整实例教程-跑酷游戏篇:简介及目录》(附上完整工程文件)

    跑酷游戏制作 游戏类型: 此游戏Demo,为跑酷类游戏. 框架简介: 游戏通常由程序代码和资源组成.如果说模型.贴图.声音之类的可以给游戏环境提供一个物理描述和设置,那么脚本和代码块会给游戏赋予生命, ...

  8. mysql基础篇-----mysql简介

    2017-04-19 一.mysql简介 MySQL是一个关系型数据库管理系统,由瑞典MySQL AB 公司开发,目前属于 Oracle 旗下产品.MySQL 是最流行的关系型数据库管理系统之一,在 ...

  9. Python【初识篇】简介

    python是什么? 为什么学python? python在权威语言排序网站上的热度 python历史排名 python应用领域 哪些公司在用python python官方简介 上面的话简单的总结来说 ...

随机推荐

  1. 手动添加kdump

    背景:     Linux嵌入式设备内核挂死后,无法自动重启,需要手动重启.而且如果当时没有连串口的话,就无法记录内核挂死时的堆栈,所以需要添加一种方式来记录内核挂死信息方便以后调试使用.设备中增加k ...

  2. web服务器集群

    概述 集群和分布式都是从集中式进化而来的.分布式和集群会相互合作的,同时的集群和分布式.在这里重点说说集群 集群是什么? 集群能提高单位时间内处理的任务数量,提升服务器性能 有多台服务器去处理任务,但 ...

  3. javascript arguments(转)

    什么是arguments arguments 是是JavaScript里的一个内置对象,它很古怪,也经常被人所忽视,但实际上是很重要的.所有主要的js函数库都利用了arguments对象.所以agru ...

  4. BAT“搅局”B2B市场,CIO们准备好了吗?

    "CIO必须灵活构建其所在企业的IT系统,深入业务,以应对日新月异的数字化业务环境."   BAT军团"搅局"B2B市场,CIO们准备好了吗? 庞大的企业级市场 ...

  5. 数据库 DML、DDL、DCL区别 .

    总体解释: DML(data manipulation language): 它们是SELECT.UPDATE.INSERT.DELETE,就象它的名字一样,这4条命令是用来对数据库里的数据进行操作的 ...

  6. Tomcat之APR错误

    在发布Apache Tomcat的时候,突然出现如下错误: An incompatible version 1.1.31 of the APR based Apache Tomcat Native l ...

  7. Electron 不完全快速手册

    Electron能干嘛 Vscode 基于Electron开发的,他是用来开发桌面软件的,可以轻易的跨平台 他的前身是atomshell,图标很丑,不用在意,一点也不像vscode也不用在意.   L ...

  8. android内部培训视频_第三节(3)_常用控件(ViewPager、日期时间相关、ListView)

    第三节(2):常用控件之ViewPager.日期时间相关.ListView  一.ViewPager 实例:结合PagerAdapter滑动切换图片  二.日期时间相关:AnalogClock\Dig ...

  9. 如何开发一个简单的HTML5 Canvas 小游戏

    原文:How to make a simple HTML5 Canvas game 想要快速上手HTML5 Canvas小游戏开发?下面通过一个例子来进行手把手教学.(如果你怀疑我的资历, A Wiz ...

  10. some OpenGL constants

    some OpenGL constants This is from (https://github.com/peterderivaz/pyopengles/blob/master/gl2.py) G ...