产品SDK化转型:标准化与机构个性化定制解决方案

一、背景

在互联网行业中,企业通常可分为两大类别:2C和2B。对于2B企业而言,它们的产品往往以产品的形式提供给各个合作机构。以金融领域为例,一家2B金融公司通常将产品销售给各个银行和证券公司,这是2B领域常见的做法。

然而,在与众多合作机构合作时,常常需要进行产品迭代和定制化功能的开发。这些定制化功能涉及到前端页面和后端接口,有些功能甚至不适合合并到主线产品中,而只能作为合作机构的特殊功能。随着合作机构的增加,一个产品可能需要兼容数十家银行,这使得面对众多定制化需求以及需求可能需要合并至产品的情况变得复杂。

采用分支管理来解决这些问题是一个可能的方法,但随着时间的推移,分支管理可能会变得难以维护。开发人员可能会忘记自己的分支所对应的功能,这会导致混乱和错误。

因此,2B企业需要一种高效的方式来解决这些问题,同时还能够快速地与合作公司对接。这种方式可以是一种灵活的架构设计,可以支持定制化需求,并且具备良好的可维护性和可扩展性。

二、解决方案

针对这种情况,业内存在两种常见的观点:

  1. 超级产品:产品自身支持所有合作机构所需功能
  2. 产品SDK化:产品自身维持标准版本,各机构版本定制化功能单独开发

第一种方案通常适用于合作机构较少、产品量级较小的公司。然而,随着产品功能和合作机构数量的增长,采用这种方案可能导致应用变得越来越庞大且臃肿。产品可能会逐渐偏离最初的设计理念,因为众所周知,产品的功能越多并不意味着产品的质量更好。此外,如果在后期发现问题需要进行调整,那么回归到原始的设计将变得非常困难,这会降低系统的扩展性,如下图:

在这里插入图片描述

第二种方案是笔者推荐的方法。将产品SDK化,打造成通用的标准版本,各合作机构只需依赖SDK产品并开发定制化功能即可。这种方式既保持了产品的精简性,又实现了各机构版本的可插拔性。通过从一开始就设计良好的架构,可以极大地方便后续的扩展和维护。同时,这种方案也为未来的产品升级和技术创新提供了更大的空间和灵活性,如下图:

在这里插入图片描述

三、什么是SDK?

SDK本质上是一个JAR包,其中包含了一个应用的所有功能和组件。

  1. 假设你是一家提供自行车租赁服务的公司你的自行车租赁服务可以让用户通过手机App租借自行车并进行骑行。

现在,你的公司面临一个问题:你的自行车租赁服务需要适应不同城市的交通规划和用户需求,每个城市可能都有自己特定的交通规则和骑行习惯。

这时候,你可以考虑将你的自行车租赁服务功能SDK化。所谓SDK化,就是将你的自行车租赁服务功能打包成一个软件开发工具包(SDK),让其他开发者可以轻松地集成你的自行车租赁服务功能到他们的应用中去。

比如,你可以把自行车租赁服务功能封装成一个SDK,其中包含了租借自行车的接口、骑行路线规划的文档说明、示例代码等。其他应用开发者只需要引入你的SDK到他们的应用中,并根据你提供的文档和示例代码来调用租借自行车的接口,就可以在他们的应用中实现自行车租赁服务了。

  1. 假设你是一家提供在线支付服务的公司,你的服务可以让其他应用在其应用中集成支付功能。

现在,你的公司面临一个问题:你的支付服务需要适应不同的应用场景和不同的合作伙伴,每个合作伙伴可能都有自己特定的需求和定制化要求。

这时候,你可以考虑将你的支付服务SDK化。所谓SDK化,就是将你的支付服务打包成一个软件开发工具包(SDK),让其他开发者可以轻松地集成你的支付功能到他们的应用中去。

比如,你可以把支付服务封装成一个SDK,其中包含了支付接口、支付流程的文档说明、示例代码等。其他开发者只需要引入你的SDK到他们的应用中,并根据你提供的文档和示例代码来调用支付接口,就可以在他们的应用中实现支付功能了。

这样一来,你的支付服务就变得更加灵活和易用了。不同的应用开发者可以根据自己的需求定制支付功能,而你也不用为每个合作伙伴单独开发定制化的支付功能,节省了大量的时间和精力。同时,你的支付服务也更加易于扩展和维护,因为你只需要维护一个统一的SDK版本,而不用担心不同版本之间的兼容性和更新问题。

四、SDK化实操

以下示例均可在 gitHub#sdk-examples 仓库上找到。

为了让读者更好地理解产品SDK化改造的过程,我们将采用传统应用应用SDK化两种实现进行对比,以便读者更轻松地理解两者之间的区别和详细实现方式。

4.1、传统应用

  1. 这里我们简单模拟一个web应用,使用maven工具创建三个model模块,结构如下:

    sdk-app└─app-api└─app-common└─app-web
    
  2. app-web模块中编写常见的controller、service层且该模块依赖app-api及app-common,结构如下图:

在这里插入图片描述

  1. app-web模块的实现非常简洁明了。它通过对外开放/order接口,调用OrderService接口,最终返回Order实体类。以下是相关代码示例:

    // AppWebApplication
    @SpringBootApplication
    public class AppWebApplication {public static void main(String[] args) {SpringApplication.run(AppWebApplication.class, args);}
    }
    
    @RestController
    public class AppOrderController {@Autowiredprivate OrderService orderService;@GetMapping("/order")public Order selectOrder() {return orderService.getOrder();}
    }
    
    public interface OrderService {Order getOrder();
    }
    
    @Service
    public class OrderServiceImpl implements OrderService {// 获取application.properties配置文件参数值@Value("${tenant.order.name}") private String orderName;@Overridepublic Order getOrder() {return new Order(orderName);}
    }
    
    @Data
    @AllArgsConstructor
    public class Order {private String name;
    }
    
    # application.properties
    tenant.order.name=macbook
    

    至此,我们实现了一个传统上的服务应用,接下来我们将其改造为SDK。

4.2、应用SDK化

  1. 当考虑将产品转化为 SDK 时,首要考虑的是确定粒度,粒度的选择至关重要。通常情况下,一个服务应该对应一个 SDK,这代表该SDK中包含了一个服务的所有依赖。

  2. 本文所述的是一个 app-web 应用,因此只需要一个对应该应用的 SDK。然而,平台通常包含多个服务,因此每个服务应该对应一个独立的 SDK 包。

  3. SDK本质也是一个jar包,规范上应该以starter结尾,且该jar包包含了一个服务的所有功能及依赖,此刻的app-web模块已经包含了所有功能和依赖,故我们更名app-webapp-starter,此时结构如下:

    sdk-app└─app-api└─app-common└─app-starter
    
  4. 因SDK包只作为功能提供,故不应包含Application启动类,删除AppWebApplication启动类。

  5. 为了提高代码的清晰度和简洁性,在SDK化后,传统应用中通过注解(如@Service、@Component、@Bean等)将类托管至IOC容器中的方式将被统一。为此,在SDK的根目录下创建了AppWebAutoConfiguration类,使用@Configuration注解,并在其中统一使用@Bean方式构建所有被IOC托管的类,以便更方便地管理。下面是代码示例:

    /*** 自动装配类*/
    @Configurable
    @ComponentScan({ "org.example.sdk.app.starter" })
    public class AppWebAutoConfiguration {@Beanpublic OrderService orderService() {return new OrderServiceImpl();}
    }
    
  6. 此外,在传统的设置中,application.properties文件中的参数配置通常是分散在各个服务层中,通过@Value注解的方式使用。这种分散的配置方式不够清晰,因此我们也应该将这些参数配置统一管理,并交由@Configuration注解的配置类来处理。为了与上面构建的AppWebAutoConfiguration类区分开来,我们可以新建一个AppWebConfig配置类,用于托管所有的配置参数。下面是示例代码:

    /*** 参数配置类*/
    @Getter
    @Configurable
    public class AppWebConfig {@Value("${tenant.order.name}")private String orderName;}
    
  7. 在服务使用层,只需引入AppWebConfig配置类,就可以通过其提供的get方法轻松获取相应的配置参数,使代码结构更加清晰。下面是改造后的OrderServiceImpl示例:

    public class OrderServiceImpl implements OrderService {@Resourceprivate AppWebConfig appWebConfig;@Overridepublic Order getOrder() {return new Order(appWebConfig.getOrderName());}
    }
    
  8. 在当前阶段,我们还面临一个问题:**当合作机构引入了SDK包时,我们如何加载新增的两个配置类呢?**由于我们没有启动类,无法自动加载这些被@Bean注解托管的类。为了解决这个问题,我们可以利用Spring提供的自动装配功能,即 spring.factories

  9. spring.factories 是Spring框架中的一种特殊配置文件,用于自动化配置和加载Spring应用中的扩展点。在Spring Boot应用中,spring.factories 文件通常位于 META-INF/spring.factories 路径下。该文件使用标准的Java properties格式,其中包含了各种Spring应用中需要自动加载的配置信息,我们在SDK中新建 META-INF/spring.factories 自动装配配置文件,如下图:

    在这里插入图片描述

  10. 截止目前,我们已经完成了SDK的基本改造,主要做了以下四件事:

    1. 删除 AppWebApplication 启动类。
    2. 新增托管所有被IOC容器管理的配置类 AppWebAutoConfiguration,以及管理所有配置项的 AppWebConfig 类。
    3. 修改代码中对配置的引用和使用方式。
    4. 新增 META-INF/spring.factories自动装配配置文件。

完成了sdk化改造,接下来我们在机构仓库中使用SDK。

4.3、机构使用

  1. 首先要明确的是:每个合作机构对应一个独立的Git仓库,也就是说,每个机构都有自己的项目。如果有20家合作机构,那就对应着20个不同的机构仓库。这样的做法能够充分利用SDK的优势。

    举例来说,假设机构A使用SDK-1.0版本,而机构B希望使用SDK-2.0版本,而机构C则希望在SDK-2.0版本的基础上新增一些定制化的功能。正是由于我们将不同机构之间通过不同的仓库方式进行分割,才能够灵活地满足上述需求。

    机构A的仓库只需依赖SDK-1.0版本,机构B的仓库依赖SDK-2.0版本,而机构C的仓库则可以依赖SDK-2.0版本,并且在该仓库中开发新的功能。这样做完全实现了隔离,同时也实现了灵活的配置和可插拔性,如下图所示:

    在这里插入图片描述

    1. 了解了上述结构后我们模拟江苏银行合作机构,这里使用模块来模拟机构仓库,构建sdl-jsbank模块,又由于一个SDK对应一个服务应用,虽然我们只有一个sdk,但仍构建子模块jsbanl-app-web作为SDK的使用模块,此时结构如下:

      sdk-jsbank└─jsbank-app-web
      

      产品一般是微服务架构 ,服务个数 = SDK个数 = 机构仓库下web模块个数。

    2. jsbank-app-web模块中依赖app-starter,如下

      <artifactId>jsbank-app-web</artifactId><dependencies><dependency><groupId>org.example</groupId><artifactId>app-starter</artifactId></dependency>
      </dependencies>
      
    3. jsbank-app-web模块根目录下创建JsAppWebApplication作为SDK应用的启动类,如下:

      @SpringBootApplication
      public class JsAppWebApplication {public static void main(String[] args) {SpringApplication.run(JsAppWebApplication.class, args);}
      }
      
    4. 此外还需在jsbank-app-web模块resources文件夹下创建application.properties放置SDK所需配置,如下:

      tenant.order.name=macbook
      
    5. 现在,只需启动JsAppWebApplication应用,然后执行curl ip:port/order,即可得到SDK返回的结果:{name: macbook}。这是因为SDK已经配置了spring.factories自动装配文件,在机构端启动后便会自动加载SDK所需的托管类。

    6. 假设现在江苏银行需要进行定制化开发,添加一个/customize接口。在这种情况下,我们只需要在 jsbank-app-web 模块中新增控制器层和相应的服务层代码即可。这样的操作完全与SDK标准化产品和其他机构的功能隔离开来。下面是新增的 CustomizeController 的代码示例:

      @RestController
      public class CustomizeController {@Autowiredprivate CustomizeService customizeService;@GetMapping("/customize")public String customize() {return customizeService.customize();}
      }
      
    7. 新增CustomizeService接口及实现类CustomizeServiceImpl,代码如下:

      public interface CustomizeService {String customize();
      }
      
      @Service
      public class CustomizeServiceImpl implements CustomizeService {@Overridepublic String customize() {return "jsBankCustomize";}
      }
      
    8. 启动 JsAppWebApplication 应用,然后执行 curl ip:port/customize,即可获得定制化功能返回结果:jsBankCustomize。此时再次执行 curl ip:port/order,即可得到SDK返回的结果:{name: macbook}。这说明机构定制化功能与SDK标准化产品完全隔离。

    至此机构使用SDK结束。

五、其他问题

在上述SDK化改造过程中,我们看到对于合作机构新增定制化功能已经有了很好的支持。但如果合作机构需要的定制化功能不仅仅是新增,而是需要对SDK本身进行更改呢?

举例:假设SDK中对外用户身份认证的 /userAuth 接口底层算法是普通加密,而南京银行需要使用国密算法进行身份认证。在这种情况下,既不能更改 /userAuth 接口的定义,也不能直接修改SDK中的代码,因为这会影响其他合作机构的使用。

这种类似情况十分常见,针对这类问题也很好解决,解决方式如下:

  1. 首先我们的SDK模块中所有具体操作均要抽象成接口,即全面面向接口编程,这样才会给后续机构定制化留出口子。

  2. 在机构仓库中根据SDK提供的接口从而实现对应的实现类,如上示例中在机构仓库中便可以实现/userAuth接口对应的UserAuthservice接口,从而实现国密算法。

  3. 接下来有两种方式可以将我们自实现类注入进UserAuthservice接口中:
    3.1. 通过JDK中的SPI
    3.2. springBoot自身提供的@conditionalonproperty条件注解方式将实现注入

  4. 以上两种方式都可以将自身实现内容注入到SDK接口中,从而实现上述示例的需求。

  5. 关于JDK中的SPI实现方式可参考笔者另一篇文章: Java SPI解读:揭秘服务提供接口的设计与应用

  6. 关于@conditionalonproperty条件注解笔者后续会单独开一篇文章讲解,望读者见谅。

六、总结

在开发SDK时,需要考虑到不同合作机构可能有不同的定制化需求。为了满足这些需求,我们采取了SDK+机构仓库的架构设计,使得SDK和机构均具有高度的灵活性和可扩展性。

首先,将SDK设计为一个独立的jar包,其中包含了所有功能的总和。这使得合作机构可以轻松地引入SDK,并使用其中的功能。

其次,利用了Spring框架提供的自动装配功能,通过spring.factories文件实现了对托管类的自动加载,使得合作机构在引入SDK后可以自动加载所需的托管类,无需手动配置。

最后,通过定制开发的方式与机构紧密合作,这种方式既保持了产品的精简性,又实现了各机构版本的可插拔性。通过从一开始就设计良好的架构,可以极大地方便后续的扩展和维护,同时,这种方案也为未来的产品升级和技术创新提供了更大的空间和灵活性。

七、相关资料

  • 文章代码示例
  • Java SPI解读:揭秘服务提供接口的设计与应用
  • @ConditionalOnProperty注解使用

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://xiahunao.cn/news/2905117.html

如若内容造成侵权/违法违规/事实不符,请联系瞎胡闹网进行投诉反馈,一经查实,立即删除!

相关文章

2.11 Python关键字(保留字)

Python关键字&#xff08;保留字&#xff09;一览表 保留字是Python 语言中一些已经被赋予特定意义的单词&#xff0c;这就要求开发者在开发程序时&#xff0c;不能用这些保留字作为标识符给变量、函数、类、模板以及其他对象命名。 Python 包含的保留字可以执行如下命令进行…

企业网站建设的方法的相关问题的解决办法的问题

现在市场上比较大的公司都建立了自己的企业网站&#xff0c;比如华为、小米等&#xff0c;在他们的企业网站中&#xff0c;可以充分展示自己产品的优势&#xff0c;介绍公司的优质服务。 这都是让顾客改变购买想法的重要因素。 现在互联网发达了&#xff0c;很多人在购买产品的…

k8s局域网通过operator部署rabbitmq

参考&#xff1a;Installing RabbitMQ Cluster Operator in a Kubernetes Cluster | RabbitMQ 1、下载cluster-operator.yml wget https://github.com/rabbitmq/cluster-operator/releases/download/v2.7.0/cluster-operator.yml 2、拉取对应的镜像&#xff0c;这里的版本是根…

腾讯云4核8g服务器多少钱?2024轻量和CVM收费价格表

2024年腾讯云4核8G服务器租用优惠价格&#xff1a;轻量应用服务器4核8G12M带宽646元15个月&#xff0c;CVM云服务器S5实例优惠价格1437.24元买一年送3个月&#xff0c;腾讯云4核8G服务器活动页面 txybk.com/go/txy 活动链接打开如下图&#xff1a; 腾讯云4核8G服务器优惠价格 轻…

最优算法100例之10-数组中重复出现多次的数

专栏主页:计算机专业基础知识总结(适用于期末复习考研刷题求职面试)系列文章https://blog.csdn.net/seeker1994/category_12585732.html 题目描述 在一个长度为n的数组里的所有数字都在0到n-1的范围内。 数组中某些数字是重复的,但不知道有几个数字是重复的。也不…

安全的内网通讯软件,WorkPlus定制化 IM/办公门户解决方案

如今处于数字化转型的“加速期”&#xff0c;政企正经历着一场数字化迭代升级的时代浪潮。而不少企业都已具备了数字化管理的意识&#xff0c;数字化应用场景也在全面推开。WorkPlus不断推动信息技术与企业业务深度融合&#xff0c;作为安全的内网通讯软件&#xff0c;为企业提…

【算法刷题 | 二叉树 05】3.28(左叶子之和、找树 左下角的值)

文章目录 11.左叶子之和11.1问题11.2解法一&#xff1a;递归11.2.1递归思路11.2.2代码实现 11.3解法二&#xff1a;栈11.3.1栈思想11.3.2代码实现 12.找树左下角的值12.1问题12.2解法一&#xff1a;层序遍历 11.左叶子之和 11.1问题 给定二叉树的根节点 root &#xff0c;返回…

|行业洞察·汽车|《2024新能源汽车行业及营销趋势报告-20页》

报告的主要内容解读&#xff1a; 新能源汽车行业概述及品牌分布&#xff1a; 近年来&#xff0c;中国新能源汽车销量增速高&#xff0c;市场占有率快速提升&#xff0c;成为汽车行业的重要增量。新能源汽车消费者趋向年轻化、女性化和高端化&#xff0c;对高科技、新体验有较高…

LeetCode:718最长重复子数组 C语言

718. 最长重复子数组 提示 给两个整数数组 nums1 和 nums2 &#xff0c;返回 两个数组中 公共的 、长度最长的子数组的长度 。 示例 1&#xff1a; 输入&#xff1a;nums1 [1,2,3,2,1], nums2 [3,2,1,4,7] 输出&#xff1a;3 解释&#xff1a;长度最长的公共子数组是 [3,…

解决:npx nuxi@latest module add image 问题

背景 在 nuxtJS项目中使用内置组件 NuxtImg , 按照官方操作如下图进行安装&#xff0c;在npm run dev 时&#xff0c;出现了报错&#xff1a; "ipx" is imported by "node_modules/nuxt/image/dist/runtime/ipx.mjs", but could not be resolved – treat…

Unity 学习日记 12.小球撞击冰块游戏

目录 1.准备场景 2.让小球动起来 3.用鼠标把小球甩出去 4.加入鼠标点击小球的判断 5.小球与冰块的碰撞测试 6.撞击后销毁冰块 ​编辑 7.显示游戏计时 8.显示扔球次数 9.显示剩余冰块个数 10.游戏结束 11.完整代码 下载源码 UnityPackage 最终效果&#xff1a; 1.准…

机器学习(三)

神经网络: 神经网络是由具有适应性的简单单元组成的广泛并行互连的网络&#xff0c;它的组织能够模拟生物神经系统对真实世界物体所作出的交互反应。 f为激活(响应)函数: 理想激活函数是阶跃函数&#xff0c;0表示抑制神经元而1表示激活神经元。 多层前馈网络结构: BP(误差逆…

Git命令上传本地项目至github

记录如何创建个人仓库并上传已有代码至github in MacOS环境 0. 首先下载git 方法很多 这里就不介绍了 1. Github Create a new repository 先在github上创建一个空仓库&#xff0c;用于一会儿链接项目文件&#xff0c;按照自己的需求设置name和是否private 2.push an exis…

步态采集平台

&#x1f349;步骤一、读取视频每一帧图像 &#x1f349;步骤二、对读取的图像进行分割&#xff0c;得到全景下的步态轮廓图。 ​​​​​​​&#x1f349;步骤三、对读取的图像进行裁剪得到归一化的步态轮廓图。 ​​​​​​​&#x1f349;步骤四、保存这一帧步态轮廓图

MongoDB Atlas维护指南:常见类型、注意事项与窗口设置

为了给Atlas用户更好的产品体验&#xff0c;MongoDB产品团队会进行定期维护。 本文将会介绍&#xff1a; 常见维护项目种类及频率&#xff0c;注意事项维护期间的影响及建议维护窗口设置说明维护告警设置和邮件通知范例 维护窗口常见项目 定期SSL证书轮换软件升级&#xff…

Git--08--Git分支合并操作

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 Git分支合并操作案例流程客户端&#xff1a;GitExtensions操作步骤&#xff1a;A操作步骤&#xff1a;B操作步骤&#xff1a;C操作步骤&#xff1a;D操作步骤&#…

多焦点图像融合文献学习(一)

本文介绍的是一篇明为"A convolutional neural network-based conditional random field model for structured multi-focus image fusion robust to noise."的文献&#xff0c;主要包括文献的摘要、前言摘选、主要贡献、网络结构、实验结果及结论等方面。 文献名称摘…

DC电源模块的设计与制造流程

BOSHIDA DC电源模块的设计与制造流程 DC电源模块是一种用于将交流电转换为直流电的设备。它广泛应用于各种电子设备中&#xff0c;如电子产品、工业仪器、电视等。下面是DC电源模块的设计与制造流程的简要描述&#xff1a; 1. 需求分析&#xff1a;在设计DC电源模块之前&#…

RegSeg 学习笔记(待完善)

论文阅读 解决的问题 引用别的论文的内容 可以用 controlf 寻找想要的内容 PPM 空间金字塔池化改进 SPP / SPPF / SimSPPF / ASPP / RFB / SPPCSPC / SPPFCSPC / SPPELAN &#xfffc; ASPP STDC&#xff1a;short-term dense concatenate module 和 DDRNet SE-ResNeXt …

多源统一视频融合可视指挥调度平台VMS/smarteye系统概述

系统功能 1. 集成了视频监控典型的常用功能&#xff0c;包括录像&#xff08;本地录像、云端录像&#xff08;录像计划、下载计划-无线导出&#xff09;、远程检索回放&#xff09;、实时预览&#xff08;PTZ云台操控、轮播、多屏操控等&#xff09;、地图-轨迹回放、语音对讲…