结合领域驱动设计的SOA分布式软件架构,你还在为这些烦恼?( 五 )


  • 传输性
如果你熟悉SOA系统 , 对DTO(Data Transfer Object 数据传输对象)这个词一定并不陌生 。 DTO属于一个数据传输的载体 , 内部并不存在任何业务逻辑 , 通过DTO可以把内部的领域对象与外界隔离 。 DTO所封装的是客户端的数据 , 所以它的设计更多地是针对客户端的需求 , 而不是业务逻辑 。 比如说本来Person与Order是一对多的关系 , 但当一个页面只要显示的是一个客户的单张订单信息 , 那我们就可以根据需要把DTO中的Person和Order设计为一对一的关系 。 如果你是使用MVC开发一般的网站 , 更多时候会把返回对象直接转化为Model 。 如果你开发是一个分布式系统 , 那更多时候会从系统性能与隐藏业务逻辑出发着想 。 而且考虑到把内部对象转化为DTO , 将是一件麻烦的事 , 建议应该考虑DTO的兼容性 , 使DTO可以作为多个方法的返还载体 。 (注意:在SOA系统内 , 应该从性能出发优先考虑粗粒度元素的传输性问题)
  • 封装性
在SOA系统当中应用层服务的发布并不需要复杂的模型 , 只需使用外观模式(Facade)把一些功能封装在少数的几个服务类里面 , 使用Web Service、TCP/IP套接字、MSMQ等服务方式向外界发布 。
说到这里 , 我真的十分感激Martin先生带给我的帮助 , 在开发过程中 , 这些复杂的问题带给我不少的困扰 , Martin先生一纸富有经验的独特见解 , 真的带给在下很大的启发 。
2. 应用层的协调性
应用层服务会利用Repository , 完成实体基本的插入、更新、获取等等操作 , 并调用领域层的服务管理的业务逻辑 。 注意观察 , 一切的业务逻辑都只会隐藏于领域层 , 应用层服务只起着协调作用 , 本身不应该包含有任何业务逻辑 。
可以看到OrderService就是通过调用AccountManager、PaymentManager等领域层服务来完成结账、付款等一系列复杂业务逻辑的 。
3. 数据转换过程
前面已经解释了DTO的作用 , 但实现领域对象与DTO之间的转换是一件复杂的事件 , 因此可以建立一个数据转换器实现此功能 。
在平常的工作里 , 不太多会把“订单管理系统”做成SOA的模式 , 因为在分布式系统中 , 数据的格式与定义大多数由部门之间协定 , 其中包含明确的规则 。 但由于条件的局限 , 在这里还是想以订单管理为例子 , 希望可以带给你一定的帮助 。 例子如下:在购物车结账 , 页面会包含用户基本信息 , 当前订单信息 , 订单明细信息等多个部分 。
结合领域驱动设计的SOA分布式软件架构,你还在为这些烦恼?文章插图
要完成数据转换 , 首先可以根据页面建立DTO对象 , 在分布式系统中 , 通常会把DTO对象放在一个独立的命名空间里 , 在这个实例里面称之为Business.TransferObject 。 DTO对象更多时候是面向表现层的需求而建立 , 这里由于表现层页面所需要的只是单个用户 , 单张订单的数据 , 所以在OrderDTO对象里会包含了用户信息和订单资料 , 也存在订单详细列List 。 当然 , DTO的设计可以随着需求而修改 。
在SOA系统里 , DTO是远程服务数据的载体 , 所以会把DTO附上可序列化特性 , 这此例子中会使用WCF的数据契约实现OrderDTO和OrderItemDTO 。
结合领域驱动设计的SOA分布式软件架构,你还在为这些烦恼?文章插图
如图 , 要实现数据转换 , 就应该建立数据转换器 。 在这里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