Trantor 应用
为了更好的实现资源的可扩展机制,Trantor将应用分为『标准产品』、『应用』、『交付物』三大类。从实际意义出发可将这三类分别解释为Gaia/Gaia App/Custom App 。
Gaia 是最基本的 App,不能声明任何依赖, 所有 Gaia App 都会依赖 Gaia, Gaia App 可以选择性的依赖其他 Gaia App。 依赖应用时,可以声明版本范围, 类似 Node 的 版本声明。
ISV App 可以依赖 Gaia App 和当前 ISV 的 App, 即 ISV App 不能依赖除 Gaia 之外其他 ISV 的 App, 其他 ISV App 和 Gaia App 基本一致.
定制层可以理解为一个特殊的应用,他一定会拥有一个产品依赖(比如 Gaia),也同时可以拥有多个应用的依赖。当在定制应用内二开一个依赖应用内的资源时,需要手动申明覆盖了哪个应用的资源。
如下图:

标准产品
标准产品(Gaia)是最基本的 App, 不能声明任何依赖,所有 Gaia App 都会依赖 Gaia, Gaia App 可以选择性的依赖其他 Gaia App。依赖应用时, 可以声明版本范围,类似 Node 的 版本声明。
应用
应用可以声明自己的研发资源, 即模型/视图/逻辑流 等, 同时应用可以扩展父应用的资源,从行为上来说有点像子类 override 父类的方法。
每个应用都可以声明菜单,菜单可以理解为应用入口, 从菜单进入页面后会带相应的应用 Key, 在页面跳转和后端交互过程中, 都会带上该应用 Key。
因此, 在界面获取元信息以及 Function/Flow 的调用的过程中, 可以根据该应用 Key 来决定, 要使用的资源实现是什么。
以上述应用关系为例, 当用户从 O2O 的菜单进入应用时,请求路径会携带 O2O 的应用 Key,同时在获取菜单对应视图时,MetaStore 会根据视图 Key 在 O2O 的资源内先行查找, 有则直接使用, 没有则继续向上找到 B2C 应用的资源, 一致找到 Gaia 如果还没有找到。 则会认为该资源不存在。
整个过程可以理解为 Java 的类继承模型, 当调用一个方法时, 如果子类重写过改方法, 则会使用子类的, 子类不存在就使用父类的。
交付物
交付物是一个特殊的应用。
一个Custom App必须依赖一个标准产品(比如Gaia),并可选的依赖数个应用。在定制应用内覆盖依赖应用资源时,不管入口是哪个应用,都优先以定制的实现为主。
实现上,当存在定制应用时,都需要优先查找定制资源,然后才是原入口应用内的资源。
除了应用层面的扩展之外,Trantor 还会提供项目层面的定制机制, 即在最上层以定制化的方式,对已安装的 App 功能进行调整。
因为同一个资源可能因为 App 的扩展,已经产生了不同 App下的新的资源,所以从定制的层面,也需要声明,我定制的是哪一个 App 的资源。
例如上例图中 B2C 和 B2B 的下单,可能都扩展了 Gaia 的 CreateOrder,当从 B2C 的入口进入时,调用的是 CreateB2COrder,反之从 B2B 入口则是 CreateB2BOrder,如果从 Gaia 的入口访问的仍然是最基础的 CreateOrder。
如果交付层面需要扩展下单, 也一定要控制其影响范围,主动声明, 扩展的是 B2B 还是 B2C,亦或者 Gaia 的 CreateOrder,这样才能确保应用之间仍然是相互不影响,独立可运行。
Trantor 的资源主要分为 Java 类和文件类型,文件类型又分为 Json 和 XML 两种类型,所以会有不同的声明方式。
文件类型资源
所有结构化资源,比如 Json 和 XML 格式的, 统一增加 overrideApp, 以声明该资源在定制层覆盖的是那个应用。
例如视图 XML:
<?xml version="1.0" encoding="UTF-8"?><View overrideApp="oms" title="assign carrier manually" type="Form" forModel="oms_FulfilmentOrderBO" version="2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="https://trantor-docs-dev.app.terminus.io/static/v0.16.x/schema/base.xsd"> <Form key="assignCarrierManuallyForm" model="oms_FulfilmentOrderBO" dataFlow="app_oms_QueryCarrierByFulfilmentOrderIdFlow" dataParams="#{{id:env.id}}"> <Fields> <Field name="id" initValue="#{env.id}" submit="#{true}" show="#{false}"/> <Field name="carrierEntityRole" label="carrier"> <RenderType> <NonRelatedModelSelect type="ToOne" model="md_EntityRoleBO" dataCondition="roleStatus = 'ENABLED' and roleType = 'CARRIER'" modalTitle="carrier" fieldNames="{value: 'id', label: 'name'}"/> </RenderType> </Field> </Fields> <Actions label="operation"> <Action type="Cancel" label="cancel" action="GoBack" layout="Footer"/> <Action type="Submit" label="submit" logicFlow="app_oms_AssignCarrierManuallyFlow" layout="Footer" after="GoBack"/> </Actions> </Form></View>Json 类型:
{ "schemaVersion": 2, "overrideApp": "B2C", "name": "从延时队列获取订单进行营销计算任务", "logicFlowKey": "app_trade_GiftOrderBatchFetchFlow", "config": { "triggerType": "Cron", "expression": "0 * * * * ?", "timeout": 3600 }}Java Class 类型资源
首先我们会增加一个 trantor-custom 的 Maven 模块, 以供定制项目依赖. 该模块内会增加一个注解 @OverrideApp(AppKey), 所有打了该注解的资源, 都会被认为对应 App 下的资源.
例如 FunctionImpl:
@FunctionImpl(name = "assemble open token to func")@OverrideApp("B2B")public class AssembleOpenTokenTOFuncImpl implements AssembleOpenTokenTOFunc { public OpenTokenTO execute(TradeOrderBO tradeOrderBO) { OpenTokenTO openToken = new OpenTokenTO(); QChannelShopBO qChannelShopBO = new QChannelShopBO(); qChannelShopBO.setId(new QLongId((Long)tradeOrderBO.getShop().getId())); ChannelShopBO channelShopBO = (ChannelShopBO)DS.findOne(qChannelShopBO); if (StrUtil.isNotEmpty(channelShopBO.getAccessToken())) { openToken.setAccessToken(channelShopBO.getAccessToken()); } if (StrUtil.isNotEmpty(channelShopBO.getAppKey())) { openToken.setAppKey(channelShopBO.getAppKey()); } if (StrUtil.isNotEmpty(channelShopBO.getAppSecret())) { openToken.setSecret(channelShopBO.getAppSecret()); } if (StrUtil.isNotEmpty(channelShopBO.getGateWay())) { openToken.setGateway(channelShopBO.getGateWay()); } return openToken; }}