末了两节 细说运用层 、系统总体架构 是本文的重点,着重解释领域驱动设计与SOA之间的关系,对DDD有一定根本的朋友可以超越前面的几节,直接查看第七、八节。
源代码下载 (数据库可以在.edmx文件根据模型天生)
目录
一、SOA与DDD的定义
二、DDD的分层构造
三、把业务关系转化为领域模型
四、细说Repository
五、领域层的做事
六、工厂模式Factory
七、细说运用层
八、系统总体架构
一、SOA与DDD的定义
SOA与DDD都是常用的系统架构,但两者之间所针对的核心是不同的。
SOA(面向做事架构)由Gartner 在1996年提出来,它是一种分布式的软件架构,它可以根据需求通过网络对疏松耦合的粗粒度运用组件进行支配、组合和利用。大略来说,SOA便是一种大型系统开拓的体系架构,在基于SOA架构的系统中,详细运用程序的功能是由一些松耦合并且具有统一接口的组件(也便是service)组合构建起来的,它是针对多核心多平台之间的数据交流。
DDD(领域驱动设计)由Eric Evans在2004提出,它的核心内容是“如何将业务领域观点映射到软件工程当中”。它推翻了“软件从数据层开拓设计”的旧习气,强调领域模型在软件中发挥的强大力量,看重如何把企业内部繁芜的业务流程转化为软件。
也容许以认为SOA针对的是大型系统的总体架构,看重如何把系统进行项目分离,隔离开发,末了实现系统合并。而DDD是针对单个项目的开拓管理过程,看重如何利用领域模型把业务需求转化为软件。两者之间并没有存在理论上的冲突,能把两者结合,各展所长,更能发挥各自的上风。
二、DDD的分层构造
1. 观点
从观点上来说,领域驱动设计架构紧张分为根本举动步伐层、领域层、运用层、表现层4个观点层。
根本构造层:是为各层供应各项通用技能能力而构建的,它可以为领域层供应像Hibernate、LINQ、ADO.NET等持久化机制,为运用层通报,为表现层供应插件等等。
领域层:它是系统的核心部分,代表业务的逻辑观点。它会根据业务的实际流程定制了业务信息以及业务规则,并按一定的关系制订领域模型。领域模型只管须要依赖根本构造层进行保存,但领域模型之间的逻辑关系是跟根本构造层相隔离的。纵然根本构造层从NHibernate技能转换成LINQ技能,也不会影响到领域层的构造。领域模型只会依赖实际的业务逻辑,它只会根据业务的转变而灵巧变动。
运用层:它的任务是折衷领域层与表现层之间的关系,也可以作为系统与外界沟通的桥梁,在这层里面不会包括任何的业务逻辑。在SOA面向做事架构,这一层起着重要的浸染,在第七节将详细解释。
表现层:它是常用的界面开拓,可以以页面(ASP.NET、JSP),窗口(WinForm、WPF、Swing)等形式表现,它的紧张职责是卖力与用户进行信息沟通(把稳:在一样平常的项目开拓中,Web做事会作为与外界通讯的接口放置在表现层中,但在SOA中,Web做事会大多置于运用层中,下面将会作进一步阐明)
2. 开拓实例
在此先举个常见的订单管理例子,不才面的章节里都会以这个实例为参考:
每个用户在Person表里面都会有一个对应的帐户,里面记录了用户的姓名、地址、电话、积分(Point)等基本信息。
在Order表里记录的是每次交易的过程,每次商品的送货费(Freightage)为10元,当商品价格(Price)超过98元可免费送货,当用户Person积分(Point)超过2000分可获7折优惠(Favorable),1000~2000分可获8折,1000分以下可有9折,末了总体价格为为(TotalPrice)。
在末了结单的时候Order表里会产生订单号码OrderNumber和下订日期Delivery,Person表的积分也会加上订单总价的点数。
末了OrderItem表包含了物品Goods、物品价格Price、购买数量Count等属性,它紧张记录每次订单的详细交易状况。
上面的业务逻辑跟淘宝、当当等等大型购物网基本相似。之以是用这样一个例子作为参考,是想表现一下DDD是如果利用领域模型去适应多变的业务逻辑关系。
三、把业务关系转化为领域模型
1. 观点
模型驱动设计设计(MODEL-DRIVEN-DESIGN)是DDD里面的核心,它代表的是各个工具之间的关系,把繁芜的逻辑关系转化为模型。
模型紧张分为实体(Entity)、值工具(Value Object)与做事(Service)三种。
实体:实体所包含的不单止是持续串的属性,更主要的是与事宜的联系,在一个生命周期中环境的变革与事宜发生,将引起实体内部产生变革。彷佛在实体Order里面,Person的积分(Point)和OrderItem的价格(Price)都会直接影响总体价格(TotalPrice)的大小,而总体价格也会影响到运费Freightage的多少等等。在Order实体的统统,都会受到Person、OrderItem等这些外部因数的影响,这样的工具被视为实体。在不同的时候,实体会有不同的状态,以是在开拓过程中我们须要为实体加上一个“标识符”来区分工具的身份,它是实体的生命周期里的唯一标志。
值工具:当所用到的工具只有属性而没有其他逻辑关系的时候,我们就可以把它视为是值工具。值工具没有状态,也不须要有 “标识符”。在多数情形下它可以作为一个属性存在于一个实体的内部。一样平常情形下值工具的属性是不可改变的,当须要变动属性时,可以把全体工具删除,然后重新加入一个新工具。
做事:当实体之间存在某些操作,它们并不单一地附属于某一个实体,而是跟多个实体都有关联的时候,就可以利用做事来封装这些操作。值得把稳的是做事并非单独指Web Service, 也并非单单存在于领域层,而是在各个层当中都会存在做事,每一层的做事都有着不同的职能。在根本构造层做事可能是用于构建身份验证、电子邮件、缺点处理等等操作;在领域层,做事更多时候是一种操作,它用于折衷多个实体之间的关系,处理各种的业务问题;在运用层(特殊是在分布式开拓系统内),做事多以Web Service、TCP/IP套接字、MSMQ等等办法实现,做事在此处会作为一个与外界通讯的接口;
备注 :这里面也存在一定的争义,Eric 认为实体所代表的只是多个工具之间的关系,而它们的动作将会由做事来表示出来,这被称为血虚型模型。但在开拓过程中,越来越多人会把动作加入到实体里面,这被称为充血型模型。实在不同的问题该当客不雅观剖析,分别对待,在这个例子里面将会以按照 Eric 的定义来开拓做事,在后面的开拓过程中大家也可以从中表示一下做事层所带来的好处。
2. 实例解释
先以ADO.NET Entity Framework实现模型,Person、Order分别属于两个实体,它们都将继续Root接口,在它们的生命周期内都会天生一个Guid作为标志。此处把OrderItem作为一个值工具置于Order实体内,这意味着OrderItem会通过Order来获取,外界不能超过Order直接获取OrderItem。当然这该当由详细的业务情形来确定,当外界须要单独调用OrderItem类的时候,就该当考虑把OrderItem独立成为一个实体类。
在这里可利用分部类为实体增加Guid属性,关于分部类于分部方法的详细先容可参考C#综合揭秘——分部类和分部方法
namespace Business.DomainModel{public interface Root {} public partial class Order:Root{ private Guid _guid; public Order(){_guid = System.Guid.NewGuid();}//为根工具设置唯一的Guid;public Guid GUID{get { return _guid; }}}public partial class Person:Root{public Person(){_guid = System.Guid.NewGuid();}//为根工具设置唯一的Guid;private Guid _guid;public Guid GUID{get { return _guid; }}}}
四、细说Repository
1.观点
Repository是把持久化工具转换成领域模型的一种办法,可用于获取、更新持久工具并管理它们的生命周期。它使运用程序与持久化技能实现解耦,程序无需受制于利用Oracle还是MySql数据库,也不会受到Hibernate、LINQ、ADO.NET等数据层的约束,使开拓职员可以把把稳力集中到领域模型当中。
Repository与传统三层模式的DAL层有点相似,但Repository针对的是每一个根工具来划分边界的。在这个例子当中,Person与Order都会有对应的PersonRepository、OrderRepository。而OrderItem只是Order的子属性,以是它的插入、更新、删除都会包含在OrderRepository当中。当多个工具之间建立起联系后,关系将是繁芜的,特殊是在LINQ里面,程序可以轻易通过Person的导航属性里获取OrderItem的值,末了很随意马虎使代码变得混乱。以是确立Repository的边界,可以在有效管理每个Repository的职能。
2.实例解释
把稳OrderItem的存取、删除都包含在OrderRepository里面。在获取、修正Order的时候,也会利用“显式加载” context.Order.Include("OrderItem") 的方法,使OrderItem实现同步更新。而通过PersonRepository.GetPerson(int )获取的Person工具,它内部的Order属性将是null值,这必须清晰按照领域模型的边界划分的。
当LINQ面世往后,数据的获取变得大略,特殊在一些小型的系统开拓时,很多人会不自觉地把这种领域模型的分界规则冲破。但随着系统的繁芜化,问题就会逐渐呈现。比如当Order工具的属性被更新,利用OrderRepository.Update(Order)更新数据库后,页面的Person工具未能同步实现更新,在Person与数据库交流数据的时候,Order又被变回旧值。
在混乱的数据层开拓中,这种情形非常常见,以是不才会坚持Repository的原则,把Repository的职能清晰按照领域模型划分。
namespace Business.IRepository{public interface IOrderRepository{Order GetOrder(int id);IList GetList();IList GetListByPerson(int personID);int AddOrder(Order order);int DeleteOrder(int id);int UpdateOrder(Order order);int AddOrderItem(OrderItem orderItem);int DeleteOrderItem(int id);} public interface IPersonRepository{int AddPerson(Person person);int AttachPerson(Person person);int UpdatePerson(Person person);Person GetPerson(int id);IList GetList();}}namespace Business.Repository{public class OrderRepository:IOrderRepository{//根据ID获取单个Orderpublic Order GetOrder(int id){BusinessContext _context = new BusinessContext();Order order = null;try{using (TransactionScope scope = new TransactionScope()){ //由于OrderItem是Order实体中的一个属性,必须通过OrderRepository同步获取var list = _context.Order.Include("OrderItem").Where(x => x.ID == id);if (list.Count() > 0)order = list.First();elseorder = new Order();scope.Complete();}}catch (Exception ex){//出错处理,并返回一个空工具Business.Common.ExceptionManager.DataException.DealWith(ex);order = new Order();}_context.Dispose();return order;}....................................}public class PersonRepository:IPersonRepository{public int AddPerson(Person person){return LinqHelp.Add(person);}public Person GetPerson(int id){return LinqHelp.Get(id);}..................................}}
在更新Order这种繁芜的领域模型时,如果要分辨单个OrderItem属性是新建值还是更新值,然后分别处理,那将是比较麻烦的,而且OrderItem只是一个值工具,ID编码等属性对它没有任何实在意义。以是在更新List<OrderItem>属性时都会先把它全部删除,然后重新加载,在OrderItem数量不多的时候,这是一种十分有效的方法。
namespace Business.Repository{public class OrderRepository:IOrderRepository{..................................//更新Order,因难堪以别哪些是原有的OrderItem,哪些OrderItem是新插入//利用大略的方法,会先把原有的OrderItem的删除,再重新插入public int UpdateOrder(Order order){int returnValue = -1;BusinessContext _context = new BusinessContext();try{using (TransactionScope scope = new TransactionScope()){var list = _context.Order.Include("OrderItem").Where(x => x.ID == order.ID);if (list.Count() > 0){//更新Order列Order _order = list.First();_order.Count = order.Count;_order.Delivery = order.Delivery;_order.Favorable = order.Favorable;_order.Freightage = order.Freightage;_order.OrderNumber = order.OrderNumber;_order.PersonID = order.PersonID;_order.Price = order.Price;_order.TotalPrice = order.TotalPrice; //删除原有的订单明细项OrderItemif (list.First().OrderItem.Count != 0)foreach (var item in list.First().OrderItem)DeleteOrderItem(item.ID);//加入新的订单明细项OrderItemif (order.OrderItem.Count != 0){foreach (var item in order.OrderItem){var _orderItem = new OrderItem();_orderItem.Count = item.Count;_orderItem.Goods = item.Goods;_orderItem.OrderID = item.OrderID;_orderItem.Price = item.Price;AddOrderItem(_orderItem);}}returnValue = _context.SaveChanges();}elsereturnValue = 0;scope.Complete();}}catch (Exception ex){Business.Common.ExceptionManager.DataException.DealWith(ex);returnValue=-1;}_context.Dispose();return returnValue;}//插入OrderItempublic int AddOrderItem(OrderItem orderItem){return LinqHelp.Add(orderItem);}//删除OrderItempublic int DeleteOrderItem(int id){EntityKey key = new EntityKey("BusinessContext.OrderItem", "ID", id);return LinqHelp.Delete(key);}}}
五、领域层的做事
1. 例子解释
在第二节已基本先容过做事的浸染了,领域层做事的浸染紧张是为理解决业务上的逻辑问题,更多的时候,做事是一个与业务干系的动作。比如在上述例子中:
在Order表里记录的是每次交易的过程,每次商品的送货费(Freightage)为10元,当商品价格(Price)超过98元可免费送货,当用户 Person积分(Point)超过2000分可获7折优惠(Favorable),1000~2000分可获8折,1000分以下可有9折,末了总体价 格为为(TotalPrice)。
这繁芜的业务逻辑,完备可以由一个领域做事类AccountManager来完成
namespace Business.Service.DomainService{public class AccountManager{private Person _person;private Order _order; public AccountManager(Person person, Order order){_person = person;_order = order;}///打算总体收费 public void Account(){//打算商品数量GoodsCount();//打算商品价格PriceAccount();//打算优惠等级FavorableAccount();double price1 = (_order.Price - _order.Favorable).Value;//打算运费FreightageAccount(price1);//打算总体价费_order.TotalPrice = price1 + _order.Freightage.Value;}//打算商品数量private void GoodsCount(){_order.Count=0;foreach (var OrderItem in _order.OrderItem)_order.Count += OrderItem.Count;}//商品总体价格private void PriceAccount(){_order.Price = 0;foreach (var OrderItem in _order.OrderItem)_order.Price += OrderItem.Price OrderItem.Count;}//优惠分为三等,积分小于1000有9折,小于2000分为8折,大于2000为7折private void FavorableAccount(){int point = (int)_person.Point.GetInt();if (point < 1000)_order.Favorable = _order.Price 0.1;if (point >= 1000 && point < 2000)_order.Favorable = _order.Price 0.2;if (point > 2000)_order.Favorable = _order.Price 0.3;}//如果价格在98元以上,可免运费。别的运费为10元private void FreightageAccount(double price){if (price >= 98)_order.Freightage = 0;else_order.Freightage = 10;}}}
你可能会说,在这个业务流程中,除了积分优惠Person.Point以外,其他的业务都只与Order的属性有关,按照充血型模型的方案,完备可以把这些业务放到Order的方法当中,而把积分优惠独立成为一个做事。但不才在很多的开拓过程中创造,为模型附上动作会带来持续串的问题,彷佛你不知道哪些操作该当在模型动作上实现,哪里该当在做事中实现......。对付这些无休止的辩论不会由于这里的一个小例子而停滞,但在这里我会坚持利用血虚型模型,利用做事来完成所有的动作。
再举一个例子:在末了结单的时候Order表里会产生订单号码OrderNumber和下订日期Delivery,Person表的积分也会加上订单总价的点数。对应这个操作,也可以单独开拓一个PaymentManager做事类进行管理。
namespace Business.Service.DomainService{public class PaymentManager{//下单结算public void Payment(Order order,Person person){//确定下单,建立订单号order.OrderNumber = Guid.NewGuid().ToString();order.Delivery = DateTime.Now;//增加积分if (person.Point.HasValue)person.Point += (int)order.TotalPrice.GetValueOrDefault();elseperson.Point = (int)order.TotalPrice.GetValueOrDefault();}}}
利用领域层的做事,使得每个Manager做事类的职能非常明确,业务管理起来也十分地方便,领域层可以随着业务的改变而灵巧变动。而且领域层具有 “高内聚,低耦合” 特性,它并不依赖其它任何一层,而只是把业务逻辑包含在里面。
六、工厂模式Factory
Factory是常用到软件开拓模式,在网上像大略工厂、工厂方法、抽象工厂等开拓模式的资料都到处可寻,可这并不是领域驱动设计的主题。在这一节里,我紧张想先容Factory的适用机遇。
并非天生所有工具的时候,都须要用到工厂模式。在天生大略工具的时候,可以直策应用布局函数来代替工厂,也可以添加工厂方法来天生工具。但如果在天生工具时,内部属性之间存在一系统繁芜的业务规则的时候,就可以把天生方法独立到一个Factory类里面。这时候客户端无需理会潜在逻辑关系,而直接通过这个Factory来天生相应的工具。
举个例子,在新建Order的时候,业务上规定运费是总体金额的1%,折扣规定是7.5折...... 。如果由客户端新建一个工具Order,然后为这些属性负值,那干系的业务逻辑就会暴露在外。这时候就可以利用Factory模式,把属性之间的关系封装到Factory之内,客户端通过Factory就能轻松地天生Order工具而无须要理会繁芜的内部关系。
至于较繁芜的Factory模式,在此不多作先容,各位可以在网上查找干系资料。
七、细说运用层
1. SOA系统中运用层的特点
在开拓SOA分布式系统的时候,运用层是一个重点,它紧张有两个浸染。
第一,运用层紧张浸染是折衷领域层事情,指挥领域工具办理业务问题,但运用层本身不会牵扯到业务状态。
第二,在SOA系统当中运用层是数据运输中央和信息发放的端口,担负着数据转换与数据收发的任务。
它有以下的特点:
1、粗粒度
分布式系统与普通网站和运用程序不同,由于它假定外界对系统内部是毫无理解的,用户只想输入干系数据,末了得到一系列打算结果。以是我们该当把打算结果封装在一个数据传输工具(DTO)内,实现粗粒度的通报,这是一样平常项目与SOA系统在做事层的一个最明显的差别。 想想如果一个页面须要同时显示一个顾客的个人资料、某张订单的详细资料,那将要同时获取Person、Order、OrderItem三张表的信息。在普通系统的开拓过程中,这并不会造成太大问题,但在利用远程做事的时候,如果用三个方法分别获取,那将会造成不少的性能损耗。特殊是在分布式开拓系统中,运用层与表现层之间是实现分离的,更多时候两者是由不同部门所开拓的模块,表现层不会理解运用层中的逻辑关系,Person,Order,OrderItem三样东西在表现层看来,也便是同一样东西,那便是返回值。以是在系统内,该当把多张表的信息封装在一个DTO工具内,通过运用层一个远程方法一次性返还。利用粗粒度的数据元素是分布式系统的一个特点。
2、 传输性
如果你熟习SOA系统,对DTO(Data Transfer Object 数据传输工具)这个词一定并不陌生。DTO属于一个数据传输的载体,内部并不存在任何业务逻辑,通过DTO可以把内部的领域工具与外界隔离。DTO所封装的是客户真个数据,以是它的设计更多地是针对客户真个需求,而不是业务逻辑。比如说本来Person与Order是一对多的关系,但当一个页面只要显示的是一个客户的单张订单信息,那我们就可以根据须要把DTO中的Person和Order设计为一对一的关系。如果你是利用MVC开拓一样平常的网站,更多时候会把返回工具直接转化为Model。如果你开拓是一个分布式系统,那更多时候会从系统性能与隐蔽业务逻辑出发着想。而且考虑到把内部工具转化为DTO,将是一件麻烦的事,建议该当考虑DTO的兼容性,使DTO可以作为多个方法的返还载体。(把稳:在SOA系统内,该当从性能出发优先考虑粗粒度元素的传输性问题)
3、 封装性
在SOA系统当中运用层做事的发布并不须要繁芜的模型,只需利用外不雅观模式(Facade)把一些功能封装在少数的几个做事类里面,利用Web Service、TCP/IP套接字、MSMQ等做事办法向外界发布。
说到这里,我真的十分感激Martin师长西席带给我的帮助,在开拓过程中,这些繁芜的问题带给我不少的困扰,Martin师长西席一纸富有履历的独特见地,真的带给不才很大的启示。
2. 运用层的折衷性
运用层做事会利用Repository,完成实体基本的插入、更新、获取等等操作,并调用领域层的做事管理的业务逻辑。把稳不雅观察,统统的业务逻辑都只会隐蔽于领域层,运用层做事只起着折衷浸染,本身不应该包含有任何业务逻辑。
可以看到OrderService便是通过调用AccountManager、PaymentManager等领域层做事来完成结账、付款等一系列繁芜业务逻辑的。
namespace Business.Service.ApplicationService{public class PersonService{private IPersonRepository personRepository = DataAccess.CreatePersonRepository(); public int AddPerson(Person person){return personRepository.AddPerson(person);}public int UpdatePerson(Person person){return personRepository.UpdatePerson(person);}public Person GetPerson(int personID){return personRepository.GetPerson(personID);}public IList GetList(){return personRepository.GetList();}}public class OrderService{private IOrderRepository orderRepository = DataAccess.CreateOrderRepository();public int AddOrder(Order order){//打算Order总体用度Account(order);//加入修正后的Orderreturn orderRepository.AddOrder(order);}//调用领域层做事AccountManager,打算Order总体用度private void Account(Order order){//获取对应Person工具IPersonRepository personRepository = DataAccess.CreatePersonRepository();Person person = personRepository.GetPerson(order.PersonID);//调用做事层的AccountManager工具,打算用度,修正OrderAccountManager accountManager = new AccountManager(person, order);accountManager.Account(); }//调用领域层做事PaymentManager,确认订单public Order Payment(int orderID){var order=orderRepository.GetOrder(orderID);if (order != null){PersonRepository personRepository = new PersonRepository();var person=personRepository.GetPerson(order.PersonID);PaymentManager paymentManager = new PaymentManager();paymentManager.Payment(order, person);orderRepository.UpdateOrder(order);personRepository.UpdatePerson(person);return order;}elsethrow new Exception("Can not find order!");}public int DeleteOrder(Order order){return orderRepository.DeleteOrder(order.ID);}public Order GetOrder(int orderID){return orderRepository.GetOrder(orderID);}public IList GetList(){return orderRepository.GetList();}public IList GetListByPerson(int personID){return orderRepository.GetListByPerson(personID);}public int UpdateOrder(Order order){Account(order);return orderRepository.UpdateOrder(order);}public int AddOrderItem(OrderItem orderItem){int index = orderRepository.AddOrderItem(orderItem);Order order = orderRepository.GetOrder(orderItem.OrderID);UpdateOrder(order);return index;}public int DeleteOrderItem(OrderItem orderItem){int index = orderRepository.DeleteOrderItem(orderItem.ID);Order order = orderRepository.GetOrder(orderItem.OrderID);UpdateOrder(order);return index;}..............................................}}
3. 数据转换过程
前面已经阐明了DTO的浸染,但实现领域工具与DTO之间的转换是一件繁芜的事宜,因此可以建立一个数据转换器实现此功能。
在平常的事情里,不太多会把“订单管理系统”做成SOA的模式,由于在分布式系统中,数据的格式与定义大多数由部门之间协定,个中包含明确的规则。但由于条件的局限,在这里还是想以订单管理为例子,希望可以带给你一定的帮助。例子如下:在购物车结账,页面会包含用户基本信息,当前订单信息,订单明细信息等多个部分。
要完成数据转换,首先可以根据页面建立DTO工具,在分布式系统中,常日会把DTO工具放在一个独立的命名空间里,在这个实例里面称之为Business.TransferObject。DTO工具更多时候是面向表现层的需求而建立,这里由于表现层页面所须要的只是单个用户,单张订单的数据,以是在OrderDTO工具里会包含了用户信息和订单资料,也存在订单详细列List<OrderItemDTO>。当然,DTO的设计可以随着需求而修正。
在SOA系统里,DTO是远程做事数据的载体,以是会把DTO附上可序列化特性,这此例子中会利用WCF的数据左券实现OrderDTO和OrderItemDTO。
如图,要实现数据转换,就该当建立数据转换器。在这里OperationAssembler便是一个数据转换器,它是数据转换的核心,它是领域工具与DTO之间实现转换的工具。要在多个工具之间实现数据转换实在是一件非常麻烦的事,以是我一贯提倡把稳DTO工具的兼容性,使单个DTO工具可以适用于多个外不雅观层,以减少数据转换所带来的麻烦。
namespace Business.Service.ApplicationService{public class OperationAssembler{//把领域工具转换成DTOpublic static OrderDTO GetOrderDTO(Order order,Person person){OrderDTO orderDTO = new OrderDTO();if (person != null){orderDTO.EMail = person.EMail.GetString();orderDTO.Address = person.Address.GetString();orderDTO.Name = person.Name.GetString();orderDTO.PersonID = person.ID;orderDTO.Point = person.Point.GetInt();orderDTO.Telephone = person.Telephone.GetString();}if (order != null){orderDTO.PersonID = order.PersonID;orderDTO.Count = order.Count.GetInt();orderDTO.Delivery = order.Delivery.GetDateTime();orderDTO.Favorable = order.Favorable.GetDouble();orderDTO.Freightage = order.Freightage.GetDouble();orderDTO.OrderID = order.ID;orderDTO.OrderNumber = order.OrderNumber.GetString();orderDTO.Price = order.Price.GetDouble();orderDTO.TotalPrice = order.TotalPrice.GetDouble();var orderItemList = order.OrderItem.ToList();if (orderItemList.Count != 0){var orderItemDTO = new List();foreach (var orderItem in orderItemList)orderItemDTO.Add(GetOrderItemDTO(orderItem));orderDTO.OrderItemList = orderItemDTO;}}return orderDTO;} public static OrderItemDTO GetOrderItemDTO(OrderItem orderItem){OrderItemDTO orderItemDTO = new OrderItemDTO();orderItemDTO.Count = orderItem.Count.GetInt();orderItemDTO.Goods = orderItem.Goods.GetString();orderItemDTO.OrderID = orderItem.OrderID;orderItemDTO.OrderItemID = orderItem.ID;orderItemDTO.Price = orderItem.Price.GetDouble();return orderItemDTO;}//把DTO转换成多个工具public static void SetOrder(OrderDTO orderDTO, out Person person, out Order order){person = new Person();person.EntityKey=new System.Data.EntityKey("BusinessContext.Person","ID",orderDTO.PersonID);person.Address = orderDTO.Address;person.EMail = orderDTO.EMail;person.ID = orderDTO.PersonID;person.Name = orderDTO.Name;person.Point = orderDTO.Point;person.Telephone = orderDTO.Telephone;order = new Order();order.EntityKey=new System.Data.EntityKey("BusinessContext.Order","ID",orderDTO.OrderID);order.Count = orderDTO.Count;if (orderDTO.Delivery.Year!=0001&&orderDTO.Delivery.Year!=9999)order.Delivery = orderDTO.Delivery;order.Favorable = orderDTO.Favorable;order.Freightage = orderDTO.Freightage;order.ID = orderDTO.OrderID;order.OrderNumber = orderDTO.OrderNumber;order.PersonID = orderDTO.PersonID;order.Price = orderDTO.Price;order.TotalPrice = orderDTO.TotalPrice;var orderItemDTOList = orderDTO.OrderItemList;if (orderItemDTOList.Count() != 0)foreach (var orderItemDTO in orderItemDTOList)order.OrderItem.Add(GetOrderItem(orderItemDTO));}public static OrderItem GetOrderItem(OrderItemDTO orderItemDTO){OrderItem orderItem = new OrderItem();orderItem.EntityKey = new System.Data.EntityKey("BusinessContext.OrderItem", "ID", orderItemDTO.OrderItemID);orderItem.Count = orderItemDTO.Count;orderItem.Goods = orderItemDTO.Goods;orderItem.ID = orderItemDTO.OrderItemID;orderItem.OrderID = orderItemDTO.OrderID;orderItem.Price = orderItemDTO.Price;return orderItem;}}}//数据传输工具 DTOnamespace Business.TransferObject{[DataContract]public class OrderItemDTO{private int _orderItemID;private int _orderID;private string _goods;private double _price;private int _count;[DataMember]public int OrderItemID{get { return _orderItemID; }set { _orderItemID = value; }}........................}[DataContract]public class OrderDTO{private int _personID;private string _name;private string _address;private string _telephone;private int _point;private string _email;private int _orderID;private string _orderNumber;private int _count;private double _freightage;private double _favorable;private DateTime _delivery;private double _price;private double _totalPrice;private IList _orderItemDTOList;[DataMember]public int PersonID{get{return this._personID;}set{this._personID=value;}}....................}}
通过数据转换器,可以顺利实现领域模型与DTO之间的转换,折衷运用层做事的运行。
4. 运用层的发布
在开拓SOA系统的时候,运用层的做事须要利用远程方法对外开放,在吸收到要求的时候,它可以调用领域层做事获取运算结果,然后通过数据转换器OperationAssembler把运算结果转换成DTO,末了返还到表现层。在起初,我曾考试测验对应每个运用层的工具建立一个远程接口,但经由多次重构往后,我以为行程工具便是一个大略的对外接口,工具之间不存在什么逻辑关系。以是更大略的方法是利用外不雅观模式,建立少数的几个远程做事类,把所有的运用层工具的方法都包含在内。
可以留神代码,OperationService包括了对Person模型和Order模型的所有操作。而且每个操作都只是大略地调用运用层做事 (ApplicationService) 得到打算结果,然后利用数据转换器 (OperationAssembler)转换数据,当中并不存在任何的业务逻辑。
namespace Business.Service.ApplicationService{[ServiceContract]public interface IOperationService{[OperationContract]int AddOrder(ref OrderDTO orderDTO); [OperationContract]int DeleteOrder(OrderDTO orderDTO);[OperationContract]int UpdateOrder(ref OrderDTO orderDTO);[OperationContract]IList GetOrderByPerson(int personID);[OperationContract]OrderDTO GetOrder(int orderID);[OperationContract]int AddPerson(ref OrderDTO orderDTO);[OperationContract]int UpdatePerson(ref OrderDTO orderDTO);[OperationContract]OrderDTO GetPerson(int personID);[OperationContract]IList GetPersonList();[OperationContract]OrderDTO Payment(int orderID);}public class OperationService:IOperationService{[OperationBehavior(TransactionScopeRequired = true, TransactionAutoComplete = true)]public int AddOrder(ref OrderDTO orderDTO){OrderService orderService = new OrderService();Order order = GetOrder(orderDTO);int n = orderService.AddOrder(order);orderDTO = OperationAssembler.GetOrderDTO(order, null);return n;}[OperationBehavior(TransactionScopeRequired = true, TransactionAutoComplete = true)]public int DeleteOrder(OrderDTO orderDTO){OrderService orderService = new OrderService();return orderService.DeleteOrder(GetOrder(orderDTO));}[OperationBehavior(TransactionScopeRequired = true, TransactionAutoComplete = true)]public int UpdateOrder(ref OrderDTO orderDTO){OrderService orderService = new OrderService();Order order = GetOrder(orderDTO);int n = orderService.UpdateOrder(order);orderDTO = OperationAssembler.GetOrderDTO(order, null);return n;}............................}}
八、系统总体架构
1. 表示领域驱动设计的架构
到此总结一下领域驱动设计DDD的总体构造,Repository层利用ORM映射或SQL命令等办法把持久化数据转化为领域工具,然后根据业务逻辑设计对应领域层做事Domain Service 。接着运用层进行操作上的折衷,利用Repository、领域模型、领域层做事Domain Service 完成业务须要,再通过数据转换器把领域工具Domain Object转化为数据传输工具DTO。末了,利用远程通讯技能把运用层的做事(Application Service)对外开放。
把稳留神的是SOA系统中,UI表现层与Application Service运用层做事是实现分离的,表现层可以同时调用多方的远程做事来完成事情。
2. 表示面向做事开拓的架构
面向做事开拓SOA的架构紧张表示在表现层与运用层之间通过远程通讯实现分离,表现层可以引用多方的运用做事作为根本。由此系统实现业务上的分离,不同的功能模块可以独立开拓,末了通过做事在表现层共同表示。长期的发展,使不少的企业针对单个功能模块开拓出一套独立的系统,再通过强大的虚拟化技能为第三方供应做事,这便是云打算的前身。
就像一个通讯购物的平台,实在便是综合了内部业务管理、银行转帐做事、呼叫中央、第三方接口等多方做事的综合性平台。如果你有过这方面的履历,就会知道实在银行转帐、呼叫中央不过便是银行、电信、移动等公司供应的几个大略的接口。开拓职员根本无需理会实在内部的构造,只要通过几个大略的远程方法就能调用。这正是运用层做事 Application Service 的最好表示。
3. 结束语
写这篇文章目的只是想与各位分享一下我在开拓过程中的一些体会,欢迎各位点评,指出个中的不敷。
实在架构是去世物,人才是有脑筋的生物。每一个架构一定会有其优点,也会有不敷之处,我们该当从开拓之中一齐起来体验,而不是盲目地跟从,希望不才的鄙见能够给大家带来帮助。可别忘了支持一下,挺一挺。