Batrix Enterprise Capability Library’s Logistics Transaction Domain Capability Building Practice | JD Logistics Technology Team

Introduction

Batrix enterprise capability library is the basic foundation for JD Logistics' strategic project - technology middle platform architecture upgrade project. Committed to establishing an enterprise-level business reuse capability platform, relying on the capability reuse business framework Batrix, through the definition and reuse of general capabilities/extended capabilities, it flexibly supports the rapid capability orchestration and assembly of differentiated business scenarios, thereby assisting in a technology-driven manner Overall business delivery throughput.

In the context of the four-layer architecture (access layer, transaction layer, fulfillment layer, and execution layer), the trading platform group undertakes the business logic of the transaction layer and is responsible for the development of reusable capabilities in transaction scenarios. At present, the transaction order domain has accumulated 13 capabilities with a comprehensive score exceeding 100, and the transaction product domain has accumulated 5 capabilities with a comprehensive score exceeding 100.

This article focuses on introducing how to use the Batrix framework to accumulate capabilities in the trading domain.

Preparation

The construction of capability domains requires the participation of multiple parties, including business, products, R&D, testing, etc. What capabilities are required for a capability domain, what is the logic within the capabilities, and what are the business boundaries between different capabilities, all need to be agreed upon by business architects and technical architects, and some advanced top-level design is required.

In the early stage, the business architect can be appointed by the product side, but in the later stage, the business architect must be involved, which needs to be jointly formulated based on the existing business accumulation and future industry development plans.

The technical architect is appointed by R&D and is responsible for designing the application architecture for capability development and ensuring the rationality of capability coding. It is necessary to control the scalability of capabilities, lay a solid foundation for subsequent reuse, and agree on the domain context with the business architect. the design of.

Test students are mainly responsible for the quality of capabilities. During the capability building process, code adjustments, extraction and precipitation of expansion logic are inevitable. These require regression verification of existing business logic to ensure online stability during the capability adjustment process.

Capacity accumulation is a complex and long-term matter. It is impossible to achieve results in a short time and requires managers to carry out anticipatory management and support.

The two most critical issues in capacity building are:

1. Which logic is the basic logic of capabilities and all businesses need to execute it. How to adapt the logic if there are personalized requirements.

2. How to reuse existing capabilities.

construction method

The Batrix framework provides construction solutions for standard capabilities and extended capabilities:

▪Standard capabilities : Responsible for the basic logic of capabilities, all businesses that use capabilities must be executed. Design expansion slots in advance to lay the foundation for subsequent capability reuse and expansion.

▪Expansion capabilities : Specific implementation of expansion slots reserved in standard capabilities to solve the adaptation of personalized logic.

The Batrix framework provides engineering implementation solutions for capacity building:

BDomainFlowNode : FlowNode is an explicit capability node that can provide services independently. The Batrix framework process orchestration uses functional orchestration based on FlowNode. Simple logic can be developed and implemented directly in FlowNode. For complex logic, FlowNode calls DomainAbility or DomainService for combined reference to implement capability business logic.

BIDomainService : DomainService develops combined services of multiple capabilities. Try not to include complex business processing, only simple logical code parts.

BBaseDomainAbility : DomainAbility develops independent function points, the functions are common basic functions, and each ability supports one type of expansion slot. The implementation of expansion slots allows the development of code parts for personalized needs. If there is no personalized code part, the extension point part of the code does not need to be implemented. Multiple capabilities are combined by FlowNode or BIDomainService to provide services.

BIDomainAbilityExtension : Extension implements SPI definition and develops personalized custom code parts.

Building standards capabilities requires a clear and correct understanding of the business in the domain and long-term planning.

Different business domains have different business logics, so the ideas for building capabilities will be different. The granularity of business capabilities and the boundaries between different capabilities are difficult to fix through one design, and require continuous optimization based on specific scenarios. In the process of building capabilities, it can be roughly divided into four stages according to the level of understanding:

▪Initial stage : We only know what functions the capabilities are responsible for, but we don’t know which ones should belong to the universal standard logic part and which ones need to be expanded later. Expansion slots are reserved for expansion logic. Regarding the expansion slot, it is not clear what the input parameters and output parameters of the expansion logic are. (Most systems that have not built capability domains belong to this stage.)

▪Proposal suggestion : All codes are implemented by Extension, use a FlowNode to undertake external calling logic, and finally the Extension implements the business. If there is personalization logic, it is implemented using another Extension. In the later stage, through the abstraction of multiple Extensions, we can analyze which ones are public logic and which ones are personalized logic, and then evolve the capabilities to the next step.

▪Example : _

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);
        }
    }
}





▪Early stage : Be clear about the business logic that the capability is responsible for, and clearly understand what needs to be expanded based on the business logic. However, in order to adapt to subsequent business, it is not clear whether it can be expanded. (The trade order field is in its current state and is evolving to the next stage.)

▪Proposal suggestion : Reasonably split the business logic. In a rougher way, you can split the logic within the capability into front, middle and back logic, and support personalized logic expansion implementation calls before and after the core logic of the capability. Incoming parameter transfer Provided for use in full domain context. The advantage is that it is flexible to expand and facilitates personalized logic development. The disadvantage is that it is difficult to manage and SPI loses the interface isolation principle.

▪Example : _

@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);
            }
        }
    }
}


▪Mid -term : Be clear about the business logic that the capability is responsible for, clearly understand what can be expanded and what cannot be expanded for the business logic, and have a reasonable plan for the parameter fields used in the expansion logic. (The trading product domain is at the current stage.)

▪Proposal suggestions : Reasonably split the business logic in a refined way, have a detailed design for the logic within the capability, plan which logic can be extended, which logic must be executed, and the outgoing and incoming parameters of each Extension method. All have designs. Extensions can be distinguished between local extension point calls and remote extension point calls based on specific logic and usage scenarios.

▪Example : _

/**
 * 产品日历信息能力点
 */
@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;
    }
}

▪Later stage : The logic and robustness of the ability itself are at a high level, but there are too many usage scenarios for the ability, and the differences cannot be abstracted and unified into one ability.

▪Proposal suggestion : Don’t force all requirements to be integrated into one capability. The cost and complexity are too high. You can split a business capability and provide 2-3 different modes of technical capabilities. Business users can further choose to expand and integrate them into the business according to their needs. Use 2-3 different capability models to cover 85%-95% of business scenarios. If there are still very few businesses that cannot be used, develop personalized capability support for the business.

How to reuse

The use of capabilities is divided into several levels, and the relevant priorities are as follows:

▪ After the demand is raised, it is analyzed whether the existing standard capabilities meet the demand. The same capability can have 2-3 modes. If one of the modes meets the needs, it can be used.

▪ If the existing standard capabilities are not met, evaluate whether the use of extended capabilities meets the requirements, and select the one closest to the requirements from different modes of standard capabilities for expansion.

▪ Extension capabilities are essentially a combination of standard capabilities and extension points. Standard capabilities are responsible for the execution of general logic, and extension points are responsible for the execution of personalized logic. Based on the convenience of stability and scalability, the business system can also use remote extension points to run the expansion logic on independent services.

▪ If the expansion capability still does not meet the needs, you can create new capabilities.

▪ If it is found after analysis that a capability is missing, create a new standard capability for subsequent business reuse.

▪ If after analysis it is found that the requirement is a small scenario, you can create a new custom capability for this requirement and use it only for this business scenario.

After determining which capabilities to use, you can use the console provided by Batrix - Shenxing Platform to orchestrate the process of capabilities, and ultimately form logic that meets business needs through the orchestration and combination of capabilities. as follows:

This process is the small and medium-sized ISV-sales out-order process, which is undertaken by the transaction order domain. The process is arranged to meet the order-receiving logic of the small and medium-sized ISV's sales outbound business. By using the synchronous and asynchronous dual-engine mode of the Batrix framework, you can control the synchronous execution and asynchronous execution of capabilities to meet the different demands of the business for performance and asynchronous fulfillment scheduling.

As business requirements evolve, capabilities will also be updated and replaced. If the capabilities do not meet the new business usage, the capabilities need to be developed, or new capabilities need to be created and incorporated to replace the original logic. If the business has no users and no calls, you can delete the process and then delete the capabilities to ensure the long-term integrity and stability of the system engineering and reduce corrosion.

Enterprise capability sharing

The reuse of current capabilities is limited to the business domain system to which the capability belongs. The throughput speed of receiving demands depends on the support speed of the capable Owner. However, as the complexity of the business increases, the demands of each BP department are concentrated on the middle office department. The resources of the middle platform are limited. BP implements expansion points based on reusable middle platform capabilities, which can offset part of the dependence on the middle platform resources. However, the demand for capabilities can only be solved through engineering co-construction. In the long term, there are many hidden dangers, functions cannot be decoupled, and system stability cannot be guaranteed. If you rely entirely on the development capabilities of the middle platform, the resources of the middle platform may become a delivery bottleneck.

Therefore, the Batrix enterprise capability library supports the capability sharing model, mainly to solve the following problems:

▪ Further solve the resource bottleneck problem through the capability sharing model.

▪ Accelerate business delivery efficiency through process orchestration and on-demand loading.

Based on the capability sharing model, the front-end, middle-end collaboration methods have been adjusted:

▪ Middle-end R&D: Develop reusable and stable capabilities, design expansion point slots, and release capabilities.

▪ BP R&D: Based on business needs, select capabilities to orchestrate business processes, and implement them through extension points to complete the parts of the middle-end capabilities that do not meet the needs. If the capabilities of the middle office do not meet business needs, BP can build its own capabilities for process orchestration and operation.

▪ Business applications: Business owners run different capabilities in their own systems through process orchestration and are responsible for the docking and maintenance of external businesses. The middle office provides the stability and reusability of capabilities, maintains internal stability, and ultimately improves business delivery efficiency.

The adjusted trading order system structure is as follows:

The application architecture is designed to uniformly handle external business requests through the middle-end traffic middleware, and uniformly handle internal data processing through the middle-end data middleware. The middle-end business application container handles part of the adaptation logic, and calls shared capabilities through the Batrix framework.

▪ Middle-office traffic middleware: receives requests from business traffic channels and routes the requests to different business systems based on the distribution attributes.

▪ Business application container:

▪ Adapt to external interface standards through the REMOTE layer

▪ Process the transformation to the domain service model through the LOCAL layer

▪ Run business capabilities through the BATRIX layer and arrange operations according to processes to meet business requirements.

▪ Middle-end data middleware: handles data operation requests.

▪ Auxiliary system: Assist system operation through the configuration center, monitoring center, and security center to accelerate delivery efficiency and improve system stability.

Capability sharing is not applicable to all businesses and all capabilities. Its usage threshold is relatively high and it imposes certain requirements on both capability builders and business users.

▪ Capacity builder requirements:

▪ How to ensure that the capabilities provided are stable and efficient.

▪ How to maintain necessary basic development principles.

▪ How to manage domain context when iterating capability versions and how to be compatible with existing business.

▪ Capabilities include whether downstream interfaces, middleware, and data storage services are shared or isolated.

▪ Business user requirements:

▪ How to clearly understand the internal logic of the capabilities used so that you can operate and maintain your own business.

▪ How to quickly locate the problem and fix it when business is abnormal.

▪ The business logic development and debugging experience is poor.

Capability governance

After capability development is completed, if it is not managed, capability explosion will easily occur. Everyone is trying to build new capabilities, but without optimization and precipitation, the results will still not be achieved.

According to different stages, the life cycle of capabilities is as follows:

▪ After the requirements are accepted, new capabilities can be created for logical development.

▪ After the capability is created, it needs to be continuously refined and optimized, and it can be released to the outside world only after stable services are available.

▪ After capability development is completed, processes can be orchestrated and used for business operations.

▪ With the passage of time and version iterations, there will be a lot of redundant code in the capability itself, which requires periodic precipitation and optimization of the capability itself to ensure the stability of the capability and accept more business.

▪ If the capability cannot be optimized in the long term, the capability itself needs to be abandoned and new capabilities need to be generated to provide services.

▪ When the old capability is offline, it needs to be deleted in the corresponding usage process, the business process references that depend on the capability are deleted, and finally the relevant code is deleted.

How to judge the quality of capability development, what needs to be optimized, what needs to be cleared, and whether the logic can be universal?

Currently, Batrix capability governance provides several dimensions of measurement:

▪Invocation degree : the number of times the standard capability is used by the process (the extended capability created by the standard capability is also counted as the use of the standard capability). The higher the degree of reuse, the more businesses the capability supports.

▪Business identity coverage : the number of business identities using the capability.

▪Extensibility : The number of extension capabilities created by standard capabilities. This indicator needs to be considered together with the degree of reuse . The higher the degree of reuse, the lower the degree of expansion, indicating that the capability is more versatile and can support multiple business lines without expansion. On the contrary, the lower the degree of reuse, the higher the degree of scalability, which indicates that the general logic of the capability is not enough to support the business requirements, and a large amount of expansion is required to satisfy it.

▪Operation degree : The magnitude of the number of times a capability is run. The greater the number of times it is run, the larger the business volume it supports and the more valuable the capability is.

▪Stability : The frequency of changes in the capability code. The lower the change, the more mature and stable the capability is. The higher the amount of changes, it indicates that the capabilities are immature and require frequent changes to meet the business needs.

▪Ease of use : Is there documentation that clearly describes the logic of the capability. Whether the business system can easily expand its capabilities. In the process of developing and debugging business, whether the capability can accurately express business semantics.

▪Others to be improved

Through the comprehensive evaluation of the above multiple aspects, you can have a preliminary understanding of whether your ability is good enough. For problematic capabilities, the reasons can be analyzed in detail and further optimized. The measurement indicators currently only consider reusability and scalability, and subsequent standards will be analyzed in the next stage.

Currently, Batrix's enterprise capability library has a total of 2 standard first-level business domains, 4 second-level business domains, and 4 non-standard business domains. There are 520 standard capabilities in total, including:

▪ 2 abilities with a comprehensive score exceeding 200.

▪ There are 16 abilities with a comprehensive score exceeding 100.

▪ There are 29 capabilities with business identity coverage exceeding 15.

▪ There are 11 abilities with a calling degree exceeding 50.

▪ There are 110 abilities with a call degree exceeding 10.

▪ There are 4 capabilities with scalability exceeding 50.

▪ There are 29 capabilities with scalability exceeding 10.

Best Practices

It is difficult to give accurate standards for what abilities are good. Here are some examples of best practices using measurable comprehensive scoring dimensions:

1. Order field-pure configuration basic information verification:

▪Standard capability name : Basic information verification

▪Responsible team : Trading Platform Group-Order Center

▪Overall score : 235.3

▪Business identity coverage : 19

▪Call degree : 126

▪Expansion : 55

▪Running degree : 5

▪Explanation : The basic information verification capability of pure matching is responsible for the basic verification logic of the pure matching business of transaction orders. From a design perspective, all line businesses need to perform the most basic data verification processing, but the complexity is more or less . The problem. The boundaries of capabilities are clear, responsibilities are clear, and the code structure is concise. Personalized logic can be processed through extension points. Based on different demands, a total of 55 different extension points are implemented to ultimately meet the full support of business logic.

2. Product Domain-Product Main Increase Relationship Capability Points:

▪Standard capability name : product main increase relationship capability point

▪Responsible team : Trading Platform Group-Product Center

▪Overall score : 138

▪Business identity coverage : 32

▪Call degree : 36

▪Expansion : 0

▪Running degree : 4

▪Description : The product main-added relationship capability is responsible for logical calculations related to main products and value-added products in the product domain. It provides mandatory capabilities for verification interfaces for products, and all business logic must be executed. It unifies the main product model structure of each business line and is the core competency of the product domain. Competency boundaries are clear and responsibilities are clear. The code structure is simple and does not currently support extension logic. If there is a need for extension in the future, the code can be directly restructured into a combination of Abiliey and Extension to support the use of personalized extension logic.

Conclusion

Capacity building is a mid- to long-term matter and is relatively difficult to quantify. As a system related to the strategic project of upgrading the technology mid-stage architecture, our capacity building results hope to have a positive impact on business strategy and implementation through business reputation and the use and implementation of BP. The impact of planning is measured by the profit and loss of business lines driven by technology.

This article summarizes the methods of capacity building through the practice of capacity building in the trading domain, and provides reference for everyone to build their own capabilities.

Author: JD Logistics Shi Jiangang

Source: JD Cloud Developer Community Ziyuanqishuo Tech Please indicate the source when reprinting

Spring Boot 3.2.0 is officially released. The most serious service failure in Didi’s history. Is the culprit the underlying software or “reducing costs and increasing laughter”? Programmers tampered with ETC balances and embezzled more than 2.6 million yuan a year. Google employees criticized the big boss after leaving their jobs. They were deeply involved in the Flutter project and formulated HTML-related standards. Microsoft Copilot Web AI will be officially launched on December 1, supporting Chinese PHP 8.3 GA Firefox in 2023 Rust Web framework Rocket has become faster and released v0.5: supports asynchronous, SSE, WebSockets, etc. Loongson 3A6000 desktop processor is officially released, the light of domestic production! Broadcom announces successful acquisition of VMware
{{o.name}}
{{m.name}}

Guess you like

Origin my.oschina.net/u/4090830/blog/10313973