专栏名称: 逸言
文学与软件,诗意地想念。
目录
相关文章推荐
黑马程序员  ·  程序员是没有感情的NPC,石锤了! ·  2 天前  
程序员的那些事  ·  趣图:这个 AI 翻译要笑死人了 ·  3 天前  
程序猿  ·  完美解释了递归,哈哈哈哈 ·  6 天前  
51好读  ›  专栏  ›  逸言

项目札记005:XML模块的设计与依赖值分析

逸言  · 公众号  · 程序员  · 2024-09-29 11:52

正文

因为是元数据(metadata)驱动的SaaS平台,如何处理元数据自然成为整个平台的基础功能。我们选择xml作为存储和管理元数据的载体。之所以选择xml,原因在于:
  • xml是当时的主流文本配置协议,也提供了丰富的库方便操作xml(当时的我还不知道yaml)
  • 可以通过xsd配置包括与报表、界面、功能、实体映射等相关的元数据,相当于将xsd作为元数据的领域特定语言,以便于类型的自定义和扩展;
  • 通过jaxb可以直接把xml转换为Java对象
因此,在这个项目中,我们是重度使用xml。这在当今的项目可能并不多见,毕竟xml的表现力并不太好,语法又很冗余,显得不够简洁高效。以Kubernetes为例,就全部使用了yaml。鉴于此,我就不再饶舌xml的各种特性与操作了。重点谈谈xml模块的设计。
在我接手EISaaS的架构工作之前,xml的功能已经初具雏形,需要调用xml功能的主要是报表引擎和数据引擎。前者需要通过xml配置报表的各种配置,后者则配置了各种用于生成SQL语句的元数据,以及数据源和数据集信息。
但不知为何,当初的设计竟然将xml的功能和报表引擎模块放到了一起。由于没有清晰的包图,我一开始并没有意识到这一点,直到我引入实体引擎后,再来梳理这些模块彼此之间的关系时,才发现一些模块之间存在双向依赖以及相对不容易发现的循环依赖,如下图所示:
这是典型的职责分配问题。正是因为职责的错位,xml出现在报表引擎中,使得数据引擎为了读取相关的元数据信息而调用报表引擎,反过来,报表引擎需要访问实体引擎,获得报表需要绑定的model,实体引擎则需要通过数据引擎获得对应的数据,如此就形成了报表引擎、实体引擎和数据引擎之间的循环依赖。
同时,实体引擎为了获得实体与数据之间的映射关系,同样需要xml功能,如此又形成了报表引擎和实体引擎之间的双向依赖。
还有一个问题同样是xml错位导致的,那就是报表设计器(tool.reportdesigner)。它是一个用swing开发的客户端应用,用于业务人员设计报表模板。由于报表的格式和数据绑定都需要元数据配置,因此它也需要调用包含了xml的报表引擎。这一调用虽然没有产生循环依赖,却逼着报表设计器依赖了它不需要的除xml之外的其他功能。
罪魁祸首是xml,当然得把xml从不适合的地方赶出来。改进的设计为:
把xml从报表引擎模块中拎出来,成为基础设施层的一个单独模块,问题就得到完美解决。这也让我获得一个体会,就是在进行软件模块的设计时,要时刻注意模块之间的依赖关系,切忌出现双向依赖或循环依赖。当然,在使用如maven这样的构建工具时,也会帮助我们及时发现这样的问题。如果将这些模块放到同一个Java的module中(即编译时只会形成一个JDK),构建工具对此问题就无能为力了。即便它们都在一个共同的模块,仍然要避免出现这样纠错不清的依赖关系,它可能会随着时间的推移,慢慢变成一个大泥球,在需要对这样的模块进行拆分时,就会变得更加困难。
有一个测试工具可以对模块间的依赖关系进行验证和约束,这个工具就是ArchUnit(https://www.archunit.org/)。