Batrix企业能力库之物流交易域能力建设实践

这篇具有很好参考价值的文章主要介绍了Batrix企业能力库之物流交易域能力建设实践。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

简介

Batrix企业能力库,是京东物流战略级项目-技术中台架构升级项目的基础底座。致力于建立企业级业务复用能力平台,依托能力复用业务框架Batrix,通过通用能力/扩展能力的定义及复用,灵活支持业务差异化场景的快速能力编排组装,从而通过技术驱动的方式助力业务整体交付吞吐率。

在四层架构(接入层、交易层、履约层、执行层)的背景下,交易平台组承接交易层的业务逻辑,负责交易场景下的可复用能力开发。当前时间,交易订单域已沉淀综合评分超100的能力13个,交易产品域已沉淀综合评分超100的能力5个。

本文重点为大家介绍交易域如何使用Batrix框架沉淀能力

准备工作

针对能力域建设,需要多方共同参与,业务、产品、研发、测试等缺一不可。一个能力域需要什么能力,能力内逻辑是什么,不同能力之间负责的业务边界是什么,都需要业务架构师和技术架构师共同商定,需要有一些前置的顶层设计。

业务架构师前期可以由产品侧出任,后期一定要有业务侧重度参与,需要根据已有的业务沉淀和未来的行业发展规划共同拟定。

技术架构师由研发出任,负责针对能力的开发进行应用架构设计,保证能力编码的合理性,需要针对能力扩展性进行把控,为后续的复用打好基础,和业务架构师共同商定领域上下文的设计。

测试同学主要负责能力质量,在能力建设过程中,难免出现代码的调整,扩展逻辑的提取和沉淀,这些都需要对已有业务逻辑做回归验证,保障能力调整过程中的线上稳定。

能力沉淀是个复杂而又长期的事情,不可能短时间见成效,需要管理者进行预期管理和支持。

能力建设最关键的两个问题:

1、哪些逻辑是能力的基础逻辑,所有业务都需要执行。如果有个性化需求逻辑怎么适配。

2、如何复用现有能力。

建设方法

Batrix框架提供标准能力和扩展能力的建设方案:

标准能力:负责能力的基础逻辑,所有使用到能力的业务都要执行。提前设计扩展插槽,为后续的能力复用扩展打好基础。

扩展能力:针对标准能力内预留的扩展插槽做具体实现,解决个性化逻辑的适配。

Batrix框架提供能力建设的工程实施方案:

BDomainFlowNode:FlowNode是外显的,能够独立提供服务的能力节点,Batrix框架流程编排使用的是以FlowNode为单位的功能编排。简单逻辑可以直接在FlowNode内开发实现。复杂逻辑由FlowNode调用DomainAbility或者DomainService,进行组合引用,实现能力业务逻辑。

BIDomainService:DomainService开发多个ability的组合业务,尽量不要包含复杂的业务处理,只有简单的逻辑代码部分。

BBaseDomainAbility:DomainAbility开发独立的功能点,功能为公共的基础功能,每个ability支持一个类型的扩展插槽。扩展插槽的实现,可以开发个性化需求的代码部分。如果没有个性化代码部分,可以不实现扩展点部分代码。多个ability,由FlowNode或者BIDomainService进行应用组合,提供服务。

BIDomainAbilityExtension:Extension实现SPI定义,开发个性化自定义代码部分。

建设标准能力,需要对本域内的业务有清晰正确的认识,需要有远期的规划。

不同的业务域因业务逻辑不同,所以能力的建设思路会有差异。业务能力的划分粒度,不同能力之间的边界很难通过一次设计就固定下来,还需要根据具体场景,持续优化。在建设能力的过程中,根据理解程度的不同,可以大致分为四个阶段:

初期:只知道能力具体负责什么功能,但不清楚哪些应该属于通用标准逻辑部分,哪些后期需要扩展,为扩展逻辑预留扩展插槽。针对扩展插槽,不清楚扩展逻辑的入参有哪些,出参有哪些。(未进行过能力域建设过的系统,多数属于该阶段。)

方案建议****:全部代码由Extension实现,使用一个FlowNode对外承接调用逻辑,最终由Extension实现业务。如果有个性化逻辑,则使用另一个Extension实现。在后期通过对多个Extension的抽象,分析出哪些是公共逻辑,哪些是个性化逻辑,并把能力进行下一步演化。

示例****:

public class IntegralOccupyFlowNode extends AbstractDomainFlowNode {

    private static final Logger LOGGER = LoggerFactory.getLogger(IntegralOccupyFlowNode.class);

    @Resource
    private CreateIntegralOccupyAbility createIntegralOccupyAbility;

    @Override
    public BOutputMessage call(InputMessage inputMessage) {
        CallerInfo info = Profiler.registerInfo(this.getClass().getName() + ".call"
                , UmpKeyConstants.JDL_OMS_EXPRESS_APP_CODE
                , UmpKeyConstants.METHOD_ENABLE_HEART
                , UmpKeyConstants.METHOD_ENABLE_TP);
        try {
            BOutputMessage outputMessage = new BOutputMessage();
            if (inputMessage.getBody() == null) {
                throw new DomainAbilityException(UnifiedErrorSpec.BasisOrder.INTEGRAL_OCCUPY_FAIL).withCustom("积分占用流程节点执行失败");
            }
            ExpressOrderContext expressOrderContext = (ExpressOrderContext) inputMessage.getBody();
            outputMessage.setBody(expressOrderContext);
            LOGGER.info("积分占用流程节点开始执行");
            createIntegralOccupyAbility.execute(expressOrderContext, this);
            outputMessage.setCallBackInterface(new BDomainFlowNodeCallBack() {
                @Override
                public void callBack() {
                    AbstractDomainAbility releaseAbility = getDomainAbility(expressOrderContext, createErrorIntegralReleaseAbility, modifyErrorIntegralReleaseAbility);
                    if (releaseAbility != null) {
                        releaseAbility.execute(expressOrderContext, IntegralOccupyFlowNode.this);
                    }
                    LOGGER.info("回退积分任务完成");
                }
            });
            outputMessage.setCallBack(true);
            LOGGER.info("积分占用流程节点开始结束");
            return outputMessage;
        } catch (DomainAbilityException e) {
            LOGGER.error("积分占用流程节点执行异常: {}", e.fullMessage());
            throw e;
        } catch (Exception e) {
            Profiler.functionError(info);
            LOGGER.error("积分占用流程节点执行异常", e);
            throw e;
        } finally {
            Profiler.registerInfoEnd(info);
        }
    }

    @Override
    public String getCode() {
        return FlowConstants.EXPRESS_ORDER_INTEGRAL_FLOW_CODE;
    }

    @Override
    public String getName() {
        return FlowConstants.EXPRESS_ORDER_INTEGRA_FLOW_NAME;
    }
}





@DomainAbility(name = "纯配接单领域能力-积分占用活动", parent = DomainConstants.EXPRESS_ORDER_DOMIAN_CODE)
public class CreateIntegralOccupyAbility extends AbstractDomainAbility<ExpressOrderContext, IIntegralExtension> {

    private static final Logger LOGGER = LoggerFactory.getLogger(CreateIntegralOccupyAbility.class);

    /**
     * 积分占用
     *
     * @param context
     * @param bDomainFlowNode
     * @throws DomainAbilityException
     */
    @Override
    public void execute(ExpressOrderContext context, BDomainFlowNode bDomainFlowNode) {
        CallerInfo callerInfo = Profiler.registerInfo(this.getClass().getName() + ".execute"
                , UmpKeyConstants.JDL_OMS_EXPRESS_APP_CODE
                , UmpKeyConstants.METHOD_ENABLE_HEART
                , UmpKeyConstants.METHOD_ENABLE_TP);
        try {
            //根据Batrix配置,获取扩展点实现
            IIntegralExtension extension = this.getMiddleExtensionFast(IIntegralExtension.class,
                    context,
                    SimpleReducer.listCollectOf(Objects::nonNull), bDomainFlowNode);
            extension.execute(context);
        } catch (DomainAbilityException e) {
            LOGGER.error("纯配接单领域能力-积分占用活动执行异常: {}", e.fullMessage());
            throw e;
        } catch (Exception e) {
            Profiler.functionError(callerInfo);
            LOGGER.error("纯配接单领域能力-积分占用活动执行异常", e);
            throw new DomainAbilityException(UnifiedErrorSpec.BasisOrder.INTEGRAL_OCCUPY_FAIL, e);
        } finally {
            Profiler.registerInfoEnd(callerInfo);
        }
    }

    @Override
    public IIntegralExtension getDefaultExtension() {
        return null;
    }
}
/**
 * 积分占用扩展点
 */
@Extension(code = ExpressOrderProduct.CODE)
public class CreateIntegralOccupyExtension implements IIntegralExtension {

    private static final Logger LOGGER = LoggerFactory.getLogger(CreateIntegralOccupyExtension.class);

    @Resource
    private IntegralFacade integralFacade;

    /**
     * 积分占用
     *
     * @param expressOrderContext
     * @throws AbilityExtensionException
     */
    @Override
    public void execute(ExpressOrderContext expressOrderContext) throws AbilityExtensionException {
        CallerInfo callerInfo = Profiler.registerInfo(this.getClass().getName() + ".execute"
                , UmpKeyConstants.JDL_OMS_EXPRESS_APP_CODE
                , UmpKeyConstants.METHOD_ENABLE_HEART
                , UmpKeyConstants.METHOD_ENABLE_TP);
        try {
            LOGGER.info("开始执行接单积分占用扩展点");
            ExpressOrderModel orderModel = expressOrderContext.getOrderModel();
            if (orderModel.getFinance() == null || orderModel.getFinance().getPoints() == null) {
                LOGGER.info("未使用积分,积分占用扩展点执行结束");
                return;
            }
            IntegralFacadeRequest request = new IntegralFacadeRequest();
            request.setPin(orderModel.getOperator());
            request.setBusinessNo(orderModel.getRefOrderInfoDelegate().getWaybillNo());
            Points points = orderModel.getFinance().getPoints();
            request.setDeductNumber(points.getRedeemPointsQuantity().getValue().intValue());
            request.setRelease(false);
            request.setTitle(IntegralOperaterEnum.OCCUPY.getTitle());
            integralFacade.operateIntegrals(request);
        } catch (InfrastructureException infrastructureException) {
            LOGGER.error("接单积分占用扩展点执行异常", infrastructureException);
            throw infrastructureException;
        } catch (Exception exception) {
            Profiler.functionError(callerInfo);
            LOGGER.error("接单积分占用扩展点执行异常", exception);
            throw new AbilityExtensionException(UnifiedErrorSpec.BasisOrder.INTEGRAL_OCCUPY_FAIL, exception);
        } finally {
            Profiler.registerInfoEnd(callerInfo);
        }
    }
}





前期:清楚能力所负责的业务逻辑,针对业务逻辑清楚的了解哪些需要扩展,但为了适配后续业务,不清楚能不能扩展。(交易订单域处在当前状态,正在向下一阶段演化。)

方案建议****:针对业务逻辑进行合理的拆分,粗暴些的方式,可以把能力内的逻辑拆分为前中后逻辑,支持在能力核心逻辑的前后进行个性化逻辑扩展实现调用,入参传入全量领域上下文提供使用。好处在于扩展灵活,方便个性化逻辑开发。坏处在于不好管理,SPI丧失接口隔离原则。

示例****:

@Service
public class OccupyStockFlowNode extends AbstractDomainFlowNode {
    private static final Logger LOGGER = LoggerFactory.getLogger(OccupyStockFlowNode.class);
    /**
     * 预占库存
     */
    @Resource
    private CreateOccupyStockAbility createOccupyStockAbility;

    @Resource
    private CreateOccupyStockFrontAbility createOccupyStockFrontAbility;

    @Resource
    private CreateOccupyStockPostAbility createOccupyStockPostAbility;

    /**
     * 释放库存
     */
    @Resource
    private CreateOccupyStockErrorRollbackAbility createOccupyStockErrorRollbackAbility;

    @Resource
    private AlarmUtil alarmUtil;


    /**
     * @param
     * @return
     * @Description 预占库存能力流程节点
     * @lastModify
     */
    @Override
    public BOutputMessage call(InputMessage inputMessage) {
        CallerInfo info = Profiler.registerInfo(this.getClass().getName() + ".call"
                , UmpKeyConstants.JDL_OMS_SUPPLYCHAIN_APP_CODE
                , UmpKeyConstants.METHOD_ENABLE_HEART
                , UmpKeyConstants.METHOD_ENABLE_TP);

        CallerInfo sectionInfo = null, buSectionInfo = null;

        try {
            SupplyChainOrderContext context = (SupplyChainOrderContext) inputMessage.getBody();
            BOutputMessage outputMessage = new BOutputMessage();
            outputMessage.setBody(context);

            sectionInfo = Profiler.registerInfo(this.getClass().getName() + ".call.section." + ClobOrderUtils.getSectionString(context)
                , UmpKeyConstants.JDL_OMS_SUPPLYCHAIN_APP_CODE
                , UmpKeyConstants.METHOD_ENABLE_HEART
                , UmpKeyConstants.METHOD_ENABLE_TP);

            buSectionInfo = Profiler.registerInfo(this.getClass().getName() + ".call.bu." + context.getBusinessIdentity().getBusinessUnit()
                            + ".section." + ClobOrderUtils.getSectionString(context)
                    , UmpKeyConstants.JDL_OMS_SUPPLYCHAIN_APP_CODE
                    , UmpKeyConstants.METHOD_ENABLE_HEART
                    , UmpKeyConstants.METHOD_ENABLE_TP);

            //前置预占入参处理
            createOccupyStockFrontAbility.execute(context, this);

            //库存预占
            createOccupyStockAbility.execute(context, this);

            //后置预占结果处理
            createOccupyStockPostAbility.execute(context, this);
            outputMessage.setCallBackInterface(new BDomainFlowNodeCallBack() {
                @Override
                public void callBack() {
                    SmartPattern smartPattern = context.getSupplyChainOrderModel().getSmartPattern();
                    if (smartPattern.hasBeanOccupySuccess()) {
                        String msg = String.format("客户单号:%s,订单号:%s,接单或重新受理失败,回滚释放预占库存", context.getSupplyChainOrderModel().getChannel().getCustomerOrderNo(), context.getSupplyChainOrderModel().orderNo());
                        LOGGER.info(msg);
                        Profiler.businessAlarm(UmpKeyConstants.UMP_JDL_OMS_CREATE_PERSIST_FAIL_PDQ_RELEASE_STOCK_ALARM_MONITOR, msg);
                        createOccupyStockErrorRollbackAbility.execute(context, OccupyStockFlowNode.this);
                    }
                    
                }
            });
            return outputMessage;
        } catch (DomainAbilityException e) {
            LOGGER.error("预占库存能力流程执行异常", e);
            SupplyChainOrderContext context = (SupplyChainOrderContext) inputMessage.getBody();
            alarmUtil.newAlarm().key(UmpKeyConstants.UMP_JDL_OMS_OCCUPY_STOCK_EXCEPTION_ALARM_MONITOR)
                    .model(context.getSupplyChainOrderModel())
                    .alarmMessage("预占库存能力流程执行异常code:%s,message:%s" , e.code(), e.getMessage())
                    .alarm();
            throw e;
        } catch (Exception e) {
            Profiler.functionError(info);
            if (sectionInfo != null) {
                Profiler.functionError(sectionInfo);
            }
            if (buSectionInfo != null) {
                Profiler.functionError(buSectionInfo);
            }
            LOGGER.error("预占库存能力流程执行异常", e);
            throw e;
        } finally {
            Profiler.registerInfoEnd(info);
            if (sectionInfo != null) {
                Profiler.registerInfoEnd(sectionInfo);
            }
            if (buSectionInfo != null) {
                Profiler.registerInfoEnd(buSectionInfo);
            }
        }
    }
}


/**
 * @Description: 接单库存预占前置活动
 */
@DomainAbility(name = "仓配领域能力-接单库存预占前置活动", parent = DomainConstants.SUPPLY_CHAIN_ORDER_DOMIAN_CODE)
@AbilityScene(businessScenes = {BusinessSceneEnum.CREATE}, isDefault = false)
public class CreateOccupyStockFrontAbility extends OccupyStockFrontAbility {
    /**
     * log
     */
    private static final Logger LOGGER = LoggerFactory.getLogger(CreateOccupyStockFrontAbility.class);

    @Override
    public void execute(SupplyChainOrderContext supplyChainOrderContext, BDomainFlowNode bDomainFlowNode) throws DomainAbilityException {
        CallerInfo callerInfo = UmpUtils.getCallerInfo(this.getClass().getName() , "execute");
        try {
            //根据Batrix配置,获取扩展点实现
            IOccupyFrontExtensionPlugin extension = this.getMiddleExtensionFast(IOccupyFrontExtensionPlugin.class, supplyChainOrderContext, SimpleReducer.listCollectOf(Objects::nonNull), bDomainFlowNode);
            if (extension != null) {
                String orderNo = StringUtils.isNotBlank(supplyChainOrderContext.getSupplyChainOrderModel().orderNo()) ? supplyChainOrderContext.getSupplyChainOrderModel().orderNo() : supplyChainOrderContext.getSupplyChainOrderModel().getChannel().getCustomerOrderNo();
                IOccupyFrontRequest occupyFrontRequest = new OccupyFrontRequest();
                occupyFrontRequest.createWith(supplyChainOrderContext.getSupplyChainOrderModel().getCargoDelegate());
                occupyFrontRequest.getExtendProps().put("orderNo", orderNo);
                if (LOGGER.isInfoEnabled()) {
                    LOGGER.info("单号:{},库存预占前置扩展插件请求入参:{}", orderNo, JSONUtils.beanToJSONDefault(occupyFrontRequest));
                }
                IOccupyFrontResponse iOccupyFrontResponse = extension.execute(supplyChainOrderContext.getSupplyChainOrderModel().requestProfile(), occupyFrontRequest);
                if (LOGGER.isInfoEnabled()) {
                    LOGGER.info("单号:{},库存预占前置扩展插件返回结果:{}", orderNo, JSONUtils.beanToJSONDefault(iOccupyFrontResponse));
                }
                if (!iOccupyFrontResponse.isSuccess()) {
                    LOGGER.error("库存预占前置扩展插件异常", iOccupyFrontResponse.getThrowable());
                    throw new BusinessDomainException(UnifiedErrorSpec.BasisOrder.OCCUPY_STOCK_FAIL).withCustom(iOccupyFrontResponse.getMessage());
                }

                IStockCargoDelegate iStockCargoDelegate = iOccupyFrontResponse.getStockCargoDelegate();
                StockSupportModelCreator stockSupportModelCreator = new StockSupportModelCreator();
                stockSupportModelCreator.setCargoInfos(toCargoInfoList(iStockCargoDelegate.getCargoList()));
                Integer orderType = Integer.valueOf(supplyChainOrderContext.getSupplyChainOrderModel().getExtendProps().get(IsvExtendFieldNameEnum.ORDER_SELL_MODEL.getCode()));
                stockSupportModelCreator.setOrderType(orderType);
                stockSupportModelCreator.setBizType(BizTypeEnum.SOO.getCode());
                supplyChainOrderContext.getStockSupportModel().complementStockCargoDelegate(this, stockSupportModelCreator);
                supplyChainOrderContext.getStockSupportModel().complementOrderType(this, stockSupportModelCreator);
                supplyChainOrderContext.getStockSupportModel().complementBizType(this, stockSupportModelCreator);
            }
        } catch (AbilityExtensionException e) {
            LOGGER.error("仓配领域能力-库存预占前置活动能力执行异常: ", e);
            throw e;
        } catch (Exception e) {
            Profiler.functionError(callerInfo);
            LOGGER.error("仓配领域能力-库存预占前置活动能力执行异常: ", e);
            throw new DomainAbilityException(UnifiedErrorSpec.BasisOrder.OCCUPY_STOCK_FAIL, e);
        } finally {
            Profiler.registerInfoEnd(callerInfo);
        }
    }


    @Override
    public IOccupyFrontExtensionPlugin getDefaultExtension() {
        return null;
    }
}
/**
 * @Description: 库存预占
 */
@DomainAbility(name = "仓配领域能力-接单库存预占活动", parent = DomainConstants.SUPPLY_CHAIN_ORDER_DOMIAN_CODE)
@AbilityScene(businessScenes = {BusinessSceneEnum.CREATE}, isDefault = false)
public class CreateOccupyStockAbility extends AbstractDomainAbility<SupplyChainOrderContext, IOccupyStockExtension> {
    private static final Logger LOGGER = LoggerFactory.getLogger(CreateOccupyStockAbility.class);

    /**
     * @Description 预占库存活动能力
     */
    @Override
    public void execute(SupplyChainOrderContext supplyChainOrderContext, BDomainFlowNode bDomainFlowNode) throws DomainAbilityException {
        CallerInfo callerInfo = Profiler.registerInfo(this.getClass().getName() + ".execute"
                , UmpKeyConstants.JDL_OMS_SUPPLYCHAIN_APP_CODE
                , UmpKeyConstants.METHOD_ENABLE_HEART
                , UmpKeyConstants.METHOD_ENABLE_TP);

        CallerInfo sectionInfo = Profiler.registerInfo(this.getClass().getName() + ".execute.section." + ClobOrderUtils.getSectionString(supplyChainOrderContext)
                , UmpKeyConstants.JDL_OMS_SUPPLYCHAIN_APP_CODE
                , UmpKeyConstants.METHOD_ENABLE_HEART
                , UmpKeyConstants.METHOD_ENABLE_TP);
        try {
            //根据Batrix配置,获取扩展点实现
            IOccupyStockExtension extension = this.getMiddleExtensionFast(IOccupyStockExtension.class,
                    supplyChainOrderContext,
                    SimpleReducer.listCollectOf(Objects::nonNull), bDomainFlowNode);
            extension.execute(supplyChainOrderContext);
        } catch (AbilityExtensionException e) {
            LOGGER.error("仓配领域能力-接单库存预占活动能力执行异常: ", e);
            throw e;
        } catch (Exception e) {
            Profiler.functionError(callerInfo);
            Profiler.functionError(sectionInfo);
            LOGGER.error("仓配领域能力-接单库存预占活动能力执行异常: ", e);
            throw new DomainAbilityException(UnifiedErrorSpec.BasisOrder.OCCUPY_STOCK_FAIL, e);
        } finally {
            Profiler.registerInfoEnd(callerInfo);
            Profiler.registerInfoEnd(sectionInfo);
        }
    }
}
/**
 * @Description: 接单库存预占后置活动
 */
@DomainAbility(name = "仓配领域能力-接单库存预占后置活动", parent = DomainConstants.SUPPLY_CHAIN_ORDER_DOMIAN_CODE)
@AbilityScene(businessScenes = {BusinessSceneEnum.CREATE}, isDefault = false)
public class CreateOccupyStockPostAbility extends OccupyStockPostAbility {

    private static final Logger LOGGER = LoggerFactory.getLogger(CreateOccupyStockPostAbility.class);

    @Override
    public void execute(SupplyChainOrderContext supplyChainOrderContext, BDomainFlowNode bDomainFlowNode) {
        //根据Batrix配置,获取扩展点实现
        IOccupyPostExtensionPlugin extension = this.getMiddleExtensionFast(IOccupyPostExtensionPlugin.class,
                supplyChainOrderContext,
                SimpleReducer.listCollectOf(Objects::nonNull), bDomainFlowNode);
        if (extension != null) {
            String orderNo = StringUtils.isNotBlank(supplyChainOrderContext.getSupplyChainOrderModel().orderNo())
                    ? supplyChainOrderContext.getSupplyChainOrderModel().orderNo() : supplyChainOrderContext.getSupplyChainOrderModel().getChannel().getCustomerOrderNo();
            //订单域货品信息
            ICargoDelegate iCargoDelegate = supplyChainOrderContext.getSupplyChainOrderModel().getCargoDelegate();
            //订单域发货仓
            IWarehouse iWarehouse = supplyChainOrderContext.getSupplyChainOrderModel().getConsignor().getWarehouse();
            //订单域智能策略
            ISmartPattern iSmartPattern = supplyChainOrderContext.getSupplyChainOrderModel().getSmartPattern();
            //库存域预占结果
            Integer stockOccupyResult = supplyChainOrderContext.getStockSupportModel().getStockOccupyResult();
            //库存域货品信息
            IStockCargoDelegate iStockCargoDelegate = supplyChainOrderContext.getStockSupportModel().getStockCargoDelegate();
            //库存域缺量信息
            IStockShortDelegate iStockShortDelegate = supplyChainOrderContext.getStockSupportModel().getStockShortDelegate();
            //预占库房类型
            String occupyWarehouseType = supplyChainOrderContext.getStockSupportModel().getOccupyWarehouseType();
            //库存质押结果
            Boolean pledgeResult = supplyChainOrderContext.getStockSupportModel().getPledgeResult();
            IOccupyPostRequest iOccupyPostRequest = new OccupyPostRequest();
            iOccupyPostRequest.getExtendProps().put(BusinessConstants.OCCUPY_WAREHOUSE_TYPE, occupyWarehouseType);
            iOccupyPostRequest.createWith(iCargoDelegate, iStockCargoDelegate, iStockShortDelegate, orderNo, stockOccupyResult, iSmartPattern, iWarehouse, pledgeResult);
            if (LOGGER.isInfoEnabled()) {
                LOGGER.info("单号:{},库存预占后置扩展插件请求入参:{}", orderNo, JSONUtils.beanToJSONDefault(iOccupyPostRequest));
            }
            IOccupyPostResponse iOccupyPostResponse = extension.execute(supplyChainOrderContext.getSupplyChainOrderModel().requestProfile(), iOccupyPostRequest);
            if (LOGGER.isInfoEnabled()) {
                LOGGER.info("单号:{},库存预占后置扩展插件返回结果:{}", orderNo, JSONUtils.beanToJSONDefault(iOccupyPostResponse));
            }
            if (!iOccupyPostResponse.isSuccess()) {
                LOGGER.error("库存预占后置扩展插件异常", iOccupyPostResponse.getThrowable());
                throw new BusinessDomainException(UnifiedErrorSpec.BasisOrder.OCCUPY_STOCK_FAIL).withCustom(iOccupyPostResponse.getMessage());
            }
            //预占结果:成功
            if (OccupyResultEnum.SUCCESS.getCode().equals(iOccupyPostResponse.getOccupyResult())) {
                //获取预占后处理的货品信息
                ICargoDelegate iOrderCargoDelegate = iOccupyPostResponse.getCargoDelegate();
                //根据预占后处理的货品信息回写订单域货品信息
                assembleCargoOccupyStockNum(supplyChainOrderContext, iOrderCargoDelegate);
                //回写订单域预占结果
                assembleOccupyResult(supplyChainOrderContext, OccupyResultEnum.SUCCESS.getCode(), null);
                LOGGER.info("单号:{},库存预占后置扩展插件,预占成功回写订单域预占结果和货品预占数量:{}", orderNo, JSONUtils.beanToJSONDefault(supplyChainOrderContext));
            } else if (OccupyResultEnum.STOCK_SHORTAGE.getCode().equals(iOccupyPostResponse.getOccupyResult())) {
                // 库存不足情况处理逻辑
                LOGGER.info("单号:{},库存预占后置扩展插件,库存不足补全库存、异常支撑域", orderNo);
                handleStockShortage(supplyChainOrderContext);
            } else {
                handleOtherException(supplyChainOrderContext, iOccupyPostResponse);
                //预占结果:目标仓预占失败
                LOGGER.info("单号:{},库存预占后置扩展插件,目标仓预占失败,兜底原仓预占", orderNo);
                //清空计划库房(目标仓)
                cleanPlanWarehouse(supplyChainOrderContext);
            }
        }
    }
}


中期:清楚能力所负责的业务逻辑,针对业务逻辑清楚的了解哪些能扩展,哪些不能扩展,针对扩展逻辑所使用的参数字段,有合理的规划。(交易产品域处在当前阶段。)

方案建议:针对业务逻辑进行合理的拆分,精细化的方式,针对能力内的逻辑有详细设计,哪些逻辑能扩展,哪些逻辑必须执行都有规划,每个Extension方法的出参和入参都有设计。对Extension根据具体逻辑和使用场景,能够进行本地扩展点调用和远程扩展点调用等区分。

示例

/**
 * 产品日历信息能力点
 */
@Slf4j
@Component
@AbilityNodeUnit(code = "productCalendarNode", name = "产品日历信息能力点")
public class ProductCalendarNode extends AbstractRequiredAbilityNode {

    /**
     * 派送开始时间和结束时间的格式
     */
    private static final String RECEIVE_TIME_FORMAT = "yyyy-MM-dd HH:mm";
    /**
     * 开始和结束时间的格式
     */
    private static final String TIME_RANGE = "HH:mm";

    @Autowired
    private AddressStationAbility addressStationAbility;
    @Autowired
    private SliceDeliveryAcquirer sliceDeliveryAcquirer;
    @Autowired
    private AbilityCheckProcessor abilityCheckProcessor;

    @Override
    public boolean paramCheck(InputMessage inputMessage) {
        return true;
    }

    @Override
    public void acquireInfo(InputMessage inputMessage) throws Exception {
        ProductRequestDto productRequestDto = acquireProductRequestDto(inputMessage);
        List<ProductDto> needCheckProductDtoList = new ArrayList<>();
        try {
            //校验逻辑
            if (productRequestDto.getRequestType() == ProductRequestDto.CHECK_REQUEST_TYPE) {
                //获取需要校验此能力点的产品列表
                needCheckProductDtoList = matchAbilityCode(inputMessage);
            }
            //供给逻辑
            if (productRequestDto.getRequestType() == ProductRequestDto.SUPPLY_REQUEST_TYPE) {
                //校验所有配置了预约派送日历的产品
                needCheckProductDtoList = matchAbilityCodeIgnoreStatus(inputMessage);
            }
            log.info("[Node][日历信息能力点]ProductCalendarNode.acquireInfo(),checkProducts:{}", abilityCheckProcessor.getProductNosStr(needCheckProductDtoList));
            if (!CollectionUtils.isEmpty(needCheckProductDtoList)) {
                //从扩展点返回结果中获取站点信息完成
                addressStationAbility.getAddressStationInfo(inputMessage, this);
                //从路由接口获取日历信息
                sliceDeliveryAcquirer.acquire(needCheckProductDtoList, productRequestDto);
            }
        } catch (Exception e) {
            log.info("[Node][日历信息能力点]ProductCalendarNode.acquireInfo() exception msg:" + e.getMessage(), e);
        }
    }
}
/**
 * 地址和站点信息获取能力点
 * @date 2022/12/5
 */
@Slf4j
@Component
public class AddressStationAbility extends BBaseDomainAbility<DomainModel, AddressStationCheckService> {

    @Autowired
    private CalendarAbilityProcessor calendarAbilityProcessor;

    @Override
    public AddressStationCheckService getDefaultExtension() {
        return null;
    }

    public void getAddressStationInfo(InputMessage inputMessage, BDomainFlowNode bDomainFlowNode) {
        BDomainModel bDomainModel = (BDomainModel) inputMessage.getBody();
        ProductRequestDto productRequestDto = (ProductRequestDto) bDomainModel;
        //获取Batrix扩展点调用,实际为远程扩展点
        AddressStationCheckService addressStationCheckService = getMiddleExtensionFast(AddressStationCheckService.class, bDomainModel,
                SimpleReducer.firstOf((String f) -> f != null), bDomainFlowNode);
        //从扩展点返回结果中获取站点信息完成
        if (addressStationCheckService != null) {
            AddressStationCheckServiceAdapter addressStationCheckServiceAdapter = new AddressStationCheckServiceAdapter(addressStationCheckService);
            //调用扩展点
            AddressStationCheckResponse addressStationCheckResponse = addressStationCheckServiceAdapter.check(DtoUtils.createRequestProfile(productRequestDto),
                    calendarAbilityProcessor.createAddressStationCheckRequest(productRequestDto), productRequestDto);
            //解析出参,赋值到ProductRequestDto
            calendarAbilityProcessor.parseAddressStationCheckResponse(addressStationCheckResponse, productRequestDto);
        }
    }
}
/**
 * 从路由接口获取日历切片
 */
@Slf4j
@Component
public class SliceDeliveryAcquirer {

    @Autowired
    private CalendarAbilityProcessor calendarAbilityProcessor;

    /**
     * 获取日历切片信息
     */
    public void acquire(List<ProductDto> productDtoList, ProductRequestDto productRequestDto) {
        boolean getDeliveryInfo = productRequestDto.getEndStationDto().getSupportDaytimeDelivery() != null && productRequestDto.getEndStationDto().getSupportNightDelivery() != null;
        if (!getDeliveryInfo) {
            log.error("[Ability][日历信息校验]ProductCalendarCheckAbility.doAcquireInfo(),从扩展点获取站点配送能力失败,停止获取路由日历信息");
            return;
        }
        calendarAbilityProcessor.acquireSliceDelivery(productRequestDto, productDtoList);
    }
}
/**
 * 能力校验逻辑
 */
@Component
@Slf4j
public class AbilityCheckProcessor {
    /**
     * 根据业务身份判断校验项
     */
    @Autowired
    private ProductMarkConstant productMarkConstant;

    /**
     * 验证该产品是否需要校验此能力点
     * @return true:校验。false:不校验
     */
    public boolean check(ProductRequestDto productRequestDto, ProductDto productDto, String abilityNo) {
        if (productDto == null) {
            return false;
        }
        if (StringUtils.isBlank(productDto.getCode())) {
            return true;
        }
        if (!ProductValidResMsgEnum.SUCCESS.getCode().equals(productDto.getCode())) {
            return false;
        }
        return productMarkConstant.check(productRequestDto.getBusinessUnitEnum(), productDto.getProductNo(), abilityNo);
    }

    /**
     * 验证该产品主增嵌套关系是否需要校验此能力点,返回所有需要校验的ProductDto
     */
    public List<ProductDto> check(ProductRequestDto productRequestDto, ProductResultDto productResultDto, String abilityNo) {
        List<ProductDto> result = new ArrayList<>();
        // 增值产品如果主产品校验失败,下面的增值产品不用校验。终端揽收不走此逻辑
        if (!ProductValidResMsgEnum.SUCCESS.getCode().equals(productResultDto.getMainProductDto().getCode())) {
            if (!BusinessSceneEnum.COLLECT.getCode().equals(productRequestDto.getBusinessIdentity().getBusinessScene())) {
                return result;
            }
        }
        if (check(productRequestDto, productResultDto.getMainProductDto(), abilityNo)) {
            result.add(productResultDto.getMainProductDto());
        }
        for (ProductDto addValDto : productResultDto.getValueAddedProducts().values()) {
            if (check(productRequestDto, addValDto, abilityNo)) {
                result.add(addValDto);
            }
        }
        return result;
    }
}

后期:能力本身逻辑、健壮性等都处在较高水平,但能力的使用场景过多,且差异无法抽象统一到一个能力内。

方案建议:不用强求把所有需求集合到一个能力内,成本和复杂度过高。可以把一个业务能力做拆分,提供2-3个不同模式的技术能力出去,业务使用方根据需要,进一步选择扩展,融入到业务里边。使用2-3个不同的能力模式,覆盖85%-95%的业务场景,如果依然有极个别的业务无法使用,那针对业务开发个性化能力支持。

如何复用

能力的使用,分几个层次,相关优先级如下:

▪ 当需求提出后,经过分析,现有标准能力是否满足诉求。同一个能力可以有2-3中模式,其中某一个模式满足需求的话,就可以提供使用。

▪ 现有标准能力不满足的情况下,评估使用扩展能力是否满足诉求,从标准能力的不同模式中,选一个最接近需求的进行扩展。

▪ 扩展能力本质上是标准能力和扩展点的组合,标准能力负责通用逻辑的执行,扩展点负责个性化逻辑的执行。业务系统基于稳定性、扩展性的方便考虑,还可以使用远程扩展点,把扩展逻辑放到独立的服务上运行。

▪ 如果扩展能力依旧不满足需求的话,可以新建能力。

▪ 如果分析后发现是能力缺失,则新建标准能力,以备后续业务复用。

▪ 如果分析后发现是小场景需求,可以针对该需求新建自定义能力,只针对该业务场景使用。

确定好使用哪些能力后,可以使用Batrix提供的控制台-神行平台,进行能力的流程编排,通过能力的编排组合最终形成满足业务需要的逻辑。如下:

该流程为中小件isv-销售出-接单流程,由交易订单域承接,通过流程编排满足中小件ISV的销售出库业务接单逻辑。可以通过对Batrix框架同异步双引擎模式的使用,控制能力的同步执行和异步执行,满足业务对性能和异步履约调度的不同诉求。

随着业务需求的版本迭代,能力也会进行更新替换。如果能力不满足新的业务使用,则需要对能力进行开发,或者建立新的能力编排进去,替换原有逻辑。如果业务已经没有使用方,没有调用量了,则可以删除流程,进而删除能力,保障系统工程的长期完整、稳定,降低腐蚀

企业能力共享

当前能力的复用,也仅限于能力所属的业务域系统。承接需求的吞吐速度取决于能力的Owner的支持速度,但随着业务复杂度提高,各BP部门需求吞集中到中台部门。中台资源有限,BP基于可复用的中台能力实现扩展点,可抵消一部分对中台资源的依赖。但对能力上的需求,只能采用工程共建的方式解决,长期来讲存在很多隐患,功能无法解耦,系统稳定性无法保证。全部依赖中台开发能力的话,中台资源可能会成为交付瓶颈。

所以Batrix企业能力库支持了能力共享模式,主要为解决如下问题:

▪ 通过能力共享模式,进一步解决资源瓶颈问题。

▪ 通过流程编排按需加载,加速业务交付效率。

基于能力共享模式,前中台协同方式有所调整:

▪ 中台研发:开发可复用、稳定的能力,设计扩展点插槽,发布能力。

▪ BP研发:基于业务需求,选择能力进行业务流程编排,通过扩展点实现,补全中台能力不满足需求的部分。如果中台能力不满足业务需要,BP可自建能力进行流程编排运行。

▪ 业务应用:业务Owner,通过流程编排把不同能力运行在自己的系统中,负责对外业务的对接维护。中台提供能力的稳定性和复用度建设,维护内部的稳定性,最终提高业务的交付效率。

调整后交易订单系统架构如下:

应用架构设计,通过中台流量中间件统一收口对外业务请求,通过中台数据中间件统一收口对内数据处理,中间业务应用容器处理一部分适配逻辑,通过Batrix框架进行共享能力调用。

▪ 中台流量中间件:接收业务流量渠道的请求,根据分流属性 ,把请求路由到不同的业务系统中。

▪ 业务应用容器:

▪ 通过REMOTE层适配对外接口标准

▪ 通过LOCAL层处理转化对领域服务模型

▪ 通过BATRIX层运行业务能力,按流程编排运行,满足业务诉求。

▪ 中台数据中间件:处理数据操作请求。

▪ 辅助系统:通过配置中心、监控中心、安全中心辅助系统运行,加速交付效率,提升系统稳定性。

能力共享并不是适用于所有业务和所有能力,他的使用门槛较高,对能力的建设者和业务的使用者都有一些要求。

▪ 能力的建设者要求:

▪ 如何保证提供的能力稳定、高效。

▪ 如何保持必要的基本开发原则。

▪ 能力版本迭代的时候领域上下文如何管理,和现有业务如何兼容。

▪ 能力内涉及到下游接口、中间件、数据存储服务是共用一套,还是隔离开。

▪ 业务的使用者要求:

▪ 如何清晰的知晓所使用的能力内部逻辑,以便运维自己的业务。

▪ 业务异常的情况下,如何快速定位问题点并予以修复。

▪ 业务逻辑开发调试起来体验较差。

能力治理

能力开发完成后,如果不进行管理,就容易出现能力爆炸。大家都去新建能力,但不进行优化沉淀,依然达不到效果。

根据不同阶段,能力的生命周期如下:

▪ 需求承接后可以创建新的能力进行逻辑开发。

▪ 创建能力后需要进行不断的沉淀优化,具备稳定的服务后才能对外发布。

▪ 能力开发完成后可以进行流程编排,提供业务运行使用。

▪ 随着时间推移和版本迭代,能力本身会存在很多冗余代码,需要对能力本身进行周期性的沉淀优化,保障能力的稳定性,承接更多的业务。

▪ 如果长期看能力不能在进行优化了,那需要对能力本身废弃,生成新的能力提供服务。

▪ 旧能力下线,需要在对应的使用流程中进行删除,删除对能力有依赖的业务流程引用,最后删除相关代码。

如何判断能力开发的好坏,哪些需要优化,哪些需要清除,逻辑是否能够通用?

当前Batrix能力治理提供几个维度衡量:

调用度:标准能力被流程使用的次数(由标准能力创建出来的扩展能力也算标准能力的使用),复用度越高,表明能力支持的业务越多。

业务身份覆盖度:使用能力的的业务身份数量。

扩展度:由标准能力创建出来的扩展能力数量,该指标需要和复用度综合来看。复用度越高,扩展度约低,表明能力的通用性越高,不用进行扩展,就可以支持多业务条线。反之,复用度越低,扩展度越高,表明能力的通用逻辑不够支持业务诉求,需要进行大量的扩展,才能满足。

运行度:能力运行次数的量级,运行的次数量级越大,表明支持的业务体量越大,能力越有价值。

稳定性:能力代码的变动频率,变动量越低,说明能力越成熟,越稳定。变动量越高,说明能力不成熟,需要频繁变动才能满足业务。

易用性:是否有文档能够描述清楚能力的逻辑。业务系统能不能方便的对能力进行扩展。开发调试业务的过程中,能力是否能够准确表达业务语义。

其他待完善

通过以上多个方面综合评估,可以对能力是否够好,有一个初步了解。针对有问题的能力,可以具体分析原因,进一步进行优化。度量指标目前只考量复用度和扩展度,下一阶段会对后续标准进行分析。

当前Batrix企业能力库已有标准一级业务域共2个,二级业务域4个,非标准业务域4个。共有标准能力520个,其中:

▪ 综合评分超过200的能力2个。

▪ 综合评分超过100的能力16个。

▪ 业务身份覆盖度超15的能力有29个。

▪ 调用度超50的能力有11个。

▪ 调用度超10的能力有110个。

▪ 扩展度超50的能力有4个。

▪ 扩展度超10的能力有29个。

最佳实践

什么能力是好能力,很难给出准确的标准,以可度量的综合评分维度举例最佳实践:

1、订单域-纯配基本信息校验:

标准能力名称:基本信息校验

负责团队:交易平台组-订单中心

综合评分:235.3

业务身份覆盖度:19

调用度:126

扩展度:55

运行度:5

说明:纯配基本信息校验能力负责交易订单纯配业务的基本校验逻辑,从设计角度触发,所有条线业务都需要进行最基本的数据校验处理,只是或多或少、复杂度的问题。能力边界清晰,职责明确,代码结构简洁。个性化逻辑可以通过扩展点处理,基于不同诉求,共有55个不同的扩展点实现,最终满足业务逻辑的全量支持。

2、产品域-产品主增关系能力点:

标准能力名称:产品主增关系能力点

负责团队:交易平台组-产品中心

综合评分:138

业务身份覆盖度:32

调用度:36

扩展度:0

运行度:4

说明:产品主增关系能力负责产品域主产品和增值产品相关逻辑计算,为产品供给校验接口必选能力,所有业务逻辑必须执行。统一了各个业务线的产品主增模型结构,是产品域核心能力。能力边界清晰,职责明确。代码结构简洁,当前不支持扩展逻辑,如果后续如果有扩展需求,可以直接将代码重构成Abiliey和Extension的组合,支持个性化扩展逻辑使用。

结语

能力建设是中长期的事情,相对来讲难以量化,作为技术中台架构升级战略项目的相关系统,我们的能力建设成果,希望通过业务的口碑,通过BP的使用及落地效果,对业务战略和规划的影响力,用技术驱动导致的业务线损益呈现来衡量。

本文通过交易域能力建设的实践,总结能力建设方法,为大家建设自己能力提供借鉴意义。

作者:京东物流 史建刚

来源:京东云开发者社区 自猿其说Tech 转载请注明来源文章来源地址https://www.toymoban.com/news/detail-747436.html

到了这里,关于Batrix企业能力库之物流交易域能力建设实践的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 移动云强化云网全域安全能力,为数字中国建设保驾护航

    随着越来越多的企业上云用云、IT架构的持续更新,软件供应链风险提升、安全边界模糊、云上资产难以得到切实保障等诸多问题显露。相关报告显示,公有云在面临攻击时,攻击来源可能多种多样,其中96.1%的云上攻击主要来源于外网,3.9%的攻击来自内网横移、跨网段攻击

    2024年02月03日
    浏览(41)
  • 信息安全-应用安全-软件成分安全分析(SCA)能力的建设与演进

    SCA 概念出现其实很久了。简单来说,就是针对现有的软件系统生成粒度非常细的 SBOM(Software Bill of Materials 软件物料单)清单,然后通过⻛险数据去匹配有没有存在⻛险组件被引用。目前,市面上比较出色的商业产品包括 Synopsys 的 Blackduck 、Snyk 的 SCA 、HP 的 Fortify SCA 等,开

    2024年02月16日
    浏览(45)
  • 云科通明湖:金融业务可持续性能力建设,少不了这块“拼图”!

    随着银行行业数字化转型的不断深入,数据中心支撑业务可持续性的需求不断凸显,为了在业务发展和创新中取得行业领先,需要金融业务具备很强的适应性,以保证终端客户不断提升的使用体验,因此,金融企业的双活数据中心建设也越来越重要。 但是双活数据中心建设是

    2024年02月16日
    浏览(53)
  • 金融级低代码的三种应用场景和六个特色能力建设

    低代码平台在企业数字化转型中发挥着重要的作用,助力降低成本、提升效率。尤其对于金融行业而言,其规模庞大、复杂多变,各级分行、业务线以及科技部门都有使用低代码平台来增强效能的需求。 对于领导层 而言,要实现数字化转型在一线分支机构的全面推进,不能

    2024年01月18日
    浏览(38)
  • 【基础建设】浅谈企业网络安全运营体系建设

    引言 在网络安全环境复杂又严峻的当前,国内各大企业已开始组建自己的网络安全团队,加强企业自身安全能力建设,朝着网络安全运营一体化迈进。但企业安全运营也已逐步从被动式转变为主动式,成为将人、管理与技术结合,全面覆盖网络安全监测、预警、防护、检测、

    2024年02月09日
    浏览(48)
  • java+jsp企业物流货运快递管理系统servlet

    功能需求具体描述: (1)用户功能模块包括用户登录注册,用户信息的修改,用户发布货物信息,给客服人员留言,对运输公司进行评价。 (2)企业功能模块包括企业注册登录,企业信息的修改,受理用户发布的货物,订单查看,评价查看,给客服人员留言。 (3)评价功能模块主

    2024年02月02日
    浏览(44)
  • spingboot+jsp仓储型物流企业车辆运输管理系统

    随着时代的进步,物流车辆运输行业也逐渐变得庞大起来。当然,物流车辆运输公司要想做大做强,就有必要有自己完整的一套物流车辆运输管理系统。这必将为物流管理公司提供规范化的管理模式,在各个部门之间有效的协调、合作过程中必将为物流车辆公司提供大量的客户生源

    2024年02月06日
    浏览(51)
  • ChatGPT会改变物流领域吗?可以为企业赋能吗?

    近期大火的ChatGPT,其实是一个对话的AI模型,就像一个聊天机器人。目前一些大公司客户服务网站上的自动聊天机器人虽然智能水平不如ChatGPT,但它们属于同一类技术。 从实战来看,ChatGPT堪称“小灵通”,从答疑解惑、编写代码、到创作诗歌和钢琴曲什么都会,就算不会也

    2023年04月26日
    浏览(38)
  • 集团企业网站建设开发

    为集团提供一个互联网上的形象宣传和信息发布、收集的重要平台 利用最新的互联网动态数据库交互能力,建立一套在互联网上具有领先地位的集团网站,将集团和子公司网站做到有机的统一。集团网站不但要把集团的企业、产品等相关信息展示给我们的客户、合作伙伴和业

    2024年02月14日
    浏览(86)
  • 企业网站建设方案书

    一、网站建设目标 1.1背景分析 现在网络的发展已呈现商业化、全民化、全球化的趋势。目前,几乎世界上所有的公司都在利用网络传递商业信息,进行商业活动,从宣传企业、发布广告、招聘雇员、传递商业文件乃至拓展市场、网上销售等,无所不能。如今网络已成为企业

    2024年02月06日
    浏览(63)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包