数字化转型:服务化设计原则(上)

简介: 数字化转型:服务化设计原则(上)

在设计服务中心的过程中,对服务中心内服务接口和数据模型的设计非常重要,良好的设计原则和方法可以最大化地保障服务中心的可扩展性。


强烈建议读者学习著名建模专家Eric Evans最具影响力的著作Domain-Driven Design-Tackling Complexity in the Heart of Software(《领域驱动设计:软件核心复杂性应对之道》)以及Thomas Erl的著作SOA principles of Service Design(《SOA服务设计原则》),在实际的服务中心设计过程中,大多数情况下都可参考这两本书中的原则和方法。本书的目的是从全局的视野介绍如何更好地进行数字化企业的建设,所以不会深入探讨这个技术。


1)Façade(外观)模式。

接下来在介绍服务化设计原则时,会多次出现Facade模式。

外观模式的使用原理如图4-11所示。



image.png


外观模式的优点如下:

松散耦合:外观模式使得前台应用与中台服务中心可以进行松散耦合,让服务中心内部的模块能更容易地扩展和维护。

简单易用:外观模式让服务中心的服务更加易用,前台应用不再需要了解服务中心内部的实现,也不需要跟服务中心内部众多的功能模块进行交互,只需跟外观类交互就可以了。

更好地划分访问层次:通过合理使用外观模式,可以更好地划分访问的层次,有些方法是对系统外的,有些方法是系统内部使用的。把需要暴露给外部的功能集中到外观中,这样既方便客户端使用,也很好地隐藏了内部的细节。


2)DTO的使用。

DTO可以将服务中心复杂或易变的数据对象对前台应用屏蔽,让前台具备更好的稳定性。DTO是系统分层设计和服务化架构中经常使用的技术,概念本身也容易理解,如图4-12所示。



image.png


业务中台架构的核心是各个业务领域的设计建模以及服务接口的设计,笔者结合业界优秀的设计原则以及自己的实践,将服务接口典型的设计原则整理如下,供各位读者参考。


(1)契约先行

服务间的交互类似于不同组织之间的合作,按照正常逻辑,两个组织之间合作的首要任务就是先签订明确的契约,详细规定双方合作的内容、合作的形式,等等,这样才能对双方形成强有力的约束和保障,同时工作也能够并行不悖,不用相互等待。因此服务化架构中最佳的实践方式也是服务契约先行,即先做服务契约的设计。在进行服务接口设计时需要有业务、产品和技术等不同方面的人员共同参与,并定义出相应的契约,然后再实现具体的代码。


在实际的中台架构设计阶段,当在企业不同的业务部门收集到业务需求,形成产品需求调研文档后,需要从全局的视角对服务中心的服务接口进行统筹设计,即不是按照单一应用场景,如仅从电商或仅从CRM系统的角度,进行服务接口设计。虽然这些前台系统都是按照步骤逐步建设起来的,但服务中心的接口设计首先需要在全局的业务视角下进行规划和设计,有了清晰的接口设计,前台和服务中心就有了清晰而相对稳定的交互边界,就能大大降低后期实现和运营期的协作成本,总体效率更高。


由于服务的用户范围很广,在服务契约公开发布之后就要保证良好的稳定性,不能随便重构,即使升级也要考虑尽可能地向下兼容性。


(2)服务功能内聚

服务功能内聚几乎是任何服务化设计中最基本的要求。要创建功能内聚的服务接口,应该使功能相关的一组操作聚合到一起,同时必须将可能影响到业务正确性的逻辑在对应的服务中提供,而不能依赖服务调用方遵循正确逻辑。比如,用户注册的服务,其中包含了对于用户邮箱格式、用户名称以及密码强度的校验逻辑,虽然这些逻辑在前台应用的Web页面或者App中都进行了相关的校验,但前台应用最终调用用户中心的用户注册服务时,依然要在该服务中实现对这些用户属性的校验工作,而不能寄希望于前台应用做这些校验工作,这样才能避免因为前台应用遗漏校验而导致不合规则的用户能成功进行注册。一个典型的服务功能内聚的例子如

图4-13所示。


image.png


(3)服务粗粒度

服务的使用者对特定业务流程的了解一般比不上服务中心内部的人,所以服务的接口设计通常需要粗粒度,一个操作有可能对应一个完整的业务用例或者业务流程,这样既能减少远程调用次数,又能降低学习成本和耦合度。

例如,文档服务要给前台应用提供批量删除文章的支持,已有接口中提供deleteArticle(long id)方法,可以供用户自己做循环调用来实现批量删除文章的目的。此时,服务中心最好提供deleteArticles(Set<Long> ids)方法供前台应用调用,将N次远程调用减少为一次。

再例如,用户下订单的用例,要有一系列操作:

addItem(累计商品)→addTax(计算税)→calculateTotalPrice(计算总价)→ placeOrder (创建订单)

交易中心当然可以将这些服务以单个接口方法的方式提供给前台应用,这样不仅需要前台应用对于订单创建流程和逻辑有更高的要求,而且会增加出现服务调用错误的概率,最好封装一个粗粒度的方法供用户做一次性远程调用,同时也隐藏了内部业务的很多复杂性。服务调用方也从依赖4个方法变成了依赖1个方法,从而大大降低了程序耦合度。

另外,从服务和接口方法的数量角度来看,服务将通常作为测试和发布的单位,如果粒度过粗,将大量操作分组到单个服务中,则可能增加单个服务的使用者,这样就为服务使用者快速找到正确的操作带来了挑战,从而导致服务使用体验不佳。要更改服务,势必需要重新发布整个服务,从而影响较多使用者。

所以要避免服务粒度的两个极端:

提供仅有几个方法的很多服务。

数十或数百个操作均集中在几个服务中。

应考虑多个因素,如可维护性、可操作性和易用性,并进行折中。

还有一种划分服务粒度的方法是,创建反映业务对象生命周期的状态的服务接口。例如,费用申领中,每笔费用申领的生命周期都包含四个状态,如图4-14所示。



image.png


由于业务对象状态常常能同时反映业务和技术两方面的内容,因此完全可以将ExpenseClaimService(费用申领服务)拆分为适应每个状态的多个服务:ClaimEntryService(费用构建服务)、ClaimApprovalService(费用审批服务)、ClaimPaymentService(费用支付服务),得到如下所示的服务代码:

ClaimEntryService {

   createClaim(String userId);

   ClaimItemDetails[] getClaimItems(int );

   ClaimErrors[] validateClaim(int claimId);

   void removeClaimItem(int claimId, int itemId);

   int addClaimItem(int claimId, ClaimItemDetails details)

   int submitClaim(int claimId);

}


ClaimApprovalService {

   int approveClaimItem(int claimId, int itemId, String comment);

   void approveClaim(claimId)

   void returnClaim(claimId)

   ClaimItemDetails[] getClaimItems(int );

   ClaimErrors[] validateClaim(int claimId);

}


ClaimPaymentService {

   void payClaim(int claimId);

}

通过这种方式,能更方便地理解每个服务。而且,将接口这样划分非常适合服务的开发、部署、维护和使用方式。总结来说,通过将划分逻辑放在对象生命周期上,我们就可以建立具有恰当粒度的服务。


(4)消除冗余数据

由于服务的远程调用需要网络开销,特别是在并发量很大的场景下,这样的开销就不是一个可以忽略的因素了。所以在服务的输入参数和返回结果中,要尽量避免携带当前业务场景不需要的冗余字段,来减少序列化和传输的开销。同时,去掉冗余字段也可以简化接口,避免给外部用户带来不必要的困惑。

比如“文档服务”中有个返回文章列表的方法:

List<Article> getArticles(...)

如果业务需求仅仅是要列出文章的标题,那么在返回的文章对象中就要避免携带它的内容等字段。

这里有一个经典解决方案,就是引入前面提到的DTO模式,专门针对前台业务应用定制要传输的数据字段,这里需要添加一个AriticleSummary(文章概要)的额外数据传输对象:

List<ArticleSummary> getArticleSummaries(...)

ArticleSummary能很好地避免服务中心与前台应用间的冗余数据传输。


(5)通用契约

由于服务不假设用户的范围,所以一般要支持不同语言和平台的客户端。但各种语言和平台在功能丰富性上有很大差异,这就决定了服务契约必须取常见语言、平台以及序列化方式的最大公约数,才能保证服务具备广泛兼容性。因此,服务契约中不能有某些语言才具备的高级特性,参数和返回值也必须是被广泛支持的较简单的数据类型(比如不能有对象循环引用)。

例如,原有对象模型如下:

Class Foo {

   private Pattern regex;

}

其中,Pattern是Java特有的预编译,可序列化正则表达式(可提高性能),但在没有特定框架支持的情况下,其他开发语言可能识别不了,所以最好采用DTO的方式改成常用的数据类型,如下所示:

Class FooDto {

   private String regex;

}

相关文章
|
IDE 编译器 Linux
linux 编译 c或cpp 文件为动态库 so 文件(最简单直观的模板)
linux 编译 c或cpp 文件为动态库 so 文件(最简单直观的模板)
|
数据采集 SQL 分布式计算
数据处理 、大数据、数据抽取 ETL 工具 DataX 、Kettle、Sqoop
数据处理 、大数据、数据抽取 ETL 工具 DataX 、Kettle、Sqoop
4128 0
|
2月前
|
人工智能 缓存 自然语言处理
Kimi K3 上线千问AI平台,支持长程编程、知识工作与复杂推理
Kimi K3 现已上线千问AI平台。模型拥有 2.8 万亿参数、原生多模态能力和 100 万 token 上下文窗口,面向长程编程、智能知识工作与复杂推理场景。
353 0
Kimi K3 上线千问AI平台,支持长程编程、知识工作与复杂推理
|
2月前
|
存储 监控 IDE
阿里云百炼新人免费 Token 每个模型多少?每个模型免费100Tokens指南,你关心的问题都在这!
新用户首次开通阿里云百炼(华北2北京地域),自动获赠超7000万Tokens免费额度,覆盖Qwen、GLM、DeepSeek等70+主流模型,每模型100万Token,有效期90天,仅限实时推理调用。在阿里云百炼官网:https://t.aliyun.com/U/fPVHqY 免费领取千万Tokens
999 0
|
消息中间件 人工智能 分布式计算
多智能体系统设计:协作、竞争与涌现行为
作为一名长期专注于分布式系统和人工智能领域的技术博主,我深深被多智能体系统(Multi-Agent Systems, MAS)的复杂性和优雅性所吸引。在过去几年的研究和实践中,我见证了多智能体系统从理论概念逐步走向实际应用的转变过程。多智能体系统不仅仅是简单的分布式计算模型,它更像是一个微观社会,其中每个智能体都具有自主性、反应性和社会性。这些智能体通过复杂的交互模式,展现出了令人惊叹的集体智能现象。从最初的简单协作模式,到复杂的竞争博弈,再到最终涌现出的群体智慧,多智能体系统为我们提供了一个全新的视角来理解和设计复杂系统。在本文中,我将从架构设计原则出发,深入探讨通信协议的设计要点,分析冲突
1462 0
多智能体系统设计:协作、竞争与涌现行为
|
4月前
|
人工智能 供应链 安全
金发 8 号文落地,强制国标立项:金融机构 AI 治理的 18 个月倒计时
2026年6月,金融监管总局《AI安全开发应用指导意见》与国标委《智能体应用安全强制标准》同步出台,明确金融AI安全治理进入“硬约束”阶段。文件要求覆盖全生命周期管理、六大安全能力及18个月落地时限,直指当前机构在账单分拆、调用审计、API密钥管控、合规前置等关键短板。治理已非“事后补救”,而是必须即刻构建的基础能力。
594 0
|
4月前
|
人工智能 数据可视化 数据挖掘
连续7年!阿里云凭借Quick BI成为中国唯一上榜Gartner® ABI魔力象限的BI厂商
阿里云Quick BI第七次入选Gartner分析与BI魔力象限挑战者象限,是中国唯一连续七年上榜的BI厂商。作为AI-native智能分析平台,它集多源接入、归因分析、生态协同、按需计费于一体,已服务万家客户,覆盖全球8大区域。(239字)
465 1
|
5月前
|
存储 人工智能 自然语言处理
以前写重复提示词花 10 分钟,现在用 Hermes 技能一条命令搞定,效率提升 300%
本文全面介绍 Hermes Agent 技能(Skills)系统,从核心概念、格式规范、安装管理到自定义开发,带你掌握可复用工作流与知识单元的使用方法,让 AI 智能体自动执行复杂任务。
786 0
|
7月前
|
人工智能 自然语言处理 安全
免费试用|表格易填、分析难?阿里云瑶池Data Agent+钉钉AI表格分析神器来了
阿里云瑶池Data Agent携手钉钉AI表格,推出零门槛智能数据分析插件:支持自然语言提问、秒级生成洞察报告、六大安全防护、长期记忆优化体验,免费试用每天30分钟,让每位员工轻松驾驭数据,驱动业务增长。
668 0
免费试用|表格易填、分析难?阿里云瑶池Data Agent+钉钉AI表格分析神器来了
|
存储 Java 容器
Java基本语法详解
本文深入讲解了Java编程的基础语法,涵盖数据类型、运算符、控制结构及数组等核心内容,帮助初学者构建坚实的编程基础。

热门文章

最新文章