浅析架构设计的新思路 DDD设计模式

开发 架构
本文将和大家一起探讨DDD的设计模式,这也可以说是架构设计的一种新思路,希望对大家有所启发。

前言:之前的文章,很多朋友发来了反馈,从反馈中也看出了一些问题,一个最明显的问题就是:当我提到DAL的实现的时候,一些朋友就问:DAL中采用了Repository模式吗? 初一看起来,可能认为这个问题没有什么,其实仔细的想想就会发现,确实在问题的背后隐藏的了另外的一个问题.

本篇的议题如下:

1.  问题的阐述

2. 设计方法

3.  总结

1.  问题的阐述

在项目中,我们一般都是分层,大家最熟悉的就是UI,BLL,DAL层,或者在加上一个Services服务层.一般的项目就这样设计了.由于越来越多的公司,社区倡导领域驱动设计(DDD),于是,又有了项目的分层的方式,DDD设计中的一些概念也引入了: Presentation, Service, Domain, Repository. 而且一般来说,有下面的对应关系:

Presentation

UI

Domain

BLL

Repository

DAL

但是在开发的时候,一会儿在项目中建立一个Domain的类库,一会儿又在项目建立DAL,***的情况就是:UI, Domain, DAL等等. 其实这倒是没有什么,说到底只是一个名称的问题,但是在只后面隐藏的问题就是:对DDD的不了解,很多的时候只是注重了”形”,而没有领会到”神”.

在项目中不是建立了名称为Presentation, Domain, Repository的类库,这个项目就是DDD开发了,不是这样的.本来在分层的时候采用UI,BLL,DAL,自己是很熟悉的,但是这样一搅和, ***反而把概念搞复杂了.

而且,在项目中,是采用原来朴实的那种三层,还是采用DDD开发,是要经过思考的,不是那DDD的方法来套.也就是说,不要为了DDD而DDD.就像当初我们学习设计模式一样,没有必要在写代码的过程中为了设计模式而设计模式.

2.  设计方法

到底是采用DDD还是那种朴实的三层,主要取决与业务层的设计和系统的复杂度.

如果系统确实很复杂,业务逻辑相当的复杂,那么建议采用DDD,因为DDD的引入就是用解决复杂性的.因为采用DDD的方法来设计业务逻辑层,那么业务逻辑层就只是关注业务逻辑的处理,至于怎么存储和获取数据,丝毫不关心,所以基于这个原因,在DDD中就引入了Repository的概念,Repository就是来辅助业务逻辑层处理数据的.

虽然我一直在提”朴实的三层”,其实DDD和它之间没有什么很明显的划分了,这里我之所以特意的把他们划分出来,主要就是因为我们在项目开发中一般是三层(或者N层),这里提出主要是为便于后面讲述一些问题.

下面就开始讲述一些业务逻辑层设计方法,相信大家看完之后,很多的疑惑就迎刃而解了.

业务层的设计方法有三种:Transaction Script, Active Record和Domain Model.

看过Flower的<<企业架构模式>>一书的朋友应该对上面的三个词语很熟悉,在书中,这些概念讲的确实很精炼,可能因为精炼,所以理解起来就不是很容易.

在本篇文章中,就涉及到了这些知识,只有把这些点讲清楚了,之前的问题就能解决.

如果熟悉这些概念的朋友,也不妨看看,大家可以交流!

首先来看看Transaction Script(之所以没有翻译为中文,因为翻译后的中文意思很容易让人产生误导)

其实Transaction Script就是过程化的设计方式,最直观表现就是一个个的方法,每个方法做一个业务的流程。我们来看下面一个例子。例子的背景就是在电子商务网站中订单的处理流程。

  1. public class OrderManager  
  2.     {  
  3.         public void PlaceOrder(OrderDTO order)  
  4.         {  
  5.             // Validate order based on business rules  
  6.             // Check for stock availablity on items ordered  
  7.             // Add the order to the database  
  8.             // Set the order id on the OrderDTO object  
  9.         }  
  10.         public bool CancelOrder(Guid orderId)  
  11.         {  
  12.             // Retrieve order from database  
  13.             // Determine if the order can be canceled  
  14.             // if order can be canceled, set as canceled  
  15.             // return true/false if order was canceled  
  16.         }  
  17.         public bool AddItemToOrder(Guid orderId, OrderItemDTO ItemToAdd)  
  18.         {  
  19.             // Retrieve order from database  
  20.             // Determine if the item can be added to the order  
  21.             // Add a new item row in the database  
  22.             // return true/false if item was added to the order  
  23.         }  
  24.         public bool ProcessOrder(Guid orderId)  
  25.         {  
  26.             // Check to ensure this order can be processed.  
  27.             // Validate order based on business rules  
  28.             // Update the stock levels of products ordered  
  29.             // return true/false if order was processed  
  30.         }  
  31.     } 

在上面的代码中,所有和订单处理有关的逻辑都写在OrderManager类中。类中的每一个方法就对应业务逻辑中的一个流程或者说对应一个use case,例如:CancelOrder就是取消订单。

通过Transaction Script的方式来组织业务逻辑,一个很好的好处就是直观,很容易理解代码在做什么。如果有新的流程来了,再加一个方法就行了。

同时,这种组织方式的弊端就在于,当系统中的业务变得多而且复杂的时候,那么这样的方法就开始变多,***的结果就是一个类中有成百上千个方法。而且这些方法中,除了一些基本的验证可以提取为方法重用,其他的流程控制代码在很多的地方要重写,特别是当有两个流程差不多的时候,代码不可避免的重新写。于是,这样的类开始变得庞大而难以管理。

Active Record

这种组织方式已经是我们最熟悉的了。

在很多的项目中,我们的业务实体类基本和数据库中表是一一对应的,例如一个Order业务类就是代表了数据库中的Order表。而且在平时项目中,”朴实的三层(N层)”,一般都是基于这种方式在组织逻辑。

这种方式的***的区别就是每个业务类自己负责自己的数据存取,也就是说在业务类中包含了业务逻辑的处理和数据的存取。

例如:

  1. public class Order  
  2. {  
  3.     private Guid _id;  
  4.     private DateTime _creationDate;  
  5.     private int _shippingMethod;  
  6.     private int _status;  
  7.     private List<OrderItems> _orderItems;  
  8.     public Guid Id  
  9.     {  
  10.         get { return _id; }  
  11.         set { _id = value; }  
  12.     }  
  13.  
  14.     public List<OrderItems> OrderItems  
  15.     {  
  16.         get { return _orderItems; }  
  17.         set { _orderItems = value; }  
  18.     }  
  19.     // Business Logic  
  20.     public void Place()  
  21.     {  
  22.         // Validate order based on business rules to ensure it is in  
  23.         // a good state to add to the database  
  24.         // Check for stock availablity on items ordered  
  25.         this.Add();  
  26.     }  
  27.     public void Cancel()  
  28.     {  
  29.         // Check to ensure this order can be canceled.  
  30.         this.Status = Status.Cancelled();  
  31.         this.Save();  
  32.     }  
  33.  
  34.     public void ProcessOrder()  
  35.     {  
  36.         // Check to ensure this order can be processed.  
  37.         // Validate order based on business rules  
  38.         // Udpate the stock levels of products ordered  
  39.     }  
  40.  
  41.     // Data Access Methods  
  42.     public void Save()  
  43.     {  
  44.         // Code to persist changes to the database  
  45.     }  
  46.  
  47.     public void Add()  
  48.     {  
  49.         // Code to Add this object to the database  
  50.     }  
  51.  
  52.     public void Delete()  
  53.     {  
  54.         // Code to remove this object from the database  
  55.     }  
  56.  
  57.     public static List<Order> FindAll()  
  58.     {  
  59.         // Code to retrive all Orders from the database  
  60.     }  
  61.     public static Order FindBy(Guid id)  
  62.     {  
  63.         // Code to retrive a specific Order from the database  
  64.     }  

上面的代码中,Order类包含了业务逻辑处理的代码,如Cancel, Process。通过这些方法也调用了数据访问代码来保存数据。

如果在开发的项目中,业务类和数据表是一一对应的关系,例如在开发博客或者论坛,Active Record方式就很合适。

相信很多的项目都是基于这个方式在开发和组织逻辑层,这个方式***的弊端就是:数据库表只要改动,那么业务逻辑层动,而且这种变动会一直波及到了UI那端。

Domain Model

通过用这种方式来组织业务层的时候,业务层就只是关注把现实中的概念转换为相应的业务逻辑模型,不关注其他的方面。例如,在电子商务网站开发中,一些概念就被建模表示为一个个的业务模型(也就是业务类),Order, Shopping Cart, Customer等。而且和Active Record***的区别就是:Domain Model中的业务类不是和表一一对应的,下图就是一个很好的例子:

 

而且最主要的特点就是:每个业务类包含了很多的业务验证,状态跟踪等。职责很单一,便于维护和理解。

示例代码如下:

  1. public class Order  
  2. {  
  3.     private Guid _id;  
  4.  
  5.     public Guid Id  
  6.     {  
  7.         get { return _id; }  
  8.         set { _id = value; }  
  9.     }  
  10.  
  11.     public float ShippingCost()  
  12.     {  
  13.         return ShippingMethod.ShippingCostTo(this.DispatchAddress, this.ItemsTotalWeight());  
  14.     }  
  15.  
  16.     public float Total()  
  17.     {  
  18.         return DiscountOffer.TotalPriceWithDiscountOfferAppliedTo(  
  19.         this.Items, ShippingCost());  
  20.     }  
  21.  
  22.     public void Process()  
  23.     {  
  24.         if (this.CanProcess())  
  25.         {  
  26.         // Charge the card  
  27.         Customer.Card.Charge(this.Total());  
  28.         // Set the status of the order  
  29.         this.Status = Status.Shipped;  
  30.         // Adjust the stock levels  
  31.         foreach (OrderItem item in Items)  
  32.         {  
  33.             item.Product.DecreaseStockBy(item.QtyOrdered);           
  34.         }  
  35.         else 
  36.         {  
  37.             throw new InvalidOrderStateForOperationException(  
  38.             String.Format(  
  39.             "Order {0} cannot be processed in its current state {1}",  
  40.             this.Id, this.Status.ToString());  
  41.         }  
  42.     }  
  43.  
  44.     public bool CanProcess()  
  45.     {  
  46.         if (!this.Status == Status.Shipped && !this.Status = Status.Cancelled)  
  47.         {  
  48.             return (this.HasEnoughStockFor(me.Items) &&  
  49.  
  50.             GetBrokenRules.Count() == 0);  
  51.         }  
  52.         else 
  53.         {  
  54.             return false;  
  55.         }  
  56.     }  
  57.  
  58.     public List<BrokenBusinessRule> GetBrokenRules()  
  59.     {  
  60.         List<BrokenBusinessRule> brokenRules = new List<BrokenBusinessRule>();  
  61.         if (Customer == null)  
  62.             brokenRules.Add(new BrokenBusinessRule()  
  63.             {  
  64.                 Property = "Customer",  
  65.                 Rule = "An Order must have a Customer" 
  66.             });  
  67.         else if (Customer.GetBrokenRules().Count > 0)  
  68.         {  
  69.             AddToBrokenRulesList(brokenRules, Customer.GetBrokenRules());  
  70.         }  
  71.         if (DispatchAddress == null)  
  72.             brokenRules.Add(new BrokenBusinessRule()  
  73.             {  
  74.                 Property = "DispatchAddress",  
  75.                 Rule = "An Order must have a Dispatch Address" 
  76.             });  
  77.         else if (DispatchAddress.GetBrokenRules().Count > 0)  
  78.         {  
  79.             AddToBrokenRulesList(brokenRules,  
  80.             DispatchAddress.GetBrokenRules());  
  81.         }  
  82.         // ......  
  83.         return brokenRules;  
  84.     }  

Order业务类的一部分代码,但是从代码中可以看出,这个类中包含了很丰富的业务逻辑。例如,在Process方法中,处理了下面的流程:

1.  调用CanProcess 方法来进行下面的验证:

a.       Order的是否处于合适的可以被处理的状态

b.       在Order中订购的物品是否有足够的库存

2. customer用户给这个order付款。至于怎么付款,这个逻辑就包含在了card类中。

3. 然后,对产品的库存进行更新。

可以看出,采用Domain Model方式很适合来来组织复杂的业务逻辑,而且代码也很容易阅读和理解(如果在加上重构)。

3.  总结

通过上面的一些分析和解释,不知道大家是否现在已经清楚:之前提出的问题如何解决。

一个建议就是:不要太形式化,根据项目的实际情况来。这句话可以使相当于废话,但是很多的情况确实是这样的,DDD不是***的,Transaction Script和Active Record也有它们的优势,合适就好。 

原文标题:架构设计解惑

链接:http://www.cnblogs.com/yanyangtian/archive/2010/07/13/1776355.html

【编辑推荐】

  1. UML用例建模的慨念和应用
  2. Eclipse UML插件及其安装步骤简明介绍
  3. 解析五大UML图形的建立步骤
  4. 专家指导 如何使Eclipse和UML工具EA进行连接
  5. 把Eclipse UML插件集成至Eclipse如何实现 

 

责任编辑:彭凡 来源: 博客园
相关推荐

2021-09-08 09:22:23

领域驱动设计

2017-11-06 08:28:44

DDD架构设计IT

2022-04-02 08:55:15

架构RocketMQSDK

2016-02-18 10:09:23

12306核心思路架构

2022-04-20 10:15:56

SaaS模块化客户

2024-05-31 12:59:03

2023-01-09 09:00:00

树服务架构驱动决策

2009-06-29 17:39:31

JSP设计模式

2020-05-14 14:48:15

架构模式单库

2024-09-18 09:04:33

架构模式查询

2016-03-25 09:57:09

统一监控报警平台运维

2017-02-16 09:42:37

同有

2009-07-07 16:39:40

JDK Observe

2020-10-19 13:05:32

架构模式

2013-05-27 10:58:28

Tumblr架构设计雅虎收购

2020-04-22 14:25:48

云开发高可用架构

2023-05-12 08:06:46

Kubernetes多云架构

2019-08-02 08:50:47

API架构微服务

2011-12-20 21:12:46

用户体验

2019-11-07 11:49:14

架构运维技术
点赞
收藏

51CTO技术栈公众号