【领域驱动设计】.NET实践:立足领域

    简单的说,软件开发的目的就是通过计算机解决某一领域的实际问题。这样的定义已经将我们的立足点置于领域层面了:我们需要关注的是领域本身,而不是其它的技术细节。很多人拿到需求,很喜欢从建立数据模型开始,画出数据模型图、ER图,考虑数据库表的结构,以便开始软件设计。比如,假设现在要设计一套简单的学生成绩管理系统,就管理学生各科的成绩,我们都会毫不犹豫的设计三个数据表:Students(用于保存学生信息)、Courses(用于保存学生所学的科目)以及Scores(用于保存某个学生在某个科目上的成绩)。客观的讲,这种做法不能算错,至少对于交付期很短的项目来说,这样做能够更快地达到目的;但从软件本身的角度看,这样做已经本末倒置了。

    早在两年前,我有简单的写过一篇短文,题目是《软件系统设计一定要从数据库设计开始吗?》,有网友评论说,感觉就从来没有从数据库设计开始过,这样很好,证明起码出发点没有错。在这篇文章里我写的比较简单,可能问题没有得到阐明,现在让我们从另一个角度去考虑问题的本质。正如本文开头所述,我们使用软件来解决领域问题,那么领域问题如何映射到软件系统(软件领域)就成了整个软件设计的关键。根据面向对象理论,我们可以通过对象及其之间的关系来反映领域,以面向对象语言来描述领域,这就使得领域问题能够以最自然的方式“翻译”成软件语言。因此,我们接触得最多的就是这些领域对象,因为只有它们才能合理、准确地在软件范围内将领域问题表述清楚。根据DDD,领域对象可以划分为实体、值对象和服务。既然是对象,必然有其生命周期,就会有“创建”、“使用”、“保存”、“取出”、“撤销”等生命周期状态;工厂和仓储管理了领域对象的生命周期。

    现在再来考虑数据库在整个系统中的位置。在DDD中,仓储用来存放、查找领域对象,并在需要时通过保存的数据重建领域对象。使用仓储最大的好处是,领域模型完全不需要考虑数据保存的细节问题(比如如何保存、保存在哪里),从而使领域模型独立于基础结构层。因此,如果我们的数据保存机制选用数据库的话,仓储就会与数据库打交道,将领域对象保存到数据库里。至此,你会发现,数据库并不是位于软件系统的中心位置,甚至可以说,数据库显得并不那么重要了,数据库只不过是一种保存领域对象的机制。从仓储的角度看,它可以把领域对象保存到数据库,当然还能够保存到文件,更极端一点,假设我们的应用服务器7x24不当机,我们可以直接抛弃数据库,让仓储将领域对象保存在内存里,这将大幅度提高系统的性能。因此,仓储为领域模型提供对象的保存、读取、查询等数据服务,而数据库不过是一种技术选择,它位于DDD 4层中的基础结构层,它的主要任务是保存数据,它根本没办法去描述领域问题。

    对象的关系很丰富:继承、实现、聚合、组合等;数据库关系就相对简单了:1:n,n:m。如何将对象关系保存到数据库中,我们可以借助ORM来解决这样的问题,比如NHibernate。同样,ORM位于基础结构层,它不懂领域。在整个软件系统中,数据持久化那是ORM的事情,是用一个数据表保存领域对象,还是使用主从表外键关联的方式保存,都与我们无关。

       好了,到这里我想你也应该可以慢慢地走出数据库的阴影,回归到领域模型本身上。在设计领域模型时,我们又容易踏入另一个误区:认为领域模型就是简单的POCO(Plain Old CLR Objects)及其之间的关系集合。DDD提倡“富领域模型”,这就意味着,应该尽量将业务逻辑置于领域对象里,而对于那些理论上不属于任何领域对象的业务逻辑,应将其置于服务中。这样做的好处是很明显的:因为领域对象是现实世界的面向对象表述,它不但具有属性,而且还应该有自己的行为,它的行为甚至还能触发一些其它的事情。那么什么时候使用POCO?POCO用于构建数据传输对象(DTO),以便能够集中表示数据并让数据在软件系统的各个层间自由传递,它还具有隐藏领域业务逻辑的功能。关于DTO,我会在后续的章节中讨论。

    现在来简单的看一下文章开始部分的那个例子,假如成绩管理系统需要计算每个学生的总分,我想我们的“学生”类大致可以有类似如下的定义:

view plaincopy to clipboardprint?
  1. public class Student : IEntity   
  2. {   
  3.     public Student()   
  4.     {   
  5.         this.DayOfBirth = DateTime.Now;   
  6.     }   
  7.   
  8.     /// <summary>   
  9.     /// 读取或设置学生的姓氏。   
  10.     /// </summary>   
  11.     public string LastName { getset; }   
  12.     /// <summary>   
  13.     /// 读取或设置学生的名字。   
  14.     /// </summary>   
  15.     public string FirstName { getset; }   
  16.     /// <summary>   
  17.     /// 读取或设置学生的出生日期。   
  18.     /// </summary>   
  19.     public DateTime DayOfBirth { getset; }   
  20.     /// <summary>   
  21.     /// 读取或设置学生的成绩列表。   
  22.     /// </summary>   
  23.     public IList<Mark> Marks { getset; }   
  24.   
  25.     /// <summary>   
  26.     /// 获得学生的年龄。   
  27.     /// </summary>   
  28.     public int Age   
  29.     {   
  30.         get { return DateTime.Now.Year - this.DayOfBirth.Year; }   
  31.     }   
  32.   
  33.     /// <summary>   
  34.     /// 计算学生的总成绩。   
  35.     /// </summary>   
  36.     /// <returns>总成绩。</returns>   
  37.     public float GetTotalScore()   
  38.     {   
  39.         float total = 0;   
  40.         foreach (Mark mark in this.Marks)   
  41.         {   
  42.             total += mark.CourseMark;   
  43.         }   
  44.         return total;   
  45.     }   
  46.   
  47.     public override string ToString()   
  48.     {   
  49.         return string.Format("{0}{1}",   
  50.             this.LastName, this.FirstName);   
  51.     }  
  52.  
  53.     #region IEntity Members   
  54.     /// <summary>   
  55.     /// 读取或设置学生的编号。   
  56.     /// </summary>   
  57.     public Guid Id { getset; }  
  58.     #endregion   
  59. }   

    本文主要阐述了两个观点:1、软件设计必须立足领域,以领域为关注核心,通过使用面向对象等手段尽可能合理地将领域问题映射到软件系统(领域模型)上;2、领域模型应该是“富领域模型”,否则无法完整、自然地表述领域问题。DDD实践有着很长的路要走,在实践的过程中,我们碰到的问题其实多数没有唯一准确的答案,只能说哪种答案更为合理。作为一名软件人员,我也深刻能够体会到大多数软件人员更喜欢将精力放在解决技术问题上,这样会更容易地得到他人对自己的赏识,然而,当我们真正需要设计一个软件系统时,我们不得不更多地去学习领域知识、关心领域问题,而这些内容大多是跟技术不搭界的。Eric Evans有句话说的很有意思:"Developers get interested in the domain when the domain is technical."(Domain-Driven Design: Discussion by SVPG),意思是,其实开发人员也关注领域,但是是技术领域。立足领域,DDD之道。

 

转载于:https://www.cnblogs.com/daxnet/archive/2009/03/10/1686985.html

【实例简介】 项目采用经典DDD架构(用沃恩.弗农大神的话,其实这是DDD-Lite)思想进行开发,简洁而不简单,实用至上,并且所写每一行代码都经过深思熟虑,符合SOLID规则! ####当前版本 3.0 alpha版(2017-2-7) 采用全新工作流,实现自定义表单处理; 2.0版(2016-10-31) 支持多流程模板; 增加Ace admin界面支持 秀外 输入图片说明 输入图片说明 输入图片说明 慧中 教科书级的分层思想,哪怕苛刻的你阅读的是大神级精典大作(如:《企业应用架构模式》《重构与模式》《ASP.NET设计模式》等),你也可以参考本项目。不信?有图为证,Resharper自动生成的项目引用关系,毫无PS痕迹! 输入图片说明 实用 符合国情的RBAC(基于角色的访问控制),可以直接应用到你的系统。 权限资源 菜单权限 经理和业务员登陆系统拥有的功能菜单是不一样的 按钮权限 经理能够审批,而业务员不可以 数据权限 A业务员看不到B业务员的单据 字段权限 某些人查询客户信息时看不到客户的手机号或其它字段 用户应用系统的具体操作者,我这里设计用户是可以直接给用户分配菜单/按钮,也可以通过角色分配权限。 角色为了对许多拥有相似权限的用户进行分类管理,定义了角色的概念,以上所有的权限资源都可以分配给角色,角色和用户N:N的关系。 机构树形的公司部门结构,国内公司用的比较多,它实际上就是一个用户组,机构和用户设计成N:N的关系,也就是说有时候一个用户可以从属于两个部门,这种情况在我们客户需求中的确都出现过。 ####系统工程结构: OpenAuth.Domain 系统领域层 OpenAuth.Repository 系统仓储层,用于数据库操作 OpenAuth.App 应用层,为界面提供接口 OpenAuth.Mvc 采用基于jquery与bootstrap的B-JUI界面 OpenAuth.UnitTest 单元测试 Infrastructure 通用工具集合 ####使用 管理员可直接在登录界面点击基于精典DDD的权限管理 - 点击以开发者账号登录登录; 普通应用账号使用:test(密码:test)登录; ####后续 更多狂野的功能,正在玩命加载中,敬请期待... 更多文档正在整理中.... 当然,如果你想学习完整的DDD框架,可以参考我的另一个项目(BestQ&A--开源中国推荐项目/集CQRS AES等DDD高级特性于一体的问答系统) 【实例截图】
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值