《Pro ASP.NET MVC 3 Framework》学习笔记之四【领域模型介绍】

主题:应用领域驱动开发(Applying Domain-Driven Development)

Domain Model是MVC程序的"心脏",其他的一切,包括Controllers和Views仅仅是用来跟Domain Model交互的一种方式,ASP.NET MVC并没有限制使用在Domain Model上面的技术,我们可以自由的选择跟.net framework交互的技术,并且这样的选择是非常多的。不仅如此,ASP.NET MVC为我们提供了基础的架构和约定来帮助Domain Model里面的Classes跟Controllers和Views的联系,也包括跟MVC自己的联系。

它们有三个关键的功能,如下所示:

a.模型绑定(Model Binding):是自动使用HTML表单提交的数据来组织领域对象(Domain Objects)基础的约定。

b.模型元数据(Model metadata):描述了Model Classes对.net的所要表达的意思。举例而言,就是给属性赋予一个人容易理解的表述或者是对属性呈现时的一些提示。asp.net mvc能够自动的识别并对Model Classes合理的呈现到Views里面。

c.验证(Validation):能够在模型绑定时被执行,并且能够应用被定义为元素据的那些规则。

如果你对于模型绑定(Model Binding)的理解也跟我一样还不是很清楚,也没有必要着急,更不要放弃对MVC的学习,因为书的后面章节会有详细的讲解 ,呵呵。现在我们先将ASP.NET MVC的实现放到一边,单纯思考下领域建模(Domain Modelling),下面使用.net和sql server创建一个简单的竞拍程序的领域模型。

一,创建一个简单的领域模型

下面是要创建的竞拍程序的类图


上面的模型包含了一个Members的集合,每一个Member会有一个Bids集合,每一个Bid对应一个Item,每一个Item可以有多个来自不同Members的Bids

实现我们自己的domain model并作为一个独立的组件其中一个关键的地方是我们选择的语言和术语,这个不是我们的编程语言,而是领域建模的通用的语言。这种语言是开发人员和领域的专业人员都知道的,这样主要是为了这两种人能流畅的交流,而这却是至关重要的。当领域的专家们不了解建模的一些概念时,我们应该针对使用的术语达成一个共识,这个共识就是创建一种通用的语言并贯穿在整个领域建模过程中。这样做有很多的好处。

1.首先,开发人员倾向于使用编程语言,比如类名,数据库等等名词来表达。而业务专家们是不懂这些的,他们也不需要懂。业务专家知晓一些技术方面的知识是一件非常危险的事情,因为他们会经常根据自己对技术的理解来不断筛选他们的需求,这也就意味着需求会频繁的更改,进而导致开发人员也不知道业务专家的真实需求到底是什么。创建通用语言的方法能够帮助我们避免在一个应用程序里面过度的泛化需求,程序员倾向于建立每一个可能业务实际模型,而不是具体到某一个业务需求。

2.在通用语言和领域模型之间的这种连接不应该是非常肤浅的而是向DDD(Domain-Driven Design)专家所建议的那样:对通用语言的任何变化都会导致Model的变化。假如我们让建模跟业务领域不同步,我们就需要建立一种从Model到domain映射的中间语言,从长远来看,这种做法会导致灾难。为此,我们将创建一个会两种语言的特殊人类,他们随后就开始筛选需求,这却是建立在他们对两种语言都不完全理解的基础之上,当然这样的后果可以想象。

二,聚合与简化

上面的图,为我们提供了一个很好的建模起点
但是上面图示的模型并没有提供用C#和SQL Server实现Model的任何有用的帮助,会有不少的问题困扰我们:

1.如果我们load一个Member进入内存,也是不是应该load Member的Bids以及相关的Items进入内存呢?

2.如果我们这样做了,我们是否需要将这个Item其他的bids也load进内存,并且也将做这些Bids的Members一同load进内存呢?

3.当我们删除一个对象时,我是否应该删除相关的对象呢?如果是,又有哪些呢?

4.如果我们选择用文档存储代替关系型数据库来持久化,那哪一个对象的集合应该呈现到同一个文档呢?

所有以上的问题,我们不知道作何解答,并且我们的领域模型也没有给我们任何答案。

回答这些问题的DDD方式是将Domain Objects分配到组里面,这种方式称为聚合(aggregates)。

下图很好的展示了怎样聚合在我们这个竞拍程序里面的领域模型,如下所示:


一个聚合的实体组将若干领域对象联合(Together)到了一起,有一个根实体被用来标识整个聚合,它在验证和持久化操作里面充当了"boss"的角色。在数据变化时,我把聚合当作一个单元来统筹处理,所以我们需要创建呈现在领域模型上下文里面有意义的关系的聚合,并且创建跟实现业务过程一致的逻辑操作。也就是说,我们需要通过分组对象来创建聚合,而这些对象是可以作为一个组被改变的。

一个非常重要的DDD规则是,在一个聚合实例范围外的对象,只能通过对根实体(root entity)的引用来持久化,而不是引用在聚合里面的对象。这条规则强化了将聚合里面的对象作为一个单元来对待的概念。在本例子里面,Members和Items都是聚合的根,而Bids只能在作为它们聚合根实体的Item的上下文(the context of Item)里面被访问。Bids可以引用Members(根实体),但是Members不能引用Bids(不是根实体)

聚合的一个好处是简化了对象跟领域模型之间的关系,通常这样能够帮助我们对需要建模的领域的本质的理解。本质上讲,创建聚合约束领域模型和对象之间的关系使得这种关系更加接近于现实领域里面存在的关系。下面是用C#来表达的,如下所示:

public class Member {
public string LoginName { get; set; } // The unique key
public int ReputationPoints { get; set; }
}
public class Item {
public int ItemID { get; private set; } // The unique key
public string Title { get; set; }
public string Description { get; set; }
public DateTime AuctionEndDate { get; set; }
public IList<Bid> Bids { get; set; }
}
public class Bid {
public Member Member { get; set; }
public DateTime DatePlaced { get; set; }
public decimal BidAmount { get; set; }
}

从上面的代码可以看出,我们很容易就捕捉到了Bids和Members之间的单向关系的本质,当然我们也可以建立一些其他的约束。例如:Bids是不可变的,这也是符合实际的。应用聚合能够帮助我们建立更加有用,更加精确的领域模型,也能够让我们用C#熟练的实现。

一般来讲,聚合为一个领域模型增加了结构化和精确化。这也使得应用验证变得容易(根实体负责验证聚合里面所有对象的的状态),这很明显也是持久化的单元。由于聚合的本质就是领域模型的原子单元,它们也能够适用事务管理的单元和级联从数据库删除的单元。

另一方面,聚合常常是人为加上限制。聚合(Aggregates)的概念能够很自然的从文档型数据库得到,但它不是sqlserver本身的概念,也不是存在大部分ORM工具里的概念,为了很好的实现它们,我们的团队需要科学有效的沟通。

三,定义仓库(Defining Repositories)

在某些时候,我们需要给领域模型添加持久化,这通常是通过关系型,对象型,或文档型的数据库来做。持久化不是我们领域模型的一部分,它是一个独立的关注点,这也就意味着我们不能将持久化的代码跟定义领域模型的代码混合到一起。解决这个问题通常的方式就是定义一个仓库(Repositories)

Respositories是基于数据库(可能你选择的是文件存储等等)层面的对象呈现。领域模型通过调用定义在Repositories的方法来间接的存储和查询数据库,这使得我们的Model可以独立于持久化的实现。这样约定就是为每一个聚合(Aggregates)定义单独的数据模型。在我们的竞拍程序里面,我们可以创建2个Repositories,它们分别是针对Members的Repository和针对Items的Repository。注意这里,我们并不需要创建针对Bids的Repository,因为Bids会作为Items聚会持久化的一部分)。

下面是定义的两个Repositories,如下所示:

public class MembersRepository {
public void AddMember(Member member) { ... }
public Member FetchByLoginName(string loginName) { ... }
public void SubmitChanges() { ...}
}
public class ItemsRepository {
public void AddItem(Item item) { ...}
public Item FetchByID(int itemID) { ... }
public IList<Item> ListItems(int pageSize,int pageIndex) { ...}
public void SubmitChanges() { ... }
}

特别需要注意:Repositories仅仅针对Loading和Saving Data.它们不包含任何其他的逻辑。

今天的笔记做到这里,我也是刚学习MVC,做笔记是为了巩固和加深理解,当然如果能给到那些跟我一样的初学者一点点的帮助,我就非常高兴了。笔记里面肯定会有我理解不对的地方,还请路过的大牛们多多帮助指导。谢谢!
祝路过的朋友工作顺利!

晚安!

转载于:https://www.cnblogs.com/mszhangxuefei/archive/2011/12/04/mvcnotes_4.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/380053.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Java——集合框架(List)

集合框架&#xff08;List的三个子类的特点&#xff09; A&#xff1a;List的三个子类的特点 ArrayList&#xff1a; 底层数据结构是数组&#xff0c;查询快&#xff0c;增删慢。 线程不安全&#xff0c;效率高。 Vector&#xff1a; 底层数据结构是数组&#xff0c;查询…

一步一步学pwntools(适合新手)

序 pwntools是一个二进制利用框架。官方文档提供了详细的api规范。然而目前并没有一个很好的新手教程。因此我用了我过去的几篇writeup。由于本文只是用来介绍pwntools使用方法&#xff0c;我不会过于详细的讲解各种二进制漏洞攻击技术。 Pwntools的“Hello World” 栈溢出无…

关于J2EE中死锁问题的研究(1)

大多数重要的应用程序都涉及高度并发性和多个抽象层。并发性与资源争用有关&#xff0c;并且是导致死锁问题增多的因素之一。多个抽象层使隔离并修复死锁环境的工作变得更加困难。 通常&#xff0c;当同时执行两个或两个以上的线程时&#xff0c;如果每个线程都占有一个资源并请…

python变量分配内存_Python | 声明任何变量而不分配任何值

python变量分配内存Since, Python is a dynamic programming language so there is no need to declare such type of variable, it automatically declares when first time value assign in it. 由于Python是一种动态编程语言&#xff0c;因此无需声明此类变量&#xff0c;它…

UVA 10004 - Bicoloring

模拟染色&#xff0c;因为只有两种颜色&#xff0c;所以分别用 0、 1 代表这两种颜色&#xff0c;然后从0开始深搜&#xff0c;如果 每个点都能染上色&#xff0c;且相邻两点的颜色不同&#xff0c;则符合要求。 #include<stdio.h>#include<string.h>#define MAXN …

标志寄存器:CF、PF、AF、ZF、SF、TF、DF、OF

注&#xff1a;下面说到的标志寄存器都是缩写&#xff0c;C就是CF&#xff0c;其他也一样 标志寄存器&#xff1a;C、P、A、Z、S、T、D、O的内容只会是0或1&#xff0c;0表示假&#xff0c;1表示真 O&#xff1a;溢出标志 一个寄存器如果存放的值超过所能表示的范围&#xf…

揭秘:销售人员26个致命弱点

销售人员有许多积极的态度需要学习&#xff0c; 同时也有许多不良的习惯应该避免&#xff0c;以免影响个性及专业能力。仔细看看这些缺点&#xff0c;反省你自己&#xff0c;还需要改善的画&#xff0c;直到你给自己一百分为止。找一位深知你的好 友&#xff0c;让他诚实地给你…

Java——集合(练习题)

例题1&#xff1a;产生10个1~20之间的随机数&#xff0c;要求随机数不能重复 import java.util.HashSet; import java.util.Random; public class Test1 {/*** 产生10个1~20之间的随机数&#xff0c;要求随机数不能重复* * 分析&#xff1a;* 1,有Random类创建随机数对象* 2&a…

模块化 组件化 工程化_软件工程中的模块和软件组件

模块化 组件化 工程化The module in software is a small part of the software that is responsible for performing any kind of functionality. Sometimes, the term sub-program is also used to refer to the term module. 软件中的模块是软件的一小部分&#xff0c;负责…

Firefox2狂占CPU解决办法

https://images.cnblogs.com/cnblogs_com/Tisty/138006/o_firefox3.jpg 看了一下&#xff0c;不知道 "jpeg_free_large" 是干啥的&#xff0c;遂用 "Firefox jpeg_free_large" Google 一下&#xff0c;出来的一堆东西里有帖子说可能和 Apple 的 QuickTime …

PUSHAD和POPAD,以及PUSHA和POPA

PUSHAD PUSHAD也叫保护现场&#xff0c;就是把我们的寄存器压入栈中 pushad是把eax&#xff0c;ecx&#xff0c;edx&#xff0c;ebx&#xff0c;esp、ebp&#xff0c;esi&#xff0c;edi依次压入栈中&#xff0c;ESP会减少32&#xff0c;相当于&#xff1a; push eax push ec…

Java——n个数的全排列

例题&#xff1a; 输入一串字符串&#xff0c;将该字符串中的字符元素进行全排列&#xff0c;然后&#xff0c;一串输出结果。 例如&#xff1a; 输入&#xff1a; ABCD 输出&#xff1a; ABCD ABDC ACBD ACDB ADBC ADCB BACD BADC BCAD BCDA BDAC BDCA CABD CADB CBAD CBDA…

一段个性化stringgrid的代码

需要注意的是 该段程序使用了 canvas。 procedure TW_CkbTaiZhang.KhLstDrawCell(Sender: TObject; ACol,ARow: Integer; Rect: TRect; State: TGridDrawState);begin if ARowkhlst.Row then with khlst.Canvas do //画 cell 的边框 begin Pen.Color : $00ff0000; …

dp 扔鸡蛋_使用动态编程(DP)的鸡蛋掉落问题

dp 扔鸡蛋Problem statement: You are given N floor and K eggs. You have to minimize the number of times you have to drop the eggs to find the critical floor where critical floor means the floor beyond which eggs start to break. Assumptions of the problem: 问…

MOVSX和MOVZX

MOVSX 先符号扩展,再传送 格式&#xff1a; MOVSX 操作数A &#xff0c;操作数B //操作数B的空间小于A比如说我们使用命令&#xff1a; movsx eax&#xff0c;bxbx是16位&#xff0c;eax是32位&#xff0c;传值过程&#xff1a; 先用bx的符号位把eax高16填满&#xff0c;b…

统计学习以及支持向量机的国内外基本比较重要的书

1、支持向量机导论&#xff0c;此书乃是SVM方面的经典著作&#xff0c; 该书的作者也是近年来SVM、kernel methods学术圈内的活跃学者&#xff0c;对于这些领域均有过重要的贡献。这本书从“线性机器、核方法、统计学习理论、凸优化”四个方面揭示了SVM的内在机理 --利用核…

Java——集合(TreeSet)

package com.wsq.set; //这里进行调用Person()方法&#xff0c;要进行导包 import java.util.TreeSet; import com.wsq.bean.Person; public class Demo3_TreeSet { /*** TreeSet集合是用来对元素进行排序的&#xff0c;同样它也可以保证元素的唯一* 当compareTo()方法返…

setmonth_日期setMonth()方法以及JavaScript中的示例

setmonthJavaScript日期setMonth()方法 (JavaScript Date setMonth() method) setMonth() method is a Date class method, it is used to set the month to the Date object with a valid month value (between 0 to 11. 0 for January, 1 for February and so on). setMonth(…

LEA与XCHG

LEA 格式&#xff1a; LEA 通用寄存器 内存地址功能&#xff1a;取地址命令 将内存地址赋值给寄存器 lea eax,dword ptr ds:[ecx0x16]dword 双字 就是四个字节ptr pointer缩写 即指针ds 数据段版寄存器[]里的数据是一个地址值&#xff0c;这个地址指向一个双字型数据 将dwo…

分域名优化的时候要考虑备选IP的问题

我们在需要下载很多内容的时候&#xff0c;很容易想到做分域名的并发下载&#xff0c;给原来的服务器多分几个域名&#xff0c;因为分不同的域名可能可以在浏览器中分到更多的下载进程&#xff0c;提高下载速度。 但是在做网络应用的时候&#xff0c;我们的一个域名下面有的时候…