最近不少朋友问我同一个问题:不买 SAP BTP,还能不能在本地 SAP 系统里搞 ABAP Cloud,走向 Clean Core?我理解他们背后的焦虑——云战略喊得响,但预算、合规、存量系统迁移成本都摆在那里,直接上云不现实。我自己的答案是:可以,而且本地落地 ABAP Cloud 不是权宜之计,是一条独立的技术路线。这篇就把我实际踩过的坑、验证过的策略,以及那些 SAP 官方文档里不会写清楚的细节,掰开揉碎讲一遍。
先说清楚什么叫 Clean Core。它不是云专属概念,核心思想很简单:让标准系统保持“干净”,把客户定制代码从内核里剥离出来,放到清晰的扩展层里,这样升级时不会冲突,创新时不用翻旧账。过去我们习惯直接在标准表上加字段、在标准函数里改逻辑,系统越用越“脏”,升级一次痛一次。Clean Core 要求我们用官方扩展点、用 RAP 开发模型、用合适的伙伴对象来写扩展,而不是碰标准代码。这些约束在本地 S/4HANA 里同样能做到,甚至某些方面比云端更灵活。
这篇文章面向的读者,是那些暂时没有 BTP 预算,或者因为数据驻留、网络隔离、系统架构原因必须维持在本地环境的开发者和架构师。如果你正准备新启动一个 S/4HANA 项目,或者正在为老系统做升级评估,这篇内容可以帮你省掉大量试错时间。
1. 为什么要在本地系统谈 Clean Core
1.1 Clean Core 不是云专属
SAP 官方把 ABAP Cloud 定义为一种开发模型,强调三件事:第一,开发对象必须支持云就绪,也就是能在 Steampunk 技术栈上运行;第二,必须使用标准的扩展点,不能修改 SAP 交付的代码;第三,通过 RAP 统一开发业务逻辑和接口。这套模型最早和 BTP 绑定,给人感觉好像只有上云才能用。但实际上,ABAP Cloud 的开发规范和 Clean Core 原则,可以完全在你的本地 S/4HANA 系统里落地,只要你的系统版本满足条件。
我见过很多企业误以为“不上云就谈不上 Clean Core”,结果继续在标准系统里乱改,等到要升级到新版本时,才发现一堆增强点和隐式增强因为代码冲突无法激活。实际上,SAP 从 S/4HANA 2020 左右开始,就在本地系统里提供了足够的工具和框架来支持云开发模式,包括 RAP 标准的业务对象模型、基于 CDS 的数据建模、以及通过“业务添加-自定义字段”等功能实现扩展。这些能力不依赖任何 BTP 订阅,只需要你愿意改变开发习惯。
所以,本地落地 Clean Core 的第一步,是从思想上把“云”和“ABAP Cloud”解耦。ABAP Cloud 是一种开发范式,BTP 只是一个可选的运行环境。就像跑步机是运动工具,户外跑步也能达到健身效果,关键是你跑不跑。
1.2 本地落地的实际驱动
为什么非要本地搞?我遇到的主要有几种情况。
第一种是成本控制。BTP 按用量计费,尤其是云平台的标准订阅、消息流量、数据库存储,长期下来是一笔不小的支出。很多企业的 SAP 系统使用率已经固定,为了几个扩展功能每年付云平台费用,财务上过不去。更容易出的方案是把开发模型本地化,保留未来迁移云端的可能性,但当下不付费。
第二种是数据合规与网络隔离。有些企业因为行业监管要求,核心业务数据必须留在自有数据中心,或者整个网络环境不允许外部连接。BTP 作为公有云服务,很难满足这类要求。本地系统反而能完全掌控数据流,该隔离的隔离,该日志审计的日志审计。
第三种是现有架构整合。很多传统企业已经在本地部署了一整套 SAP 生态,包括各个模块、中间件和周边系统。如果为了 ABAP Cloud 强行引入 BTP,等于又多了一个需要集成、监控、运维的平台。在本地系统里统一开发标准,反而是最平滑的整合方式。
明确驱动之后,你需要判断自己的系统版本和升级路径。如果当前还在 ECC 6.0,建议先把基础版本升级到 S/4HANA 2021 及以上,否则很多云开发功能不可用。如果已经在 S/4HANA 2020 以上,可以直接开始改造。
2. 本地 ABAP Cloud 的架构基础与选型
2.1 ABAP Cloud 开发模型的三个支柱
在动手前,需要把 ABAP Cloud 开发模型的底层逻辑理解透。它有三个支柱:CDS 数据模型、RAP 行为模型、以及在 Fiori 层上的服务暴露。
CDS(Core Data Services)用于定义数据模型。它和传统 ABAP 数据字典最大的区别,在于它直接在数据库层做建模,通过注解实现语义补充,生成的 SQL 更高效,还能自动暴露 OData 服务。CDS 是 ABAP Cloud 的数据基础,几乎所有新开发都应该从 CDS 开始,而不是先建透明表。
RAP(ABAP RESTful Application Programming Model)定义了基于 CDS 的实体、行为、服务如何组合。它把 CRUDQ 操作、校验、权限、事件等统一到行为定义里。在本地系统里,你可以用 RAP 创建一个完整的 Fiori 应用,也可以发布一个服务给外部系统调用。RAP 最大的好处是强制你在业务逻辑和服务接口之间做清晰分层,不会像传统报表那样把所有代码堆在一个 ABAP 程序里。
第三个支柱是服务暴露。RAP 定义的服务模型可以绑定到 OData 协议上,前端 Fiori、SAP 集成套件、第三方系统都能消费。本地系统里可以通过事务代码 SEGW 或直接在 RAP 里发布,不需要额外中间件。
2.2 在本地复现 BTP 能力的替代路径
BTP 上常见的服务包括身份认证、工作流、业务规则、日志分析等。本地落地 ABAP Cloud 时,这些能力可以由 ABAP 环境自身或配套组件替代,关键是别指望功能完全对等。
身份认证:BTP 的 IAS/IPS 可以由本地 S/4HANA 自带的 Fiori 框架和网关认证机制替代。如果你有 SSO 需求,直接配 Kerberos 或 SAML 到企业自己的 IdP,效果可能更好,因为完全内网可控。
工作流:BTP 的工作流服务实例化程度高,但 S/4HANA 本身还有经典工作流(ArchiveLink + Workflow)以及新的“可维护工作流”功能。后者是基于云就绪模型,可以在测试环境里建模。如果流程复杂度不高,用本地工作流引擎足够。
业务规则:BTP 的规则服务可以拆到 S/4HANA 的 BRFplus 中。BRFplus 从旧版本就有,一直维护到现在的 S/4HANA,可以定义决策表、表达式和规则集,且能在 RAP 行为逻辑里直接调用。
日志和监控:BTP 上的 SaaS 应用日志可以用本地系统的事务代码 SLG1 和 ST05 替代,甚至更熟悉。重点是别为了“云原生”去托管一个没有实际收益的额外平台。
我的建议是,先列出在 BTP 上计划使用的所有组件,再到本地 S/4HANA 上找对应的替代品,做一次能力映射。如果映射后发现某些服务确实无法替代,再考虑是否要让该模块保持本地空转,或者用轻量中间件调用外部服务。
2.3 适合本地落地的目标系统形态
并不是所有本地系统都适合直接做 ABAP Cloud 开发。我根据项目经验,建议按以下优先级排序:
最理想的是全新的 S/4HANA 私有云或本地部署系统,从项目第一天就用 Clean Core 原则建立代码治理框架。这样没有任何历史包袱,扩展开发全部走 RAP,测试传输从开始就规范。事实上,SAP 官方的“Clean Core”参考架构就是针对这种绿色地带的。
第二档是已有的 S/4HANA 系统,但还没有被严重改脏。系统里可能有一些自定义表和增强,但核心流程仍可用标准功能。这类系统适合用“逐步侵蚀”策略,先把新需求全部用 RAP 开发,再慢慢把旧的自定义代码迁移到扩展层。
第三档是遗留 ECC 或自定义非常多且混乱的 S/4HANA。这种情况不考虑直接做本地 Clean Core 大改造,而是先建立标准和限制清单:只允许新增对象,不允许修改标准对象。然后用接口或迁移方式逐渐把核心业务往标准功能上靠。你需要意识到,这种情况下“本地 ABAP Cloud”是长远目标,不是短期冲刺。
我自己更推荐从“新增开发全用 ABAP Cloud”开始,因为存量改造的复杂性远超预期。先保证新代码不脏,再逐步消化存量。
3. 存量改造与新增开发的分层策略
3.1 先态后势:划分代码边界
本地系统的代码构成通常三类:SAP 标准代码、合作伙伴代码、客户自有代码。我们要做的就是让后两者不侵入第一类。这里最核心的实践是“划分代码边界”。
我具体做法是:用软件包(Package)强制分层。比如设计一个客户扩展根包:
ZALL ZALL_CORE -> 所有 CDS/RAP 业务对象 ZALL_DT -> 所有数据字典扩展 ZALL_IF -> 服务接口 ZALL_UI -> Fiori 相关配置 ZALL_UTILS -> 工具类然后给每个包设置“仅允许创建”的访问控制关键字。这样从物理上阻止开发人员在标准包里创建对象。除了包结构,还可以用 Transport Layer 区分开发对象。点击软件包属性里的“传输层”,将客户扩展包绑定到自定义传输层上,和 SAP 标准传输层分离。这样升级时传输请求不会交叉,也能清晰看到哪些是客户包。
在代码边界上,我还建议启用“增强点”和“隐式增强”的管控。SAP 在标准代码里提供了大量显式增强点,指标准程序里预留给客户的扩展按钮。开发规范里强制优先使用显式增强点,实在没有才允许用隐式增强,而且隐式增强必须单独建立增强项目并登记。本地系统里可以使用事务代码 SEOG 管理增强实施。维护得当的话,升级时这些增强都能自动识别。
3.2 用 RAP 替代经典对话事务
本地系统里大量现有事务,比如报表、维护界面,都是基于经典 ABAP + 屏幕的。这些程序不是不行,但很难满足 Clean Core 的“云就绪”要求。新开发我不建议再用 Module Pool,而是直接以 RAP 为核心。
一个具体场景:客户要求做一个“销售人员维护客户价格清单”的新功能。传统做法是建表、写屏幕、写 PBO/PAI 逻辑。RAP 做法是:
第一步,定义 CDS 视图作为数据根。
@AccessControl.authorizationCheck: #CHECK @EndUserText.label: '客户价格' @Metadata.ignorePropagatedAnnotations: true define root view entity ZI_CustomerPriceList as select from ztc_price as p association [1..1] to I_Customer as _Customer on _Customer.Customer = p.Customer { key p.Customer as Customer, key p.Product as Product, p.Price as Price, p.ValidFrom as ValidFrom, p.ValidTo as ValidTo, _Customer.CustomerName as CustomerName }第二步,在这个 CDS 上定义行为实现类,通过FOR UPDATE、FOR CREATE等标注来控制权限和验证。这些行为定义在 ABAP 开发环境里是标准的,不依赖任何外部服务。
第三步,把 business object 暴露为服务,绑定到 OData 协议。前端可以配 Fiori Elements,或者用第三方工具通过 OData 直接读写。这样整个应用的业务逻辑都放在后端行为类里,结构清晰,测试性好,也符合 RAP 的规范。
RAP 不是只能做简单 CRUD。行为定义里可以写确定、验证、动作、函数,甚至触发后续事件。比如价格修改后写审计日志,可以在 RAP 的Save阶段做,而不需要动标准表。值得花时间学习它的行为交互模型,这会让你的代码质量比传统方式高一个等级。
3.3 扩展字段与自定义逻辑的官方通道
本地系统里最常见的“脏”,就是在标准表上乱加字段。比如给材料主数据表加一个“物流组”,很多人直接在表上追加 includes,或直接改表结构。正确的做法是利用自定义字段框架。
S/4HANA 里事务代码SFW5开启相关业务功能后,通过扩展字段(Customer Fields)功能添加字段。这个框架会自动在相关的 CDS 视图和 UI 模型上同步字段,保证后续升级时合并冲突最小。它不是直接改标准表,而是建立一个扩展字段表,并在运行时按逻辑关联,SAP 官方会维护这个机制,兼容性最好。
如果需要在标准功能上增加自定义业务逻辑,优先使用 BAdI(业务插件)实现。SAP 标准流程几乎都预留了 BAdI 接口,比如物料主数据的保存、销售订单的创建、凭证校验等等。找到合适的 BAdI,在里面挂钩子,比直接增强方法更安全。
举个例子,我们曾有一个需求:创建销售订单时,必须根据自定义汇率表重新计算价格。直接在标准函数里改代码必挂。后来用了销售订单创建主流程的 BAdI 之一,在完成创建后、保存前调用我们的 ABAP Cloud 类,逻辑通过自定义日志记录。整个改造没有触碰标准代码,升级时一挂即过。
这个过程中,需要维护一个“扩展清单”,列出所有的自定义字段、BAdI 实施、隐式增强。后续升级前可以用这个清单做影响分析,提前发现不兼容变更,而不是等到激活报错才哭。
4. 实操落地:关键步骤与工具链
4.1 项目启动前的基线评估
在开始写任何代码之前,先给系统做一次全面的“健康检查”。这个项目能不能顺利推进,完全取决于你对现状的理解有多清楚。
首先,梳理当前系统的 S 版本补丁级别。使用事务代码SAP_BASIS_INFO或SUIM查看系统版本。注意 ABAP Cloud 模型的完整支持依赖于较新的内核版本,尤其是 RAP 相关功能。如果系统还在 2020 以下,建议先升级内核和组件,至少到 SP 02 以上。
其次,运行事务代码SE08查询系统代码目录,搜索所有对标准表中的直接修改。用SLIN或代码分析器检查 ABAP 对象质量,重点看是否使用了MODIFY、INSERT对 SAP 数据字典对象的直接操作,是否存在自定义函数覆盖标准函数。这些是“脏”的集中地。
建议把结果输出到一个电子表格中,为每个问题对象标注“建议方案”:迁移到 RAP、用 BAdI 替代、删除废弃代码、保留并隔离。这会形成项目改造的完整 backlog。
最后,检查一下传输请求系统。如果原来客户对象混在标准传输层里,需要尽早分离。你可以创建一个自定义传输层,把所有客户开发对象调整到这个层里。这个动作越早做越好,因为一旦业务代码变多,调整传输层会影响导入顺序。
4.2 开发对象的迁移与创建
前面提到,新开发全部走 RAP。但已经存在的 ABAP 报表、函数模块和类,怎么处理?我的经验是,不需要一次全部迁移。你可以先确立一个“RAP 优先”原则,然后在每个业务需求迭代中,把相关的旧对象逻辑替换掉。
具体操作时,首先为每个旧功能创建一个新的 RAP 对象,例如用 CDS 视图替代旧的 SQL 查询,用行为类替代旧函数模块中的写入逻辑。然后在新的 UI 层暴露 OData 服务,并把旧程序的入口隐藏或废弃。整个过程可以并行运行,直到旧程序无人使用后删除。
迁移时注意一点:不要在 RAP 的 CDS 里使用旧的自定义表结构。如果表结构本身很乱,先把它规范化。规范的规则包括:主键明确、时间戳字段使用ABP_LAST_CHANGED_AT、所有字段都加描述标签、全部使用 CamelCase 命名。这些细节决定后续维护时是否清晰。
为了快速创建 RAP 对象,你可以在 ADT(ABAP Development Tools)里使用项目模板。选择Fiori项目模板,可以生成 CDS、行为定义、服务定义和工程基本结构。我习惯在本地先写 CDS,再生成行为,再在 Eclipse 里调服务,整个链路比 SE80 舒服许多。
4.3 构建、测试与传输
ABAP Cloud 项目非常依赖自动化测试。本地系统没有 BTP 的 CI/CD 服务,但你可以自建,或者用系统自带的工具。
在构建环节,使用GCTS(Git 版 CTS)可以把传输请求和 Git 仓库绑定,这样每一次开发提交都会留 Git 记录。如果你没有 Git 服务器,也可以在本地用 ADT 的Check功能做静态检查。更重要的是把 ATC(ABAP Test Cockpit)检查嵌入开发流程。ATC 可以按 Clean Core 规则检查某个对象是否符合云就绪标准,比如是否使用不推荐语句、是否有安全问题。
每次开发完成后,运行事务代码ATC,选择相关对象集,查看检查结果。如果你在开发过程中不断解决 ATC 提示,项目推进到后期会非常顺畅。否则,到了验证阶段再拉着一堆 RED 状态的问题去逐条解释,那是灾难。
传输环节依然走 CTS。建议每个 RAP 对象都打成一个独立的传输请求,并且请求说明里写明关联的业务场景。这样出问题时,可以通过请求号快速定位影响范围。同时在SE01里维护传输层的依赖关系,确保传入测试系统时按正确的顺序导入。
5. 常见的坑与避坑清单
5.1 命名空间和软件包管理的坑
很多团队在刚做 ABAP Cloud 时,对命名空间不重视,开发对象全都放在默认的$TMP或标准包Z下,随意建对象。这导致后期清理时根本分不清哪些是正式对象,哪些是测试残留。
我建议从一开始就定义清晰的命名规则:所有开发对象必须以Z(或Y)开头,且最好包含业务模块缩写。例如销售,客户对象以ZSD开始;财务以ZFI开始;主数据以ZMD开始。在包结构里,为每个模块建一个主包,下面建子包,子包再按“DT、BO、UI”划分。这样给你的后续维护带来极大便利。
还有个大坑是包类型和传输层的匹配。如果你不小心把客户对象建到了标准包SAP下,传输请求会混入标准请求,升级时会冲突。发现这类情况,需要用事务代码SE03把对象从标准包里转出。这个操作本身容易出问题,所以最稳妥的办法是:新开发前,先建立好完整包结构,并给团队做一次强制培训。
5.2 版本升级与兼容性的坑
本地系统最大的优势是完全可控,但升级依然是不可避免的。Clean Core 的最终目的就是在升级时不出冲突。实际过程中,最常遇到的坑是 CDS 视图与数据库表的版本兼容。
S/4HANA 的 CDS 视图基于数据库表的动态字段列表。如果标准 CDS 视图在升级中增加了新字段,而你的自定义扩展视图没有同步调整,会导致激活失败。这个问题的典型解法是:不要创建“扩展视图”去复制标准视图内容,而是使用extend view的能力,通过扩展视图添加自己的字段和关联。本地系统里这种扩展方式兼容性好,升级时通常能自动合并。
另一个兼容性坑是隐式增强。隐式增强在旧版本里允许你给标准方法添加任意代码,但新版本内核在每次升级后都会检查这些增强是否仍然兼容。如果增强引用了标准方法签名中已经被改过的参数,升级时就报错。解决方法是:尽量少用隐式增强,尤其不要在关键接口上用。非要使用,也得把增强代码的粒度做小,逻辑集中在自定义类里,不要直接在增强里写复杂业务。
5.3 团队技能与流程的坑
很多项目在技术上走对了,但团队还停留在旧开发模式,导致代码风格混乱、质量参差。比如,有人仍然用WRITE输出报表、在 ABAP 里写 HTML、用旧 BDC 方式批量处理。这些代码虽然能跑,但在 CDS 和 RAP 环境里就是“脏”。
团队技能需要在项目开始时同步补课。我建议给团队统一安排 RAP 和 ADT 培训,并建立“代码评审委员会”。每次传输请求入测试系统前,必须由另一名熟悉 ABAP Cloud 的开发者做评审。你可以在 ADT 里配置“对象检查字号”,将 ATC 检查设为强制执行,只有检查结果无省略项才能释放请求。
还有流程上的坑:不要把所有扩展都堆在开发系统里,而不做定期的“清扫”。每个迭代结束后,把废弃请求和未选择的对象清理掉,保持开发系统干净。否则,到了升级测试阶段,你会发现开发环境本身就乱得无法进行变更。
5.4 性能与运行稳定性
本地系统里使用 RAP 后,代码结构更清晰,但如果不注意性能,依然会慢。CDS 视图的关联不能滥用。每个关联都会生成额外的 JOIN 或子查询,如果关联层级过深,性能会急剧下降。建议使用@OData.publish: true的属性时,只发布必要的服务,避免把所有视图都暴露给 OData。
RAP 行为类中的读写操作,必须使用 EML(Entity Manipulation Language)的现代语法,而不要用MODIFY直接改表。EML 会自动处理锁和校验,但你必须理解事务边界。我见过有同事在FOR MODIFY里对每个子记录单独写操作,结果引起锁冲突。正确做法是先把数据收集到一个表,再一次性调用 EML 批量处理。
运行时稳定性方面,要给本地系统开启 ABAP 日志和监控。使用事务代码SLGL打开 ABAP 运行日志,记录 RFC 调用、更新任务和数据库操作。结合自定义的错误处理机制,把每个 RAP 行为类的业务异常都抛给统一错误类处理。这样即使用户操作出现问题,也能在日志里快速定位,而不是随机报错后无从下口。
6. 本地 Clean Core 的长期维护与演进
6.1 持续合规的代码扫描
本地系统需要建立周期性的代码扫描制度。我通常每个季度运行一次 ATC,针对所有客户开发对象做一次云就绪检查,输出不符合项,然后制定整改计划。要让这个过程自动化,可以用 ABAP 计划任务定期触发 ATC 检查,并把结果发送到邮件列表。
扫描的重点包括:是否使用了不推荐的 ABAP 语句、是否有安全漏洞、是否引用了不存在的字段、是否违反了命名规范。通过定期扫描,团队能时刻知道自己离 Clean Core 有多远。
除了 ATC,还要关注权限检查。RAP 的行为定义中要显式使用访问控制,不要图方便让所有用户都能 OData 访问。你可以用 DCL(数据控制语言)为 CDS 视图定义权限逻辑,这样在前端 Fiori 里会自动生效,也不会因为误配置造成数据泄露。
6.2 与未来云迁移的衔接
最后聊一下本地 ABAP Cloud 和未来云迁移的关系。很多人担心本地做好了,以后上云是否还得重做。我的回答是:不会,反而会轻松很多。
因为 ABAP Cloud 的开发规范本质上是云兼容的。如果你的本地代码严格遵循 RAP、CDS、云开发检查规则,并且所有扩展都使用标准通道,那么未来迁移到 S/4HANA 公有云或私有云时,这些对象都能原样运行或做非常少的调整。真正麻烦的是那些仍停留在旧开发模式的程序,那才是迁移时的最大阻力。
所以本地落地 ABAP Cloud 的意义不仅是解决眼前升级痛,更是为未来云迁移保留了选择权。你现在做的每一步,都在降低未来的技术债。
我个人在实际操作中最深的体会是,Clean Core 不是一次性完成的“项目”,而是一个需要持续维护的规范。你可以把它当成一个长期习惯来培养,最开始可能处处受限,但坚持下来会发现,系统的可维护性、交付速度、升级平滑度都会显著提升。如果你刚起步,我建议先从一个小模块的 RAP 扩展开始,走通全流程,再逐步铺开。路虽然长,但每一步都值得。