谈及中台,大都雾里看花,抱有一份敬畏之心,恐误导众人。但愿通过自己的思考与一同思考实践的朋友们一些启发,让中台建设得到它应有的收益,总结出更多的成功经验。
最近接触到一些公司说在做中台
,交流之后大都是应该使用什么样的技术,如何解决数据一致性问题等。其中公司发展时间有长有短,有十几二十年的传统企业,也有三四个月才起步的创业团队。交流下来心中不免有些担忧,不太清楚所谓中台是追求一种技术实现还是一个流行噱头。
经过较长时间的思考、学习和实践,我发现了解得越多越不敢讲自己做的称之为中台
。它是一种企业级业务构架设计方法论,如何做好还得从企业的愿景发出分析企业发展目标,合理利用资源对系统架构进行持续性的演进。
每个企业的愿景和目标都不一样,对信息化诉求不一样,所构建出的中台系统
自然也不一样,但是对企业经营有有效的提升是显尔易见的,所以设定好可量化的指标尤为重要。
本文通过一个简单的例子来讲述如何进行中台化落地,企业实际过程远比这复杂得多。这是一家新零售企业,通过数字化转型获得新的业务增长点。“数字钱包”是公司的一个重点产品,项目特点是和其它业务相对独立且前端功能多样化,经商量解决引入中台化的思想来规划这个项目。
架构设计就是为未来而设计的,首先要清楚这个产品的愿景是什么,做这个事是目的是什么,要达成什么目标等。
战略分析是至上而下的,经历公司的发展历史,了解公司现在的发展状况,清楚公司未来的发展方向。此过程需要公司领导及各业务负责人参与沟通,达成一致意见。
此产品主要用于公司各种类型资金交易的解决方案。目前包括:
* 记名:用于会员
* 不记名:用于赠送、福利卡
* 亲属,用于家庭共用卡
主要为线上线下结合,不同的终端不一样的使用场景
不同的业务模式和业务场景有不一样的业务功能,这里需要去切分和隔离
战术设计就是根据战略目标制定具体的作战步骤。
本项目所处在钱包功能急将上线解决业务功能闭环的阶段,需要快速出成果故在后续系统结构不做大的改变的情况下,考虑到线下操作都是由公司员工完成,实施风险相对可控,故先完成线下基础版本。
由于第一阶段功能较简单,架构关键点在于如何保持系统的灵活扩展性,故前期的架构设计是重点,而后的功能实现就能顺理成章了。
可量化指标是实现新老功能的迁移,实现多端操作的整合。
根据战略愿景的诉求,系统设计上要求保持灵活性,易于功能扩展和业务形态快速复制性。
我们采用DDD(领域驱动设计
)思想来分析业务:
领域实体
,形成聚合
命令模式
驱动业务操作,以交易流水实体为聚合根
驱动钱包账户实体的变化领域事件
来联动系统内与系统外相关功能此阶段可以以事件风暴
的形式,与领域专家一起使用通用语言
来展开讨论,以达到业务、技术认识一致性
系统需要保持满足的复用能力,可以方便快速的迭代出新的业务功能、业务规则和业务场景。故需要识别出这其中的业务共性和可变性,通过多种程序设计模式保持系统的灵活性
这个项目的业务共性就是所有的业务操作都是可以以交易流水为驱动,引发一个业务变化
可变性就是不同的业务变化,如金额增加、金额减少、账户锁定、密码变动等
可变的内容抽象为业务行为
和业务规则
,不可变的就是交易处理
、交易完成
和交易事件发布
这里主要指系统间的扩展性。需要定义好相互通讯的协议和标准,通过定义好的流程将数字钱包系统与其它系统融合成一个整体。
上图为系统架构的核心逻辑,主要有3大部分组成
所有交易操作的执行器
public interface WalletService {
void done();
}
DefaultService 默认的抽象类,主要实现
CheckPolicy
和Behavior
接口的主线流程调用
现实类调用:
public class ConsumeService extends DefaultService {
public ConsumeService(Wallet wallet, BigDecimal tradeAmount) {
super(TradeRecord.builder().wallet(wallet).tradeAmount(tradeAmount).build());
}
}
public interface Behavior {
void doAction();
InOutFlag getInOutFlag();
}
public class CreditBehavior extends DefaultBehavior {
private final BigDecimal tradeAmount;
public CreditBehavior(Wallet wallet, BigDecimal tradeAmount) {
super(wallet);
this.tradeAmount = tradeAmount;
}
@Override
public void doAction() {
super.doAction();
wallet.setBalance(wallet.getBalance().add(tradeAmount));
}
@Override
public InOutFlag getInOutFlag() {
return InOutFlag.IN;
}
}
根据设计方案将所有钱包账户操作都定义为行为,此处实现的是具体的账户操作逻辑,实现类继承至抽象类进行简单的封闭。
getInOutFlag()
是对行为产生的资金进出结果的配置
public interface CheckPolicy {
void check();
}
public class NoOverdraftAllowed implements CheckPolicy {
private final Wallet wallet;
private final BigDecimal tradeAmount;
public NoOverdraftAllowed(Wallet wallet, BigDecimal tradeAmount) {
this.wallet = wallet;
this.tradeAmount = tradeAmount;
}
@Override
public void check() {
if (wallet.getBalance().compareTo(tradeAmount) < 0){
throw new BizException("余额不足");
}
}
}
实现类用于判断相关操作是否存在余额不足(透支)情况,如果有则中止执行
public abstract class DefaultService implements WalletService {
protected abstract static class TradeConfig {
public abstract TradeType tradeType();
public abstract Behavior behavior();
public abstract List<CheckPolicy> checkPolicies();
}
protected abstract TradeConfig tradeConfig();
@Override
public void done() {
check();
tradeConfig().behavior().doAction();
tradeRecord.setBalance(tradeRecord.getWallet().getBalance());
tradeRecord.setTradeStatus(TradeStatus.SUCCEED);
}
}
public class LockService extends DefaultService {
public LockService(Wallet wallet) {
super(TradeRecord.builder().wallet(wallet).build());
}
@Override
protected TradeConfig tradeConfig() {
return new TradeConfig() {
@Override
public TradeType tradeType() {
return TradeType.LOCK;
}
@Override
public Behavior behavior() {
return new LockBehavior(getWallet());
}
@Override
public List<CheckPolicy> checkPolicies() {
return CheckPolicyBuilder.builder()
.add(new NoAvailableStatusAllowed(getWallet()))
.build();
}
};
}
}
在主交易流程中,将共有的流程放在done()
中执行,将可变的部分抽象成配置模板供现实类现实
模板模式的优点:
- 扩展性好,对不变的代码进行封装,对可变的进行扩展; - 可维护性好,因为将公共代码进行了提取,使用的时候直接调用即可; - 现实类在有限的空间扩展,不影响主流程的实现;
public class RechargeRollbackService extends DefaultService {
private final TradeRecord sourceTrade;
public RechargeRollbackService(TradeRecord sourceTrade) {
// ...
}
@Override
protected DefaultService.TradeConfig tradeConfig() {
return new TradeConfig() {
@Override
public TradeType tradeType() {
return TradeType.RECHARGE_ROLLBACK;
}
@Override
public Behavior behavior() {
return new DebitBehavior(getWallet(), getTradeAmount());
}
@Override
public List<CheckPolicy> checkPolicies() {
return CheckPolicyBuilder.builder()
.add(new NoRechargeTypeAllowed(sourceTrade))
.add(new NoTimeoutAllowed(sourceTrade))
.add(new NoOverdraftAllowed(getWallet(), getTradeAmount()))
.add(new NoAvailableStatusAllowed(getWallet()))
.build();
}
};
}
}
在交易行为和交易规则的设计中使用了策略模式,可根据不同业务操作设计不同的策略
策略模式的优点
- 扩展性好,可以在不修改对象结构的情况下,为新的算法进行添加新的类进行实现; - 灵活性好,可以对算法进行自由切换; - 结构清晰,代码可读性高;
public abstract class DefaultTowPCService extends DefaultService implements TwoPCWalletService {
public DefaultTowPCService(TradeRecord tradeRecord) {
super(tradeRecord);
}
@Override
public void process() {
check();
getTradeRecord().setTradeStatus(TradeStatus.PROCESSING);
}
}
public class RechargeService extends DefaultTowPCService {
public RechargeService(Wallet wallet, BigDecimal tradeAmount){
super(TradeRecord.builder().wallet(wallet).tradeAmount(tradeAmount).build());
}
}
TwoPCWalletService,*DefaultTowPCService* 用于在原有接口基础上扩展的二阶段提交功能,此处为了保持DefaultService功能的单一性,并没有在原有类上进行功能扩展,而是使用组合模式
进行功能扩展
此现实类就现实了WalletService
和TwoPCWalletService
两个接口
策略模式的优点
- 扩展性好,可以在不修改对象结构的情况下,为新的功能增加新的现实; - 变动性小,不需要现实的类,不修改代码
这一阶段,业务模式单一,业务功能单一,业务量较少,开发人员也较少,将所有模块打包运行在一个jvm中
这一阶段,随着业务模式和业务量的增加,模式间的业务功能也不相同或有互斥性,单一结构已不能满足,故将基础模块封闭为SDK
,每种业务模式单独一套系统独立维护
随着业务模式、业务量、业务功能需求持续增加。由于各业务线独立运营,导致SDK版本不一致,增加了维护成本。多团队维护架构导致功能重复且实现过程参差不齐,带来一定维护成本且系统间无法实现复用,同时每条业务线独立运营也带来用人成本的增加。
这一阶段的目标是将大部分的共用功能下沉形成标准化逻辑,统一维护版本,减少人力成本,故架构演进采用中台化的思想
本文通过一个较简单的例子讲述中台架构演进的过程,实际场景远比此复杂。但最重要的不是最终的系统架构,而是对系统演进的思考和实施过程,因为中台的形态也是随时间不断变化的。
文中代码由于篇幅原因有一定省略并不是完整逻辑,如有兴趣请Fork源代码
领取专属 10元无门槛券
私享最新 技术干货