何为业务逻辑?

2.1、业务逻辑到底是什么?

       广义上的业务逻辑——社会所赋予行业的职能,行业应具备的特性,是这个行业存在的核心和灵魂。

      狭义上的业务逻辑——等同于分层架构中“业务逻辑层”的职责,是软件中处理与业务相关任务的部分,一般狭义上的业务逻辑不包含数据持久化,而只关注领域内的相关业务。

      对于以上两种定义,希望朋友们不要割裂开来看,而要辩证统一的去看,这样,才能构建一个完整而辩证统一的“业务逻辑”概念。在下文中,将不再明确区分狭义和广义,“业务逻辑”一词将代表两者的辩证统一体。

  业务逻辑:领域实体在遵守业务规则(及完整性约束)的前提下有序的执行原子业务最终完成一个业务的活动

  2.2、业务逻辑的组成结构

      业务逻辑作为一个高层次概念,其内在结构也是非常丰富的,下面我们深入其里,去探寻一下业务逻辑都是由哪些更底层的部分构成的。

      2.2.1、领域实体(Domain Entity)

      通俗的说,领域实体就是这个领域内有哪些东西。例如,银行业领域内有账户、支票、前台营业员等实体;B2C电子商务领域有商品、订单、交易等实体;魔兽世界游戏的领域内有角色、种族、道具、魔法等实体;高等代数领域有矩阵、行列式等实体。

      领域实体是某个领域内各种对象的抽象,可以用名词表示(可以是具体名词或抽象名词,甚至动名词,只要其具有名词性),构成了整个业务逻辑的骨骼和静态模型。一般每个领域实体有自己的一些属性和行为。顺便说一句,领域实体的存在时OOA&D的基础。

      在具体的软件系统中,领域实体往往会根据架构的不同有不同的映射存在形式。

      其中一种叫做Business Object(BO),即业务对象,某些文献称其为“充血实体类”,这种对象完整抽象了领域内的某个实体,封装了此实体相关属性和行为。在面向对象的设计和架构中,这种实体类很常见。

      另一种叫做Data Transfer Object(DTO),某些文献称其为“贫血实体类”,其特点是仅有属性,不存在行为。这种实体类主要负责整体性传递数据。另外,与BO不同的是,DTO可以不抽象领域实体的全部属性,而只根据需要抽象一部分。例如,某个“User”实体存在很多属性,但如果某个方法仅需要其联系方式,可以设计一个DTO,仅有id,email,address,phone等就够了。在面向过程的设计和架构中,这种实体设计比较常见。

      2.2.2、业务规则(Business Rules)

      业务规则就是某个领域内运作的规则,构成了整个业务逻辑的灵魂和动态模型。业务规则作用于领域实体,领域实体遵从业务规则进行运作。

      如:在银行领域内,“转账时从A账户扣除相应款项,在B账户添加相应款项,并从A账户扣除相应手续费,并通过某些途径通知A和B账户的户主”就是一条规则。需要注意的是,业务规则比较抽象,并不是需求,需求需要具体且无二义性,而业务规则只是抽象的一种描述,例如,通知户主的途径是什么?电子邮件?电话?短信?并没有具体描述,但在规则中有“通知”这一项,因此不能将业务规则等同于需求。

      2.2.3、完整性约束(Validation)

      领域实体和业务规则构建了业务逻辑的主体,但在这主体之上,还存在着一个限制,这就是完整性约束。

      完整性约束是对业务领域中的数据、规则的强制性规定与约束。这种约束是系统正常运转的保证。

      如“账户密码不能为空”,“身份证号必须符合具体格式规定”,“转账流程必须具有原子性,A账户扣钱、B账户存钱、A账户扣除手续费、通知户主四项操作必须要么都做,要么都不做”,都是完整性约束。

      2.2.4、业务流程及工作流(Business Processes and Workflows)

      有了上述三项,业务逻辑还不能正常工作,因为还没有“启动器”和“过程托管器”。设想我们有了各种实体类,它们有各自的属性和行为,也有定义好的业务规则和完整性约束。现在实体类仅仅具有实现业务规则的能力,但它们如何启动并交互协调完成业务规则呢?因此我们需要有东西去触发和协调实体。

      业务流程或工作流是启动及托管协调领域实体完成既定规则的过程。例如,“在线订购”是一个业务流程,它包括“用户登录-选择商品-结算-下订单-付款-确认收货”这一系列流程。各个实体如会员、订单、商品等已经包含了完成在线订购必要的行为,但仍需一个流程,才能真正完成业务。

      具体到程序中,业务流程也许通过一个方法来实现,这个方法负责启动并协调各个实体类,完成一个流程。

  2.3、业务逻辑层职责及相关争议

      2.3.1、数据的格式化

      关于数据的格式化应该放在业务层进行还是表示层进行一直存在争议。我个人的意见是这样的:

      业务层送给表示层的数据应该具备以下要求。1)返回的数据应该完成了所有必要的业务处理和业务计算。例如,若返回订单信息让表示层展示,会有个必要的数据——订单总额。这个数据需要首先用各个订单项的单价乘以数量,然后加和。那么,这个数据应该在业务层完成计算直接返回,总之不应让表示层进行任何业务处理和计算操作。2)一次性返回所有需要的数据,避免表示层再一个Action里调用多次业务。打个比方,例如订单中有个“客户姓名”,这个数据不保存在订单表中,而是通过外键关联的,那么,业务层应该将“客户姓名”一并取出返回给表示层。总之,避免表示层在一个Action里多次调用业务层。3)不携带任何格式信息,仅仅是结构良好的纯净数据,如DTO形式。因为,数据如何展示,是表示层的职责,如何在业务层中返回了过多格式信息,就会造成表示层的修改困难。例如,我曾听说过所里承接的一个实际项目,开始是使用B/S,当时他们的业务层返回的数据全都附带了html代码。后来,客户嫌B/S响应不够迅速(可能是客户公司的网络条件不好),要求改成C/S,当时全傻眼了,貌似几乎修改了整个业务层。那个项目相当庞大,7个子系统,投入200人开发了1年多,想想修改的难度吧。

  2.3.2、数据合法性及完整性验证

      一般做系统,都避免不了数据验证。上文曾经提到,完整性约束是业务逻辑的一部分。如此看来,数据验证一般应该放在业务层。但是,实际情况并不尽然。个人认为数据验证的方式,目前没有统一标准,可以根据需要放在表示层或业务层。但是,我个人不提倡在“表示层的服务端”放置过多完整性验证。因为,表示层的职责应该仅仅是接收数据并传递给业务层,不应对数据是否合法负责。过多的数据验证,不但令表示层代码臃肿,而且使得表示层职责变得不明确。

      可以在“表示层的服务端”放置一些简单的验证,如空值验证,两次输入密码是否一致等,但业务关系紧密的验证,最好放在业务层。甚至有些验证只能在业务层验证,如“当前用户名不能与已有用户名重复”,这种验证需要访问持久化数据,需要由业务层完成。

      这里之所以强调“表示层的服务端”,是因为一般在B/S系统中,都会在JavaScript里加入一些基本的数据验证,如空值检查,格式正则匹配等。这主要是为了减轻服务器负担,将大多数显然包含不合法数据的请求拒绝掉,而不发给服务端验证。当然,因为可能会出现JS被屏蔽或黑客恶意攻击行为,所以,所有验证不论JS中是否验证过,服务端(可能是表示层的服务端部分或业务层)一定要再进行验证。

      2.3.3、CRUD

      CRUD,即常说的增删改查操作。关于CRUD是否是业务层的职责,一直也是争议不断。因为目前并没有权威的定义,所以这里我斗胆说一下我对这个问题的看法。还请大家批判性阅读。

      一说到“增删改查”,大家一定会觉得这理所当然是数据访问层的职责。我认为这个理解是对的,但是只对了一半!之所以这么说,是因为“增删改查”有两个层次含义。

      第一个层次,是数据访问层次上的。在这个层次上,“增删改查”只是单纯的数据库操作,“增删改查”可以理解为“插入一条记录,删除一条记录,更新一条记录的信息,获取一条或多条记录”四个操作,其意义和着眼点完全是数据访问层面上的,不带有任何业务成分和业务知觉。这个层面上的CRUD应该属于数据访问层的职责。

      第二个层次,是业务逻辑层次上的。在这个层次上,“增删改查”是业务领域内实体的变化以及一系列相关反应,“增删改查”可以理解为“领域内新增一个业务实体,领域内去掉一个业务实体,领域内一个业务实体更新了信息,得到领域内一个或多个业务实体的信息”。

      两者最大的不同,是业务层面上的增删改查往往不是单纯的增加减少,还包括实体变化后相关的业务流程。下面举个例子:

      “添加一个新的订单”——这是一条典型的“增”操作。在数据访问层面上,它的意义是“在表示订单的数据表里增加一条记录”;而在业务逻辑层面上,它的意义除了“领域内多了一个订单实体”外,还可能包括“根据业务规则判断是否是重复下单,根据金额对下订单客户的等级做相应提升、发送Email和短信通知客户等”。可以看到,业务层面上的“增”可能不仅是简单封装一个简单的插入记录,可能还要去做其他数据访问——提升用户等级,以及做一些非CRUD的业务操作——发送短信通知。

      在许多稍微复杂的系统中,业务往往不仅仅是封装了一条数据访问操作,而是还有很多如计算等业务处理,一个业务操作期间可能要多次使用数据访问操作。退一步说,即使某个业务仅仅封装了一条数据访问操作,其意义和层面也是不同的,在数据访问层面,仅仅是多了一条记录,而业务逻辑层面,是领域内多了一个业务实体。也许其本质上都是往数据库插入一条记录,但人类的抽象思维可以将之在不同层面上区分,这也是人类思维层面的一种抽象能力的表现。例如,我们知道太阳升起不过是地球自转使得从背阴面转到了向阳面,但当人们看日出时,很少有人会说“看!我们从背阴面转到向阳面了!”,我们会说“看!日出!”,这就是同一事物的不同层次表现。

      2.3.4、存储过程

      也许是性能上的诱惑,许多人喜欢在数据库系统中写很复杂的存储过程。这样,许多业务操作就被写到存储过程中去了。我个人建议,除非对性能要求极高,否则最好还是不要用存储过程实现业务。例如,在一般的系统中,某个业务操作可能需要1秒,而是用了存储过程只用0.1秒,看上去存储过程将效率提高了10倍。但对大多数用户来说,1秒和0.1秒的差别并不大,但是这样做的话,业务会变得十分不容易维护。所以,我个人觉得,除非十分必要,还是不要用存储过程实现业务。

 后篇http://kb.cnblogs.com/page/50527

  作者: T2噬菌体
  出处: http://leoo2sk.cnblogs.com
  本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
目前整个开发社区对AOP(Aspect Oriented Programing)推崇备至,也涌现出大量支持AOP的优秀Framework,--Spring, JAC, Jboss AOP 等等。AOP似乎一时之间成了潮流。Java初学者不禁要发出感慨,OOP还没有学通呢,又来AOP。本文不是要在理论上具体阐述何为AOP, 为何要进行AOP . 要详细了解学习AOP可以到它老家http://aosd.net去瞧瞧。这里只是意图通过一个简单的例子向初学者展示一下如何来进行AOP.   为了简单起见,例子没有没有使用任何第三方的AOP Framework, 而是利用Java语言本身自带的动态代理功能来实现AOP.   让我们先回到AOP本身,AOP主要应用于日志记录,性能统计,安全控制,事务处理等方面。它的主要意图就要将日志记录,性能统计,安全控制等等代码从商业逻辑代码中清楚的划分出来,我们可以把这些行为一个一个单独看作系统所要解决的问题,就是所谓的面向问题的编程(不知将AOP译作面向问题的编程是否欠妥)。通过对这些行为的分离,我们希望可以将它们独立地配置到商业方法中,而要改变这些行为也不需要影响到商业方法代码。   假设系统由一系列的BusinessObject所完成业务逻辑功能,系统要求在每一次业务逻辑处理时要做日志记录。这里我们略去具体的业务逻辑代码。 Java代码 public interface BusinessInterface {  public void processBusiness(); } public class BusinessObject implements BusinessInterface {  private Logger logger = Logger.getLogger(this.getClass().getName());  public void processBusiness(){   try {    logger.info("start to processing...");    //business logic here.    System.out.println(“here is business logic”);    logger.info("end processing...");   } catch (Exception e){    logger.info("exception happends...");    //exception handling   }  } } public interface BusinessInterface {  public void processBusiness(); } public class BusinessObject implements BusinessInterface {  private Logger logger = Logger.getLogger(this.getClass().getName());  public void processBusiness(){   try {    logger.info("start to processing...");    //business logic here.    System.out.println(“here is business logic”);    logger.info("end processing...");   } catch (Exception e){    logger.info("exception happends...");    //exception handling   }  } }   这里处理商业逻辑的代码和日志记录代码混合在一起,这给日后的维护带来一定的困难,并且也会造成大量的代码重复。完全相同的log代码将出现在系统的每一个BusinessObject中。   按照AOP的思想,我们应该把日志记录代码分离出来。要将这些代码分离就涉及到一个问题,我们必须知道商业逻辑代码何时被调用,这样我们好插入日志记录代码。一般来说要截获一个方法,我们可以采用回调方法或者动态代理。动态代理一般要更加灵活一些,目前多数的AOP Framework也大都采用了动态代理来实现。这里我们也采用动态代理作为例子。   JDK1.2以后提供了动态代理的支持,程序员通过实现java.lang.reflect.InvocationHandler接口提供一个执行处理器,然后通过java.lang.reflect.Proxy得到一个代理对象,通过这个代理对象来执行商业方法,在商业方法被调用的同时,执行处理器会被自动调用。   有了JDK的这种支持,我们所要做的仅仅是提供一个日志处理器。 Java代码 public class LogHandler implements InvocationHandler {  private Logger logger = Logger.getLogger(this.getClass().getName());   private Object delegate;   public LogHandler(Object delegate){    this.delegate = delegate;   }  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {   Object o = null;   try {    logger.info("method stats..." + method);    o = method.invoke(delegate,args);    logger.info("method ends..." + method);   } catch (Exception e){    logger.info("Exception happends...");    //excetpion handling.   }   return o;  } }   现在我们可以把BusinessObject里面的所有日志处理代码全部去掉了。 public class BusinessObject implements BusinessInterface {  private Logger logger = Logger.getLogger(this.getClass().getName());  public void processBusiness(){   //business processing   System.out.println(“here is business logic”);  } } public class LogHandler implements InvocationHandler {  private Logger logger = Logger.getLogger(this.getClass().getName());   private Object delegate;   public LogHandler(Object delegate){    this.delegate = delegate;   }  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {   Object o = null;   try {    logger.info("method stats..." + method);    o = method.invoke(delegate,args);    logger.info("method ends..." + method);   } catch (Exception e){    logger.info("Exception happends...");    //excetpion handling.   }   return o;  } }   现在我们可以把BusinessObject里面的所有日志处理代码全部去掉了。 public class BusinessObject implements BusinessInterface {  private Logger logger = Logger.getLogger(this.getClass().getName());  public void processBusiness(){   //business processing   System.out.println(“here is business logic”);  } }   客户端调用商业方法的代码如下: Java代码 BusinessInterface businessImp = new BusinessObject(); InvocationHandler handler = new LogHandler(businessImp); BusinessInterface proxy = (BusinessInterface) Proxy.newProxyInstance(  businessImp.getClass().getClassLoader(),  businessImp.getClass().getInterfaces(),  handler); proxy.processBusiness(); BusinessInterface businessImp = new BusinessObject(); InvocationHandler handler = new LogHandler(businessImp); BusinessInterface proxy = (BusinessInterface) Proxy.newProxyInstance(  businessImp.getClass().getClassLoader(),  businessImp.getClass().getInterfaces(),  handler); proxy.processBusiness();   程序输出如下: INFO: method stats... here is business logic INFO: method ends...   至此我们的第一次小尝试算是完成了。可以看到,采用AOP之后,日志记录和业务逻辑代码完全分开了,以后要改变日志记录的话只需要修改日志记录处理器就行了,而业务对象本身(BusinessObject)无需做任何修改。并且这个日志记录不会造成重复代码了,所有的商业处理对象都可以重用这个日志处理器。   当然在实际应用中,这个例子就显得太粗糙了。由于JDK的动态代理并没有直接支持一次注册多个InvocationHandler,那么我们对业务处理方法既要日志记录又要性能统计时,就需要自己做一些变通了。一般我们可以自己定义一个Handler接口,然后维护一个队列存放所有Handler, 当InvocationHandler被触发的时候我们依次调用自己的Handler。所幸的是目前几乎所有的AOP Framework都对这方面提供了很好的支持.这里推荐大家使用Spring。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值