领域驱动设计案例:Tiny Library:领域模型
2010-10-27 16:32
549 查看
本讲主要介绍基于Entity Framework的领域驱动设计建模。首先回顾一下Tiny Library的业务逻辑:
任何用户可以添加Library中的图书(简化起见,图书不能修改也不能删除),也可以查看图书的详细信息 注册用户,也就是读者,可以借书、还书、查看自己借过的图书列表和借书信息
请注意上面描述的黑体部分,这些概念出现在Tiny Library的领域知识(Domain Knowledge)中,换言之,是Tiny Library领域的通用语言的组成元素。
一、实体与聚合根
首先分析出实体,不难看出,读者和图书是实体;由于每个读者都将有自己的借书信息(比如,什么时候借的哪本书,是否已经归还,或者是否已经过期),而与之对应地,每本书也可以有被借历史(比如,这本书是什么时候借给哪个读者),于是,借书信息也是实体。
再来看看聚合。借书信息是与读者和图书关联的,也就是说,没有读者,借书信息没有存在的意义,同样,没有图书,借书信息也同样不存在。每个读者可以没有任何借书信息(或者说借书记录),也可以有多条借书信息;而每本书也同样可以没有任何被借信息(或者说被借记录),也可以有多条被借记录。因此存在两个聚合:读者-借书信息聚合(1..0.*)以及图书-借书信息聚合(1..0.*)。读者和图书分别为聚合根,借书信息为实体。与Tiny Library对应起来,总结如下:
读者:Reader,聚合根 图书:Book,聚合根 借书信息:Registration,实体
根据上述描述,我们可以确定,我们将来需要针对读者(Reader)和图书(Book)实现仓储以及相应的规约。
.src_container{background-color:#e7e5dc; width:99%; overflow:hidden; margin:12px 0 12px 0 !important; padding:0px 3px 3px 0px}.src_container .titlebar{ background-color:#d4dfff; border:1px solid #4f81bd; border-bottom:0; padding:3px 24px; margin:0; width:auto; line-height:120%; overflow:hidden; text-align:left; font-size:12px}.src_container .toolbar{ display:inline; font-weight:normal; font-size:100%; float:right; cursor:hand; color:#00f; text-align:left; overflow:hidden}.toolbar span.button{ display:inline; font-weight:normal; font-size:100%; cursor:hand; color:#00f; text-align:left; overflow:hidden; cursor:pointer;}.src_container div.clientarea{ background-color:white; border:1px solid #4f81bd; margin:0; width:auto !important; width:100%; height:auto; overflow:auto; text-align:left; font-size:12px; font-family: "Courier New","Consolas","Fixedsys",courier,monospace,serif}.src_container ol.mainarea{ padding:0 0 0 52px; margin:0; background-color:#f7f7ff !important}.number_show{ padding-left:52px !important; list-style:decimal outside !important}.number_show li{ list-style:decimal outside !important; border-left:1px dotted #4f81bd}.number_hide{ padding-left:0px !important; list-style-type:none !important}.number_hide li{ list-style-type:none !important; border-left:0px}ol.mainarea li{ display:list-item !important; font-size:12px !important; margin:0 !important; line-height:18px !important; padding:0 0 0 0px !important; background-color:#f7f7ff !important; color:#4f81bd}ol.mainarea li pre{color:black; line-height:18px; padding:0 0 0 12px !important; margin:0em; background-color:#fff !important}.linewrap ol.mainarea li pre{white-space:pre-wrap; white-space:-moz-pre-wrapwhite-space:-pre-wrap; white-space:-o-pre-wrap; word-wrap:break-word}ol.mainarea li pre.alt{ background-color:#f7f7ff !important}function CopyCode(key){var codeElement=null;var trElements=document.all.tags("ol");var i;for(i=0;i.src_container{background-color:#e7e5dc; width:99%; overflow:hidden; margin:12px 0 12px 0 !important; padding:0px 3px 3px 0px}.src_container .titlebar{ background-color:#d4dfff; border:1px solid #4f81bd; border-bottom:0; padding:3px 24px; margin:0; width:auto; line-height:120%; overflow:hidden; text-align:left; font-size:12px}.src_container .toolbar{ display:inline; font-weight:normal; font-size:100%; float:right; cursor:hand; color:#00f; text-align:left; overflow:hidden}.toolbar span.button{ display:inline; font-weight:normal; font-size:100%; cursor:hand; color:#00f; text-align:left; overflow:hidden; cursor:pointer;}.src_container div.clientarea{ background-color:white; border:1px solid #4f81bd; margin:0; width:auto !important; width:100%; height:auto; overflow:auto; text-align:left; font-size:12px; font-family: "Courier New","Consolas","Fixedsys",courier,monospace,serif}.src_container ol.mainarea{ padding:0 0 0 52px; margin:0; background-color:#f7f7ff !important}.number_show{ padding-left:52px !important; list-style:decimal outside !important}.number_show li{ list-style:decimal outside !important; border-left:1px dotted #4f81bd}.number_hide{ padding-left:0px !important; list-style-type:none !important}.number_hide li{ list-style-type:none !important; border-left:0px}ol.mainarea li{ display:list-item !important; font-size:12px !important; margin:0 !important; line-height:18px !important; padding:0 0 0 0px !important; background-color:#f7f7ff !important; color:#4f81bd}ol.mainarea li pre{color:black; line-height:18px; padding:0 0 0 12px !important; margin:0em; background-color:#fff !important}.linewrap ol.mainarea li pre{white-space:pre-wrap; white-space:-moz-pre-wrapwhite-space:-pre-wrap; white-space:-o-pre-wrap; word-wrap:break-word}ol.mainarea li pre.alt{ background-color:#f7f7ff !important}function CopyCode(key){var codeElement=null;var trElements=document.all.tags("ol");var i;for(i=0;i 二、基于Entity Framework建立领域模型
目前Entity Framework支持三种建模方式:Model First、Database First以及Code First。Code First是在今年刚发布的Feature Pack中才支持的。为了迎合领域驱动设计思想,我们采用Model First。
根据上面的分析,现建模如下:
注意:如何在Visual Studio中使用Entity Framework进行Model First建模不是本文讨论的重点,读者朋友请自己参阅相关文档。
此时,我们需要使用C#部分类的特性,将Reader和Book定义为聚合根,将Registration定义为实体。我开发的一个DDD框架(Apworks)中为聚合根和实体的接口作了定义,现在,只需要引用Apworks的程序集,然后使用部分类的特性,让Reader和Book实现IAggregateRoot接口,让Registration实现IEntity接口即可。从技术上看,这样就将Apworks框架整合到了领域模型中。代码如下:
隐藏行号 复制代码 ? Reader聚合根
隐藏行号 复制代码 ? Book聚合根
隐藏行号 复制代码 ? Registration实体
三、添加业务逻辑
根据DDD,实体是能够处理业务逻辑的,应该尽量将业务体现在实体上;如果某些业务牵涉到多个实体,无法将其归结到某个实体的话,就需要引入领域服务(Domain Service)。Tiny Library案例业务简单,目前不会涉及到领域服务,因此,在本案例中,业务逻辑都是在实体上处理的。
以读者(Reader)为例,它有借书和还书的行为,我们将这两种行为实现如下:
隐藏行号 复制代码 ? Reader中的业务逻辑
.src_container{background-color:#e7e5dc; width:99%; overflow:hidden; margin:12px 0 12px 0 !important; padding:0px 3px 3px 0px}.src_container .titlebar{ background-color:#d4dfff; border:1px solid #4f81bd; border-bottom:0; padding:3px 24px; margin:0; width:auto; line-height:120%; overflow:hidden; text-align:left; font-size:12px}.src_container .toolbar{ display:inline; font-weight:normal; font-size:100%; float:right; cursor:hand; color:#00f; text-align:left; overflow:hidden}.toolbar span.button{ display:inline; font-weight:normal; font-size:100%; cursor:hand; color:#00f; text-align:left; overflow:hidden; cursor:pointer;}.src_container div.clientarea{ background-color:white; border:1px solid #4f81bd; margin:0; width:auto !important; width:100%; height:auto; overflow:auto; text-align:left; font-size:12px; font-family: "Courier New","Consolas","Fixedsys",courier,monospace,serif}.src_container ol.mainarea{ padding:0 0 0 52px; margin:0; background-color:#f7f7ff !important}.number_show{ padding-left:52px !important; list-style:decimal outside !important}.number_show li{ list-style:decimal outside !important; border-left:1px dotted #4f81bd}.number_hide{ padding-left:0px !important; list-style-type:none !important}.number_hide li{ list-style-type:none !important; border-left:0px}ol.mainarea li{ display:list-item !important; font-size:12px !important; margin:0 !important; line-height:18px !important; padding:0 0 0 0px !important; background-color:#f7f7ff !important; color:#4f81bd}ol.mainarea li pre{color:black; line-height:18px; padding:0 0 0 12px !important; margin:0em; background-color:#fff !important}.linewrap ol.mainarea li pre{white-space:pre-wrap; white-space:-moz-pre-wrapwhite-space:-pre-wrap; white-space:-o-pre-wrap; word-wrap:break-word}ol.mainarea li pre.alt{ background-color:#f7f7ff !important}function CopyCode(key){var codeElement=null;var trElements=document.all.tags("ol");var i;for(i=0;i 业务逻辑的添加仍然是在我们新建的partial class中,这样做的目的就是为了不让Entity Framework的代码自动生成器覆盖我们手动添加的代码。相应地,我们在Book和Registration中实现各自的业务逻辑(具体请参见案例源代码)。从TinyLibrary.Domain这个Project上看,TinyLibrary.edmx定义了基于Entity Framework的领域模型,而其它的几个C#代码文件则使用部分类的特性,分别针对每个实体/聚合根实现了一些业务逻辑。
下一讲将详细介绍基于TinyLibrary领域模型与Entity Framework的仓储的实现方式。
任何用户可以添加Library中的图书(简化起见,图书不能修改也不能删除),也可以查看图书的详细信息 注册用户,也就是读者,可以借书、还书、查看自己借过的图书列表和借书信息
请注意上面描述的黑体部分,这些概念出现在Tiny Library的领域知识(Domain Knowledge)中,换言之,是Tiny Library领域的通用语言的组成元素。
一、实体与聚合根
首先分析出实体,不难看出,读者和图书是实体;由于每个读者都将有自己的借书信息(比如,什么时候借的哪本书,是否已经归还,或者是否已经过期),而与之对应地,每本书也可以有被借历史(比如,这本书是什么时候借给哪个读者),于是,借书信息也是实体。
再来看看聚合。借书信息是与读者和图书关联的,也就是说,没有读者,借书信息没有存在的意义,同样,没有图书,借书信息也同样不存在。每个读者可以没有任何借书信息(或者说借书记录),也可以有多条借书信息;而每本书也同样可以没有任何被借信息(或者说被借记录),也可以有多条被借记录。因此存在两个聚合:读者-借书信息聚合(1..0.*)以及图书-借书信息聚合(1..0.*)。读者和图书分别为聚合根,借书信息为实体。与Tiny Library对应起来,总结如下:
读者:Reader,聚合根 图书:Book,聚合根 借书信息:Registration,实体
根据上述描述,我们可以确定,我们将来需要针对读者(Reader)和图书(Book)实现仓储以及相应的规约。
.src_container{background-color:#e7e5dc; width:99%; overflow:hidden; margin:12px 0 12px 0 !important; padding:0px 3px 3px 0px}.src_container .titlebar{ background-color:#d4dfff; border:1px solid #4f81bd; border-bottom:0; padding:3px 24px; margin:0; width:auto; line-height:120%; overflow:hidden; text-align:left; font-size:12px}.src_container .toolbar{ display:inline; font-weight:normal; font-size:100%; float:right; cursor:hand; color:#00f; text-align:left; overflow:hidden}.toolbar span.button{ display:inline; font-weight:normal; font-size:100%; cursor:hand; color:#00f; text-align:left; overflow:hidden; cursor:pointer;}.src_container div.clientarea{ background-color:white; border:1px solid #4f81bd; margin:0; width:auto !important; width:100%; height:auto; overflow:auto; text-align:left; font-size:12px; font-family: "Courier New","Consolas","Fixedsys",courier,monospace,serif}.src_container ol.mainarea{ padding:0 0 0 52px; margin:0; background-color:#f7f7ff !important}.number_show{ padding-left:52px !important; list-style:decimal outside !important}.number_show li{ list-style:decimal outside !important; border-left:1px dotted #4f81bd}.number_hide{ padding-left:0px !important; list-style-type:none !important}.number_hide li{ list-style-type:none !important; border-left:0px}ol.mainarea li{ display:list-item !important; font-size:12px !important; margin:0 !important; line-height:18px !important; padding:0 0 0 0px !important; background-color:#f7f7ff !important; color:#4f81bd}ol.mainarea li pre{color:black; line-height:18px; padding:0 0 0 12px !important; margin:0em; background-color:#fff !important}.linewrap ol.mainarea li pre{white-space:pre-wrap; white-space:-moz-pre-wrapwhite-space:-pre-wrap; white-space:-o-pre-wrap; word-wrap:break-word}ol.mainarea li pre.alt{ background-color:#f7f7ff !important}function CopyCode(key){var codeElement=null;var trElements=document.all.tags("ol");var i;for(i=0;i
目前Entity Framework支持三种建模方式:Model First、Database First以及Code First。Code First是在今年刚发布的Feature Pack中才支持的。为了迎合领域驱动设计思想,我们采用Model First。
根据上面的分析,现建模如下:
注意:如何在Visual Studio中使用Entity Framework进行Model First建模不是本文讨论的重点,读者朋友请自己参阅相关文档。
此时,我们需要使用C#部分类的特性,将Reader和Book定义为聚合根,将Registration定义为实体。我开发的一个DDD框架(Apworks)中为聚合根和实体的接口作了定义,现在,只需要引用Apworks的程序集,然后使用部分类的特性,让Reader和Book实现IAggregateRoot接口,让Registration实现IEntity接口即可。从技术上看,这样就将Apworks框架整合到了领域模型中。代码如下:
隐藏行号 复制代码 ? Reader聚合根
public partial class Reader : IAggregateRoot
{
}
隐藏行号 复制代码 ? Book聚合根
public partial class Book : IAggregateRoot
{
}
隐藏行号 复制代码 ? Registration实体
public partial class Registration : IEntity
{
}
三、添加业务逻辑
根据DDD,实体是能够处理业务逻辑的,应该尽量将业务体现在实体上;如果某些业务牵涉到多个实体,无法将其归结到某个实体的话,就需要引入领域服务(Domain Service)。Tiny Library案例业务简单,目前不会涉及到领域服务,因此,在本案例中,业务逻辑都是在实体上处理的。
以读者(Reader)为例,它有借书和还书的行为,我们将这两种行为实现如下:
隐藏行号 复制代码 ? Reader中的业务逻辑
public partial class Reader : IAggregateRoot
{
public void Borrow(Book book)
{
if (book.Lent)
throw new InvalidOperationException("The book has been lent.");
Registration reg = new Registration();
reg.RegistrationStatus = RegistrationStatus.Normal;
reg.Book = book;
reg.Date = DateTime.Now;
reg.DueDate = reg.Date.AddDays(90);
reg.ReturnDate = DateTime.MaxValue;
book.Registrations.Add(reg);
book.Lent = true;
this.Registrations.Add(reg);
}
public void Return(Book book)
{
if (!book.Lent)
throw new InvalidOperationException("The book has not been lent.");
var q = from r in this.Registrations
where r.Book.Id.Equals(book.Id) &&
r.RegistrationStatus == RegistrationStatus.Normal
select r;
if (q.Count() > 0)
{
var reg = q.First();
if (reg.Expired)
{
// TODO: Reader should pay for the expiration.
}
reg.ReturnDate = DateTime.Now;
reg.RegistrationStatus = RegistrationStatus.Returned;
book.Lent = false;
}
else
throw new InvalidOperationException(string.Format("Reader {0} didn't borrow this book.",
this.Name));
}
}
.src_container{background-color:#e7e5dc; width:99%; overflow:hidden; margin:12px 0 12px 0 !important; padding:0px 3px 3px 0px}.src_container .titlebar{ background-color:#d4dfff; border:1px solid #4f81bd; border-bottom:0; padding:3px 24px; margin:0; width:auto; line-height:120%; overflow:hidden; text-align:left; font-size:12px}.src_container .toolbar{ display:inline; font-weight:normal; font-size:100%; float:right; cursor:hand; color:#00f; text-align:left; overflow:hidden}.toolbar span.button{ display:inline; font-weight:normal; font-size:100%; cursor:hand; color:#00f; text-align:left; overflow:hidden; cursor:pointer;}.src_container div.clientarea{ background-color:white; border:1px solid #4f81bd; margin:0; width:auto !important; width:100%; height:auto; overflow:auto; text-align:left; font-size:12px; font-family: "Courier New","Consolas","Fixedsys",courier,monospace,serif}.src_container ol.mainarea{ padding:0 0 0 52px; margin:0; background-color:#f7f7ff !important}.number_show{ padding-left:52px !important; list-style:decimal outside !important}.number_show li{ list-style:decimal outside !important; border-left:1px dotted #4f81bd}.number_hide{ padding-left:0px !important; list-style-type:none !important}.number_hide li{ list-style-type:none !important; border-left:0px}ol.mainarea li{ display:list-item !important; font-size:12px !important; margin:0 !important; line-height:18px !important; padding:0 0 0 0px !important; background-color:#f7f7ff !important; color:#4f81bd}ol.mainarea li pre{color:black; line-height:18px; padding:0 0 0 12px !important; margin:0em; background-color:#fff !important}.linewrap ol.mainarea li pre{white-space:pre-wrap; white-space:-moz-pre-wrapwhite-space:-pre-wrap; white-space:-o-pre-wrap; word-wrap:break-word}ol.mainarea li pre.alt{ background-color:#f7f7ff !important}function CopyCode(key){var codeElement=null;var trElements=document.all.tags("ol");var i;for(i=0;i
下一讲将详细介绍基于TinyLibrary领域模型与Entity Framework的仓储的实现方式。
相关文章推荐
- 领域驱动设计案例:Tiny Library:用户界面
- 领域驱动设计案例:Tiny Library:业务逻辑与系统结构
- 【领域驱动设计】CQRS体系结构模式实践案例发布:Tiny Library CQRS
- 领域驱动设计案例:Tiny Library:简介
- 一起谈.NET技术,领域驱动设计案例:Tiny Library:简介
- 领域驱动设计案例:Tiny Library:应用服务层
- 领域驱动设计案例:Tiny Library:仓储
- 领域驱动设计模型和Smark UI
- 领域驱动设计(3)模型驱动设计
- 领域模型驱动设计(Domain Driven Design)入门概述
- 领域驱动设计案例
- 领域驱动设计和贫血、失血、充血模型
- EntityFramework之领域驱动设计实践【后续篇】:基于EF 4.3.1 Code First的领域驱动设计实践案例
- [.NET领域驱动设计实战系列]专题九:DDD案例:网上书店AOP和站点地图的实现
- 领域驱动设计案例之业务实现1
- EntityFramework之领域驱动设计实践【后续篇】:基于EF 4.3.1 Code First的领域驱动设计实践案例
- 领域模型驱动设计(DDD)之模型提炼
- 使用领域驱动设计中的Bounded Context概念分解大领域模型
- 领域驱动设计之领域模型--转载
- 领域驱动设计学习-让领域模型发挥作用