精华内容
下载资源
问答
  • 2021-07-26 13:02:47

    项目代码架构分层

    1、代码分层现状

    传统项目开发中,代码分层架构大概是controller层,Service层,Dao层,在SOA架构中会有facade层,Service层,Dao层,两种方式都是将所有的业务逻辑集中在Service层,包括业务参数的校验逻辑,业务的核心逻辑,对第三方工具的访问逻辑,甚至是持久层的转换逻辑都在这一层,对持久层数据库的访问则写在Dao层;可以发现所有业务都耦合在service层,使所有业务高度耦和,不仅增加了业务理解的难度,同时对后期的开发维护以及升级都代码了极大的困难;

    2、代码分层思考

    很长一段时间,开发中项目应该是什么样的分层才能够更合理,有没有一中代码架构分层,可以将每个项目中通用的逻辑与具体的业务逻辑分开,可以清晰的知道每一层核心业务是什么,这样对不仅有利于项目的开发,更加有利于后期项目的维护和扩展,从而延长项目的生命周期;
    基于以上思考,我们首先分析了代码项目中拥有哪些可以提取的通用的业务逻辑,从而为项目分层组到有章可循:

    1. 参数校验逻辑:所有项目中都有参数校验的逻辑,参数校验又可以分为基本校验,业务校验
      a、基本校验:参数非null,日期大于当下,年龄字段必须大于零等基本的和业务无关的基本校验;
      b、业务校验:传进来的参数所对应的业务数据是否合法,比如用户id对应的用户数据是否存在且合法业务校验;
    2. 参数转换逻辑:不同模块之间参数转换隔离,做到模块升级不影响其他模块的业务。
      这里的转换是就是将其模块或其他三方工具中的数据对象转成我们业务中的数据对象,以达到隔离和防腐的目的;
      例如:我们使用MySql数据库来持久化数据,后来由于业务发展需要,改成了mongdb,二者返回的数据对象是不一样的,这时候我们只需要在防腐层实现新数据对象映射到我们需要的数据对象就可了。
    3. 异常处理:分为异常的抛出和异常的捕获处理与转换
      a、异常抛出:我们认为,不符合业务的异常均应手动抛出,比如参数校验的异常,不符合业务逻辑的异常等;对于第三方工具类抛出的异常我们任务可以统一包装成三方异常,对外抛出;
      b、异常捕获:异常的捕获应该统一处理,我们任务在出口层应该对所有异常做统一的拦截,避免到处try、catch异常逻辑与正常的业务逻辑耦和在一起;
      c、异常处理:将捕获所有的异常,并将异常的堆栈信息记录下来,对于业务异常可以设置统一的异常码外加异常信息返回给用户,同时对于未知的异常统一转换成定义的系统异常对外抛出;
    3、代码分层探索

    基于以上思考,我们对代码架构分层做了以下的探究实践;

    1. facade层:门面层:只有接口和接口入参,以及入参中的一些枚举和异常
      a、这一层主没有任何的业务和逻辑,因为这一层是作为二方包提供给其他使用者调用的,所以这一层要保证绝对的干净。
      b、这一层如果需要有业务逻辑的话,我们认为可以将接口签名算法的业务逻辑放在这一层,方便调用方使用现成的签名算法快速方便的调用接口;
    2. interface层:参数转换校验以及异常的捕获处理转换
      a、接口层完成参数转换,转换成下层业务需要的稳定的数据对象,这样能够保证业务数据变动不对使用方造成影响;对于基本数据类型的转换,我们认为放在数据对象的set方法中比较合理,因为这样可以避免每次转换都写校验逻辑,可以减少大量重复代码的开发;对于业务参数的校验,我们也提倡放interface的service层实现,这样可以做到fastfail,避免后面业务中在检查业务参数而造成的一些性能的浪费;
      b、异常的拦截处理,所有的异常应该在这一层拦截和捕获和记录,然后对不同的异常分类处理,对于预期异常我们可以按既定策略返回;对于其他异常,则应该统一转成其他(或系统)异常对外抛出,注意避免将异常信息直接展示给用户。
      c、infrastructure层:基础设施层,对redis、mq等三方工具的对访问
      这一层主要完成和第三方插件数据的交互,完成数据转换并将数据传递到中间件,从中间件获取数据数据,并转换成业务需要的数据对象;
      d、responsitory层:仓储层,实现持久化对象到业务对象到转换和交互
      在领域设计里,数据库也是中间件的一种,是放在基础设施层的,但是基于现有开发模式以及数据库的重要性,我们应该强化这一层概念,将仓储层单独成模块,来实现数据持久化的相关逻辑。
      e、application层和domain层:核心业务实现模块
      在领域驱动设计中,application层不处理业务只用来编排业务和调用基础设施层的,domain则是核心业务实现的地方,目前我们对业务编排的概念还在探索中国,所以这一部分业务逻辑分层处理我们后续会继续更新讨论。

    在这里插入图片描述

    4、参数统一封装返回

    系统统一参数返回:BaseResult、PojoResult、CollectionResult、ListResult、MapResult、PageResult;BaseResult实现Serializable使结果对象可以序列化传输;其他结果对象均继承BaseResult,只是各自的返回值类型不同而已。

    欢迎大家一块提意见讨论,我们也在继续摸索中;

    更多相关内容
  • 一文教会你如何写复杂业务代码

    千次阅读 2019-08-11 20:14:20
    一个复杂业务的处理过程业务背景简单的介绍下业务背景,零售通是给线下小店供货的B2B模式,我们希望通过数字化重构传统供应链渠道,提升供应链效率,为新零售助力。阿里在中间是一...

    一个复杂业务的处理过程

    业务背景

    简单的介绍下业务背景,零售通是给线下小店供货的B2B模式,我们希望通过数字化重构传统供应链渠道,提升供应链效率,为新零售助力。阿里在中间是一个平台角色,提供的是Bsbc中的service的功能。
    640?wx_fmt=png

    在商品域,运营会操作一个“上架”动作,上架之后,商品就能在零售通上面对小店进行销售了。是零售通业务非常关键的业务操作之一,因此涉及很多的数据校验和关联操作

    针对上架,一个简化的业务流程如下所示:640?wx_fmt=png

    过程分解

    像这么复杂的业务,我想应该没有人会写在一个service方法中吧。一个类解决不了,那就分治吧。

    说实话,能想到分而治之的工程师,已经做的不错了,至少比没有分治思维要好很多。我也见过复杂程度相当的业务,连分解都没有,就是一堆方法和类的堆砌。

    不过,这里存在一个问题:即很多同学过度的依赖工具或是辅助手段来实现分解。比如在我们的商品域中,类似的分解手段至少有3套以上,有自制的流程引擎,有依赖于数据库配置的流程处理:640?wx_fmt=png

    本质上来讲,这些辅助手段做的都是一个pipeline的处理流程,没有其它。因此,我建议此处最好保持KISS(Keep It Simple and Stupid),即最好是什么工具都不要用,次之是用一个极简的Pipeline模式,最差是使用像流程引擎这样的重方法

    除非你的应用有极强的流程可视化和编排的诉求,否则我非常不推荐使用流程引擎等工具。第一,它会引入额外的复杂度,特别是那些需要持久化状态的流程引擎;第二,它会割裂代码,导致阅读代码的不顺畅。大胆断言一下,全天下估计80%对流程引擎的使用都是得不偿失的

    回到商品上架的问题,这里问题核心是工具吗?是设计模式带来的代码灵活性吗?显然不是,问题的核心应该是如何分解问题和抽象问题,知道金字塔原理的应该知道,此处,我们可以使用结构化分解将问题解构成一个有层级的金字塔结构:
    640?wx_fmt=png

    按照这种分解写的代码,就像一本书,目录和内容清晰明了。

    以商品上架为例,程序的入口是一个上架命令(OnSaleCommand), 它由三个阶段(Phase)组成。

    @Command	
    public class OnSaleNormalItemCmdExe {	
    
    	
        @Resource	
        private OnSaleContextInitPhase onSaleContextInitPhase;	
        @Resource	
        private OnSaleDataCheckPhase onSaleDataCheckPhase;	
        @Resource	
        private OnSaleProcessPhase onSaleProcessPhase;	
    
    	
        @Override	
        public Response execute(OnSaleNormalItemCmd cmd) {	
            	
            OnSaleContext onSaleContext = init(cmd);	
            	
            checkData(onSaleContext);	
    
    	
            process(onSaleContext);	
    
    	
            return Response.buildSuccess();	
        }	
    
    	
        private OnSaleContext init(OnSaleNormalItemCmd cmd) {	
            return onSaleContextInitPhase.init(cmd);	
        }	
    
    	
        private void checkData(OnSaleContext onSaleContext) {	
            onSaleDataCheckPhase.check(onSaleContext);	
        }	
    
    	
        private void process(OnSaleContext onSaleContext) {	
            onSaleProcessPhase.process(onSaleContext);	
        }	
    }

    每个Phase又可以拆解成多个步骤(Step),以OnSaleProcessPhase为例,它是由一系列Step组成的:

    @Phase	
    public class OnSaleProcessPhase {	
    
    	
        @Resource	
        private PublishOfferStep publishOfferStep;	
        @Resource	
        private BackOfferBindStep backOfferBindStep;	
        //省略其它step	
    
    	
        public void process(OnSaleContext onSaleContext){	
            SupplierItem supplierItem = onSaleContext.getSupplierItem();	
    
    	
            // 生成OfferGroupNo	
            generateOfferGroupNo(supplierItem);	
           	
           // 发布商品	
            publishOffer(supplierItem);	
    
    	
            // 前后端库存绑定 backoffer域	
            bindBackOfferStock(supplierItem);	
    
    	
            // 同步库存路由 backoffer域	
            syncStockRoute(supplierItem);	
    
    	
            // 设置虚拟商品拓展字段	
            setVirtualProductExtension(supplierItem);	
    
    	
            // 发货保障打标 offer域	
            markSendProtection(supplierItem);	
    
    	
            // 记录变更内容ChangeDetail	
            recordChangeDetail(supplierItem);	
    
    	
            // 同步供货价到BackOffer	
            syncSupplyPriceToBackOffer(supplierItem);	
    
    	
            // 如果是组合商品打标,写扩展信息	
            setCombineProductExtension(supplierItem);	
    
    	
            // 去售罄标	
            removeSellOutTag(offerId);	
    
    	
            // 发送领域事件	
            fireDomainEvent(supplierItem);	
            	
            // 关闭关联的待办事项	
            closeIssues(supplierItem);	
        }	
    }

    看到了吗,这就是商品上架这个复杂业务的业务流程。需要流程引擎吗?不需要,需要设计模式支撑吗?也不需要。对于这种业务流程的表达,简单朴素的组合方法模式(Composed Method)是再合适不过的了。

    因此,在做过程分解的时候,我建议工程师不要把太多精力放在工具上,放在设计模式带来的灵活性上。而是应该多花时间在对问题分析,结构化分解,最后通过合理的抽象,形成合适的阶段(Phase)和步骤(Step)上。
    640?wx_fmt=png

    过程分解后的两个问题

    的确,使用过程分解之后的代码,已经比以前的代码更清晰、更容易维护了。不过,还有两个问题值得我们去关注一下:

    1、领域知识被割裂肢解

    什么叫被肢解?因为我们到目前为止做的都是过程化拆解,导致没有一个聚合领域知识的地方。每个Use Case的代码只关心自己的处理流程,知识没有沉淀。

    相同的业务逻辑会在多个Use Case中被重复实现,导致代码重复度高,即使有复用,最多也就是抽取一个util,代码对业务语义的表达能力很弱,从而影响代码的可读性和可理解性。

    2、代码的业务表达能力缺失

    试想下,在过程式的代码中,所做的事情无外乎就是取数据--做计算--存数据,在这种情况下,要如何通过代码显性化的表达我们的业务呢?说实话,很难做到,因为我们缺失了模型,以及模型之间的关系。脱离模型的业务表达,是缺少韵律和灵魂的。

    举个例子,在上架过程中,有一个校验是检查库存的,其中对于组合品(CombineBackOffer)其库存的处理会和普通品不一样。原来的代码是这么写的:

    boolean isCombineProduct = supplierItem.getSign().isCombProductQuote();	
    
    	
    // supplier.usc warehouse needn't check	
    if (WarehouseTypeEnum.isAliWarehouse(supplierItem.getWarehouseType())) {	
    // quote warehosue check	
    if (CollectionUtil.isEmpty(supplierItem.getWarehouseIdList()) && !isCombineProduct) {	
        throw ExceptionFactory.makeFault(ServiceExceptionCode.SYSTEM_ERROR, "亲,不能发布Offer,请联系仓配运营人员,建立品仓关系!");	
    }	
    // inventory amount check	
    Long sellableAmount = 0L;	
    if (!isCombineProduct) {	
        sellableAmount = normalBiz.acquireSellableAmount(supplierItem.getBackOfferId(), supplierItem.getWarehouseIdList());	
    } else {	
        //组套商品	
        OfferModel backOffer = backOfferQueryService.getBackOffer(supplierItem.getBackOfferId());	
        if (backOffer != null) {	
            sellableAmount = backOffer.getOffer().getTradeModel().getTradeCondition().getAmountOnSale();	
        }	
    }	
    if (sellableAmount < 1) {	
        throw ExceptionFactory.makeFault(ServiceExceptionCode.SYSTEM_ERROR, "亲,实仓库存必须大于0才能发布,请确认已补货.\r[id:" + supplierItem.getId() + "]");	
    }	
    }

    然而,如果我们在系统中引入领域模型之后,其代码会简化为如下:

    if(backOffer.isCloudWarehouse()){	
        return;	
    }	
    
    	
    if (backOffer.isNonInWarehouse()){	
        throw new BizException("亲,不能发布Offer,请联系仓配运营人员,建立品仓关系!");	
    }	
    
    	
    if (backOffer.getStockAmount() < 1){	
        throw new BizException("亲,实仓库存必须大于0才能发布,请确认已补货.\r[id:" + backOffer.getSupplierItem().getCspuCode() + "]");	
    }

    有没有发现,使用模型的表达要清晰易懂很多,而且也不需要做关于组合品的判断了,因为我们在系统中引入了更加贴近现实的对象模型(CombineBackOffer继承BackOffer),通过对象的多态可以消除我们代码中的大部分的if-else。

    640?wx_fmt=png

    过程分解+对象模型

    通过上面的案例,我们可以看到有过程分解要好于没有分解过程分解+对象模型要好于仅仅是过程分解。对于商品上架这个case,如果采用过程分解+对象模型的方式,最终我们会得到一个如下的系统结构:
    640?wx_fmt=png

    写复杂业务的方法论

    通过上面案例的讲解,我想说,我已经交代了复杂业务代码要怎么写:即自上而下的结构化分解+自下而上的面向对象分析

    接下来,让我们把上面的案例进行进一步的提炼,形成一个可落地的方法论,从而可以泛化到更多的复杂业务场景。

    上下结合

    所谓上下结合,是指我们要结合自上而下的过程分解和自下而上的对象建模,螺旋式的构建我们的应用系统。这是一个动态的过程,两个步骤可以交替进行、也可以同时进行。

    这两个步骤是相辅相成的,上面的分析可以帮助我们更好的理清模型之间的关系,而下面的模型表达可以提升我们代码的复用度和业务语义表达能力

    其过程如下图所示:
    640?wx_fmt=png

    使用这种上下结合的方式,我们就有可能在面对任何复杂的业务场景,都能写出干净整洁、易维护的代码。

    能力下沉

    一般来说实践DDD有两个过程:

    1. 套概念阶段

    了解了一些DDD的概念,然后在代码中“使用”Aggregation Root,Bonded Context,Repository等等这些概念。更进一步,也会使用一定的分层策略。然而这种做法一般对复杂度的治理并没有多大作用。

    2. 融会贯通阶段

    术语已经不再重要,理解DDD的本质是统一语言、边界划分和面向对象分析的方法。

    大体上而言,我大概是在1.7的阶段,因为有一个问题一直在困扰我,就是哪些能力应该放在Domain层,是不是按照传统的做法,将所有的业务都收拢到Domain上,这样做合理吗?说实话,这个问题我一直没有想清楚。

    因为在现实业务中,很多的功能都是用例特有的(Use case specific)的,如果“盲目”的使用Domain收拢业务并不见得能带来多大的益处。相反,这种收拢会导致Domain层的膨胀过厚,不够纯粹,反而会影响复用性和表达能力。

    鉴于此,我最近的思考是我们应该采用能力下沉的策略。

    所谓的能力下沉,是指我们不强求一次就能设计出Domain的能力,也不需要强制要求把所有的业务功能都放到Domain层,而是采用实用主义的态度,即只对那些需要在多个场景中需要被复用的能力进行抽象下沉,而不需要复用的,就暂时放在App层的Use Case里就好了。

    注:Use Case是《架构整洁之道》里面的术语,简单理解就是响应一个Request的处理过程

    通过实践,我发现这种循序渐进的能力下沉策略,应该是一种更符合实际、更敏捷的方法。因为我们承认模型不是一次性设计出来的,而是迭代演化出来的。

    下沉的过程如下图所示,假设两个use case中,我们发现uc1的step3和uc2的step1有类似的功能,我们就可以考虑让其下沉到Domain层,从而增加代码的复用性。
    640?wx_fmt=png

    指导下沉有两个关键指标:代码的复用性和内聚性

    复用性是告诉我们When(什么时候该下沉了),即有重复代码的时候。内聚性是告诉我们How(要下沉到哪里),功能有没有内聚到恰当的实体上,有没有放到合适的层次上(因为Domain层的能力也是有两个层次的,一个是Domain Service这是相对比较粗的粒度,另一个是Domain的Model这个是最细粒度的复用)。

    比如,在我们的商品域,经常需要判断一个商品是不是最小单位,是不是中包商品。像这种能力就非常有必要直接挂载在Model上。

    public class CSPU {	
        private String code;	
        private String baseCode;	
        //省略其它属性	
    
    	
        /**	
         * 单品是否为最小单位。	
         *	
         */	
        public boolean isMinimumUnit(){	
            return StringUtils.equals(code, baseCode);	
        }	
    
    	
        /**	
         * 针对中包的特殊处理	
         *	
         */	
        public boolean isMidPackage(){	
            return StringUtils.equals(code, midPackageCode);	
        }	
    }

    之前,因为老系统中没有领域模型,没有CSPU这个实体。你会发现像判断单品是否为最小单位的逻辑是以StringUtils.equals(code, baseCode)的形式散落在代码的各个角落。这种代码的可理解性是可想而知的,至少我在第一眼看到这个代码的时候,是完全不知道什么意思。

    业务技术要怎么做

    写到这里,我想顺便回答一下很多业务技术同学的困惑,也是我之前的困惑:即业务技术到底是在做业务,还是做技术?业务技术的技术性体现在哪里?

    通过上面的案例,我们可以看到业务所面临的复杂性并不亚于底层技术,要想写好业务代码也不是一件容易的事情。业务技术和底层技术人员唯一的区别是他们所面临的问题域不一样。

    业务技术面对的问题域变化更多、面对的人更加庞杂。而底层技术面对的问题域更加稳定、但对技术的要求更加深。比如,如果你需要去开发Pandora,你就要对Classloader有更加深入的了解才行。

    但是,不管是业务技术还是底层技术人员,有一些思维和能力都是共通的。比如,分解问题的能力,抽象思维,结构化思维等等。
    640?wx_fmt=png

    用我的话说就是:“做不好业务开发的,也做不好技术底层开发,反之亦然。业务开发一点都不简单,只是我们很多人把它做“简单”了

    因此,如果从变化的角度来看,业务技术的难度一点不逊色于底层技术,其面临的挑战甚至更大。因此,我想对广大的从事业务技术开发的同学说:沉下心来,夯实自己的基础技术能力、OO能力、建模能力... 不断提升抽象思维、结构化思维、思辨思维... 持续学习精进,写好代码。我们可以在业务技术岗做的很”技术“!


    展开全文
  • 自己作为一名工作不足一年的程序员,来谈怎样写好复杂业务代码这个话题是很惶恐的,尽管有注明加上新手程序员的对象,但有些心得很想和大家交流。自己工作以来,一直在看设计模式和代码整洁的书,平常工作内容主要是...

    前言

    自己作为一名工作不足一年的程序员,来谈怎样写好复杂业务代码这个话题是很惶恐的。不过有些心得还是很想和大家交流。

    自己工作以来,一直在看设计模式和代码整洁的书,平常工作内容主要是写业务代码,最大的感受就是完成需求容易,但把业务代码写简单、整洁易懂真的很不容易。随着慢慢地实践设计模式,参考别人的优秀代码,慢慢写多了,自己也有了这方面的体会或者说方法套路,和大家分享下。

    一、懂得过程分解

    写业务代码经常会碰到很比较复杂的业务流程,比如一个toB场景订单创建流程,就涉及很多处理过程,比如商品状态校验,买家购买资质校验,卖家售卖资质校验,销售规则校验,地址查询,发票查询,库存查询,库存校验,运费计算,促销(优惠卷、购物卡等)计算,价格计算,创建订单,促销占用,库存占用等等。这么多过程的确让人头大,一个Service方法一把梭写下来?老实说在复杂程度高的业务流程,一堆方法和类堆砌起来是最糟糕的写法了。谁都不愿意看到几百行或上千行的一个方法,复杂的业务逻辑穿插其中。

    这时候必要的分解就很有必要了,简单来说就是分而治之,分治的时候把相同阶段的流程合并,就形成一个带有结构化的分治,分解如下:
    在这里插入图片描述
    到这里其实可以怎样实现思路比较清晰了,我们可以利用组合方式,创建订单的类,注入三个服务对象,分别为了初始化上下文,查询校验,执行。然后分别再分治实现每个阶段各个步骤,这样就很清晰了。(这里具体代码就不给出了)

    二、利用领域驱动设计增强业务表达能力

    上面的过程分解,很好地把复杂的业务流程进行合理的拆分成小的过程,使实现起来容易,并且代码也很清晰,但这个过程是比较发散的,而且每个过程只关心自己这个过程的处理,这样会有一个问题就是一些应该被复用的代码可能没有很好复用起来。这时候可以适当使用充血模型(可以理解为具有业务方法的模型),领域方法,将相关的领域能力聚合起来,达到复用的时候,也增强了业务表达能力。

    1.1充血模型

    比如价格的模型,价格类一般包含很多价格字段:商品金额总优惠,商品原价总价,商品改价金额,运费总价,运费改价金额,运费金额总优惠,税费总价,税费改价金额,税费金额总优惠,实付金额。假如我订单预览过程要算实付金额,很多人的做法是,在计算金额的节点把相应的运费、商品价格、税费捞出来,然后算一把实付金额,看起来没有啥问题,这样子做实际上业务表达能力是比较弱的,假如我别的地方也要算这个实付金额呢?重新算一遍,或者利用service封装的方法都不太好,最好的方式是把这个计算方法放进了价格这个模型,这样整个业务的表达能力瞬间就强了很多,实付价格本应属于这个价格模型的,就让它帮我们算就好,不必要在别的地方封装一个方法出来。

    1.2 领域层方法

    其实我们是可以按照领域驱动设计的思想,从系统中分别出各个领域,比如电商系统中,存在商品领域、促销领域、营销领域、会员领域、买家领域、库存领域等等,因此在过程分解中,把零散的相同领域内的方法聚合到一个领域类内,把领域知识聚合起来,知识能沉淀起来。

    三、熟悉使用设计模式

    能熟练应用设计模式到合适的场景,真的能事半功倍,我觉得新手程序员可以不妨深入学习下常用的设计模式,刻意用起来,然后慢慢体会设计模式的好处。比如策略模式、命令模式、工厂模式、责任链模式等等。

    这里我结合一个开发例子说下策略模式,有一个多阶段支付的场景,就是一个订单要分多个阶段支付,比如预付款、货前款、到货款、结算款、质保款等等,支付后成功回调,就可能存在这几种阶段情况。不同阶段支付成功要进行不同的逻辑处理,按常规的处理,要安排上几个if-else了。这样的代码是不太好的,不好扩展,不好修改。如果应用策略设计模式会怎样呢?

    1.1应用策略模式

    上面说的几种支付阶段,就是对应几种策略。下面是具体实现细节

    1. 处理支付回调类:
    @Slf4j
    @MQConsumer(consumeMode = ConsumeMode.CONCURRENTLY,
        consumerGroup = MqConstants.ConsumerGroup.Payment.NOTIFY_PAYMENT_PAID_GROUP)
    @RequiredArgsConstructor
    public class PaymentPaidEventListener {
        @Autowired
        private PaySuccessHandlerContext paySuccessHandlerContext;
    
        @MQSubscribe(topic = "${mq.topic.pay}",tag = MqConstants.ConsumerGroup.Payment.NOTIFY_PAYMENT_PAID_TAG)
        public void paySuccessConsumer(PayCenterPaySuccessPayload payload) {
            if (Objects.isNull(payload)) {
                log.warn("could not handle empty payCenterPaySuccessPayload.");
                return;
            }
    
            if (Objects.isNull(payload.getCurrentStage())) {
                log.warn("could not handle payCenterPaySuccessPayload without currentStage.param:{}", payload);
                return;
            }
            paySuccessHandlerContext.handle(payload);
        }
    }
    
    1. 处理上下文:
    @Service
    @Slf4j
    public class PaySuccessHandlerContext {
    
        @Autowired
        private List<PaySuccessHandler> handlerList;
    
        public void handle(PayCenterPaySuccessPayload payload) {
            PaySuccessHandler handler = Optional.ofNullable(handlerList).orElse(null)
                .stream().filter(h -> h.accept(String.valueOf(payload.getCurrentStage())))
                .findFirst().orElse(null);
            if (handler == null) {
                throw new RuntimeException("can not find paySuccessHandler");
            }
            handler.handle(payload);
        }
    
    }
    
    1. 各个不同支付阶段处理器
    • 3.1结算款支付处理器(其他阶段处理器类似)
    @Service
    @Slf4j
    public class PayForSettlementHandler implements PaySuccessHandler {
        @Override
        public void handle(PayCenterPaySuccessPayload payload) {
            // 处理过程
        }
        @Override
        public boolean accept(String type) {
            return PayStageEnum.PAY_FOR_SETTLEMENT.getOrder().toString().equals(type);
        }
    }
    

    经策略模式处理后,是不是觉得代码很整洁,并且扩展性很好?要增加多一个新的支付阶段处理逻辑很容易,直接增加一个Handler就好了,需求变动修改某个阶段的处理逻辑,就在相应的处理器修改就好了,并且不存在误修改其他阶段处理逻辑的情况。

    四、后记

    在实际开发中,懂过程分解、领域驱动设计、设计模式真的比较容易在复杂的业务场景写出整洁、简单的代码,至少肯定不会写出很烂的代码。我个人认为在实际业务开发中,实现需求仅仅是底线而已,写出简单易懂、扩展性高的代码才是最有挑战性的。沉下心来,务实基础技术能力,不断锻炼分解问题的能力,抽象思维,写出一手好的代码来!与君共勉。

    展开全文
  • 1、业务逻辑、流程本身就复杂,而且有很多定制的小细节 2、后端soa化之后,相同功能抽象成服务,在一个模块中,往往集成了原来各个产品线的逻辑,业务分支极具增大,导致代码里面充斥这if else、重复代码、大代码段...
  • 上例中,将捕获到的异常信息打印到日志中,这样就可以在不影响业务逻辑的情况下,还知道出现了什么异常,可以进一步解决问题。 二、实例解释 最近工作中,发现其他人员开发的模块功能中,在catch语句块中调.

    一、前言

    在catch中捕获到的异常一定要做处理,不能直接return。

    处理方式:

    (1)继续抛出

    (2)打出日志

    反例:

    上例中,对捕获到的异常没有做任何处理,这是不合适的,虽然不影响代码的逻辑,但是代码确实出问题了,而又没有将错误内容显示出来,这样会影响代码排除错误。

    正例:

    上例中,将捕获到的异常信息打印到日志中,这样就可以在不影响业务逻辑的情况下,还知道出现了什么异常,可以进一步解决问题。

    二、实例解释

     最近工作中,发现其他人员开发的模块功能中,在catch语句块中调用了业务方法,目的是当try语句块中的业务逻辑执行过程中发生异常,再执行catch语句块中代码。

          上述情况的业务场景是这样的,try语句块中查询redis缓存(try中查询redis的代码有调用了其他开发人员写的逻辑比较复杂的方法,且多个方法调用),catch语句块中查询后端数据库,开发者意图很明显,就是如果查询redis缓存出现异常,则查询后端数据库,看似很完美的代码设计逻辑。但是完美下面也存在一定几率的风险。

           风险分析。暂定该开发人员叫A,如果try语句块中逻辑比较复杂,且调用了其他开发人员(名字为B)的方法,这时候开发人员B在自己的方法中也利用catch捕获了异常,而不是向上抛出异常,这时候问题出现了,A写的代码中catch语句块的业务逻辑有可能不会执行,这就违背了A的设计意图,也就产生了非常讨厌的逻辑bug。开发人员都知道逻辑bug的原因很难找的。

       所以,在日常开发工作,catch语句块中尽量不要写业务逻辑,就打印写异常日志就可以了。

    参考:

    https://blog.csdn.net/dhklsl/article/details/85095592

    https://blog.csdn.net/qq_27988539/article/details/84753494?utm_medium=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-1.channel_param&depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-1.channel_param

    展开全文
  • java业务逻辑,写在哪里比较好?

    千次阅读 2021-03-12 20:36:04
    java业务逻辑,写在哪里比较好?对于这个问题,我想说的是:规范是死的,人是活的,一般情况下,我们可以根据不同的 java 框架规范的目录来写,特殊情况下也可以自定义。问题分析接触过 java 的同学可能都知道,java...
  • CRM 复杂业务场景的低代码开发实践

    千次阅读 2022-03-15 09:47:22
    高代码开发 其次,对于部分非标准的控件,底下可能包含了非常多的业务逻辑代码,如果使用可视化编排会非常复杂,那么采用编写代码的方式进行封装,封装后还可以沉淀成一个业务组件,以后供其他应用使用。...
  • 按照 Spring 事务管理的要求,事务都是加在 Service 层上,Controller 层只调用一个 Service 方法处理业务。...如果我在同一个 Service 类中注入这多个模块的 Service ,岂不是又增加了代码的耦合度?感觉很矛盾。
  • 但是代码继续看,我发现了我比较惊讶的一个问题,sql写得有点复杂。 首先说明一下,这里说的sql比较复杂,倒并不是动不动几百行那么夸张,也就是用了多张表关联然后加上一些比较生僻的函数之类的。其实对于我个人的...
  • 最近工作中,发现其他人员开发的模块功能中,在catch语句块中调用了业务方法,目的是当try语句块中的业务逻辑执行过程中发生异常,再执行catch语句块中代码。  上述情况的业务场景是这样的,try语句块中查询redis...
  • java业务逻辑怎么写?

    千次阅读 2021-02-28 17:55:40
    现在Java项目一般都是用...一个请求到达后端之后会根据请求的路径找到对应的Controller,Controller会调用相应的业务Service,Service会调用DAO,DAO去执行具体数据库查询操作。同时DAO返回数据到Service,数据经...
  • 逻辑代码自动生成相关技术概述

    千次阅读 2020-08-13 13:44:00
    极大程度地减轻了程序员的开发负担, 使得程序员可以更加关注于业务价值赋能。作者 | 妙净作者周婷婷(花名:妙净,微信: weekendingting)是阿里淘系技术部高级前端技术专家,视觉稿智能生成代码 imgcook平台负责人...
  • 企业业务逻辑常见风险

    千次阅读 2021-01-12 15:06:07
    开发人员的安全意识薄弱(只关注功能的实现,而忽略了用户使用过程中个人行为对Web应用程序业务逻辑功能的安全影响)和开发代码的频繁迭代导致了这些平台业务逻辑级的无休止的安全风险。业务逻辑漏洞主要是开发人员...
  • 复杂业务代码要怎么写

    万次阅读 多人点赞 2019-08-01 18:33:14
    我相信,同样的方法论可以复制到大部分复杂业务场景。 一个复杂业务的处理过程 业务背景 简单的介绍下业务背景,零售通是给线下小店供货的B2B模式,我们希望通过数字化重构传统供应链渠道,提升供应链效率,为新零售...
  • 同步更新博客: cnblogs: 深耕业务 ---- 探索复杂/超复杂前端业务的开发与设计 知乎:深耕业务 ---- 探索复杂/超复杂前端业务的开发与设计 github:深耕业务 ---- 探索复杂/超复杂前端业务的开发与设计 距离上...
  • 一个分层良好的系统,应该将处理用户界面和处理业务逻辑代码分开。
  • 个人观点: java处理的话:看起来会清晰一点;但性能上就要查询多次数据库; sql处理的话:性能上会好一点,但看起来就不清晰了;
  • 用JavaScript编写业务逻辑

    千次阅读 2016-07-13 16:25:19
    从ASP、PHP到Java、ASP.NET,无论采用哪种技术,作为一个系统核心的业务逻辑都是用一种运行在服务器端的语言编写的。架构师习惯将一个应用系统分为多层,视图层、业务逻辑层和数据层等,而它们也都是以某种服务器端...
  • 常见的业务逻辑漏洞-整合篇

    千次阅读 2021-10-28 10:08:38
    业务逻辑漏洞就是指攻击者利用业务/功能上的设计缺陷,获取敏感信息或破坏业务的完整性。一般出现在密码修改、越权访问、密码找回、交易支付金额等功能处。 逻辑漏洞的破坏方式并非是向程序添加破坏内容,而是利用逻辑...
  • 前一阵项目中用到了规则引擎,感觉处理一些复杂逻辑很方便,所以拿来分享一下。 业务背景:需要根据不同的法条,判断当事人是否复核减刑、假释条件,在界面上给予提示。 实现分析:因为法院比较多,且涉及计算种类...
  • 前端的业务逻辑

    千次阅读 2017-08-03 17:11:00
    业务逻辑是核心逻辑,只关注用户的业务,比如管理系统和财务系统,需要处理的业务肯定不一样,但是页面的跳转大致相同,对于持久层的操作也差不多。如果你使用了 SSH 框架,那么 hibernate 和 struts 的代码有很大一...
  • 业务逻辑之终极分析

    千次阅读 2018-08-26 20:01:20
    细说业务逻辑     前言 记得几个月前,在一次北京博客园俱乐部的活动上,最后一个环节是话题自由讨论。就是提几个话题,然后大家各自加入感兴趣的话题小组,进行自由讨论。当时...
  • 复杂业务系统的架构设计思路

    千次阅读 2020-12-13 12:53:55
    最近有一些系统设计方面的思考和...比如电商的商品管理、订单交易等系统的开发和重构,业务相对复杂,开发人天在几个月以上,直接开发可能会老虎啃天,无从下手。 这时候可以通过一个流程化的模板来指导,如果抽象..
  • 开发业务逻辑

    千次阅读 2015-07-21 10:52:46
      ...记得几个月前,在一次北京博客园俱乐部的活动上,最后一个环节是话题自由讨论。就是提几个话题,然后大家各自加入感兴趣的...当时我和大家讨论ASP.NET MVC的相关话题去了,就没能加入“业务逻辑”组的讨论
  • 三层架构 业务逻辑层 workflow

    千次阅读 2019-04-17 14:59:23
    系统的主要功能和业务逻辑都在业务逻辑层进行处理。 这里所说的三层结构,不是物理上的三层,而是逻辑上的三层。 业务逻辑层主要负责对数据层的操作,把一些数据层的操作进行组合。 业务逻辑层又可以细分为业务...
  • 业务逻辑架构模式

    千次阅读 2019-06-27 09:12:55
    业务逻辑架构模式(事务脚本,表模块,活动记录,领域模型)  其实各种架构模式并不是凭空出现的,是你写代码到达一定功底的时候自然出现的结果。走的弯路多了,就会主动去思考该如何将代码组织的更好,更...
  • MVC 架构-业务逻辑写在哪儿

    千次阅读 2019-01-11 15:32:02
    在最近的一个小项目中,发现逻辑稍微复杂一点儿,把业务逻辑放在 Controller 里面就不可维护了。 感觉又象是回到了以前过程式的编程,一点儿面向对象的味道都没有了。  那么,到底在哪些写业务逻辑? 解决 在 SO上...
  • 一文教会你如何写复杂业务代码

    千次阅读 多人点赞 2019-09-21 08:28:02
    了解我的人都知道,我一直在致力于应用架构和代码复杂度的治理。这两天在看零售通商品域的代码。面对零售通如此复杂业务场景,如何在架构和代码层面进行应对,是一个新课题。针对该...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 341,447
精华内容 136,578
关键字:

复杂业务逻辑代码