我想聊聊电信计费、Java EE、GWT 和 Salesforce.com 的交汇点——这个组合彻底属于 2013 年中期,应该配上一张时代 moodboard:扁平化设计刚起飞,大家在争论响应式 CSS 是不是个昙花一现的潮流。但问题是:我在这个技术栈上给真实的电信运营商处理真实的钱。而且我在那个环境里学到的相当一部分东西到今天还适用,因为底层问题是结构性的,不是技术性的。

BluLogix 构建了 Cloud Innovation Suite——面向电信运营商的云计费平台。我的职责是后端开发、前端监督、BPMN 工作流设计,以及最有启发性的一件事:用 Talend ETL 把 CIS 和 Salesforce.com 打通。最后那部分是我想在这篇文章里重点讲的,因为工程在那里才真正变得有意思。

电信计费实际上长什么样

在你理解为什么 Salesforce 集成对电信计费系统来说是个难题之前,你需要一个关于电信计费实际上是什么的粗略模型。

电信计费不是一张发票。它是一个系统,持续地对事件评价——通话、数据会话、短信、API 调用、云资源消耗——对应产品目录,应用定价规则和折扣,管理订阅状态,处理周期内变更(套餐升级、按比例计费、附加服务),在计费周期运行周期性收费,生成符合监管的发票,处理付款,处理纠纷,产出营收保证报告。一个企业客户的单个计费周期可能涉及数千万个使用事件、数百个资费套餐的产品目录、多种货币和跨多国的税收管辖区。

这就是为什么电信计费系统作为一个专业化软件类别而存在。这个事情你没法在电子表格里做,没法在 QuickBooks 里做。它的数据模型和通用财务软件根本不同。

GWT——Google Web Toolkit——是 2013 年在 Java 里构建富 Web 界面的方式,如果你想让 UI 代码类型安全、可测试,而且是由不要求精通 JavaScript 的 Java 工程师来写的话。你写 Java,GWT 把它编译成 JavaScript,你的 IDE 类型检查器在整个栈上都有效。2013 年,TypeScript 还不存在,现代前端框架还没成熟,这是个真正合理的权衡。输出的 JavaScript 很啰嗦,调试周期很慢,但对于以 Java 为中心的团队来说,开发体验比替代方案——让你的 Java 工程师带着和对 PHP 一样热情的态度写原生 JavaScript——要好。GWT 现在死了。没关系。2013 年,在一个搭建企业软件的 Java EE 团队里,它是正确的工具。

为什么 Salesforce 恨你的计费系统

Salesforce 的数据模型建立在 CRM 抽象之上:账户、联系人、商机和产品。这套模型对销售流程非常好用。对电信计费来说是一场噩梦。

举个具体的例子。在 Salesforce 里,Account 是一家公司。Opportunity 是一个潜在的销售。Product 是你卖的东西。Opportunity 关单后变成 Order,Product 成为 Order 的一部分。

在电信计费系统里,“客户”可能有多个账户(每个子公司一个),每个账户有多个订阅,每个订阅可能由多个服务包组成,每个服务包包含多个有不同计费周期和定价层级的评价组件。“销售团队卖了什么”和”计费系统在评价和开票什么”之间的关系,是 Salesforce 标准对象完全无法表示的多对多关系。

当你被要求集成这两个系统时——一侧是 CIS,一侧是 Salesforce——工作的第一阶段不是技术性的。它是分类学的。你花几周时间把 Salesforce 概念映射到 CIS 概念,记录哪里没有映射,哪里概念只是近似等价,哪里的不一致是如此根本以至于你需要在某一侧创建自定义对象。

在 BluLogix 具体用的是 Talend Open Studio 作为 ETL 层。Talend 是一个可视化 ETL 工具:你把数据流构建成转换组件的有向图,Talend 生成底层 Java 代码。可视化模型对需要理解和审批集成逻辑的干系人有用。生成的代码才是真正运行的。“干系人在可视化模型里批准了什么”和”生成的代码在计费周期第一天凌晨三点遇到什么边缘情况”之间的落差,就是你挣薪水的地方。

BPMN 用于计费工作流

CIS 平台用 BPMN 定义业务流程——不是巧合,这和我后来在 Talentera 用 Camunda 时的工作流模型一样。电信计费有复杂的条件状态:一个订阅可以是激活中、暂停(欠费)、暂停(客户申请)、已终止、已转号、已过户。状态间的转换有业务规则:已终止的订阅不能重新激活,欠费暂停触发的重激活流程和主动暂停触发的不同。这些规则因管辖区和产品类型而异。

BPMN 是为此建模的正确语言。替代方案是把状态机逻辑硬编码嵌入计费引擎——那意味着每次规则变更都是一次代码 deploy。用 BPMN,更改特定监管市场的暂停到重激活工作流是一次流程定义 deploy,不是代码 deploy。你的合规团队可以直接审查 BPMN 图,而不是读 Java 代码。这不是理论上的优势——合规审查周期以周计,而”我们需要重新 deploy 计费引擎才能更新这个工作流”会给每次合规变更增加几周时间。

Spring Security 层处理计费平台内部服务的访问控制。Spring WS 覆盖 SOAP 服务接口——2013 年,SOAP 仍然是企业集成的通用语言,而你的平台目标客户——较大的电信运营商——有说 SOAP、期望 WSDL 的中间件。GWT 前端和 Spring MVC 端点通信。这是那个时期标准的 Java EE 架构。无聊是设计使然。

真正重要的那些 ETL 工作

基于 Talend 的 Salesforce 集成有一个核心需求:当 Salesforce 里一笔销售成交(Opportunity 移到 Closed Won)时,对应的订阅和计费配置需要在 CIS 里被创建,不需要手动录入数据。电信计费里的手动数据录入是营收漏损发生的地方。一个漏掉的折扣配置,一个错误的计费周期日期,一个配置错误的产品包——这些不是让软件崩溃的 bug。它们导致错误的发票。错误的发票产生纠纷。纠纷耗费时间、侵蚀客户信任。在电信行业,企业合同动辄年价数百万美元,错误开票是生死攸关的事。

处理这个的 Talend job 按计划运行,通过 Salesforce 的 SOAP API(后来是 REST API,随着 Salesforce 扩展 API 层)轮询 Closed Won 状态中尚未在 CIS 里配置的 Opportunity,把 Opportunity 和关联的 Product 数据转换成 CIS 订阅对象,创建计费配置,并标记记录为已配置。错误处理比 happy path 更复杂:部分失败需要是可重试的,而不会创建重复订阅。Salesforce 的批量操作并发模型需要被遵守。CIS 事务模型需要确保订阅创建和计费配置之间的原子性。

没人提前告诉我的那部分:Salesforce 的数据模型比它的 API 文档暗示的更多变。不同管理员在不同时期配置的不同 Salesforce 组织,有不同的字段布局、不同的自定义字段、不同的校验规则。一个针对某个组织完美运行的集成,需要对每个部署到的组织重新验证。我们在 Talend job 里加了一个验证步骤,在尝试转换之前验证源数据的形状,这样失败会以数据质量错误的形式浮出来,而不是以损坏的 CIS 记录。那个验证步骤每次 deploy 给我们省了大约一个数据事故。

向付费电信客户交付东西是什么感觉

有一类工程经验,只有向那些当事情出错时会打电话来的客户交付时才能获得。不是提 ticket。打电话。电信运营商有运营中心。计费出问题时,那个运营中心里有人知道,而且他们知道打给谁,他们会打来。

这种责任感带来的清醒是非常明确的。你不会发布假设性的基础设施。你不会发布你没法监控的东西。你在功能之前先构建运营工具,因为你知道有人会打电话来,而你需要能在十分钟内给他们一个答复。电信计费的关怀标准比大多数 SaaS 类别真的高,因为出错的代价可以精确到美元。

我处理过真实的电信计费事件。我交付了一个在计费系统和 CRM 之间移动真实客户数据的集成。我用 GWT、Java EE、Talend 和 BPMN 做到的,配上 Spring Security、Spring WS 和一个现在看来古老的 XML 世界。

这个世界并没有那么不同。Salesforce 和它试图集成的每个专业系统仍然有同样的数据模型不匹配。计费系统在底层仍然是 event-driven 的状态机。BPMN 仍然是合规驱动工作流的正确工具。技术栈变了。问题是一样的。

我知道如何在旧技术栈里工作。我知道它为什么做了那些选择。这比听起来要稀有。