您可以捐助,支持我们的公益事业。

1元 10元 50元





认证码:  验证码,看不清楚?请点击刷新验证码 必填



  求知 文章 文库 Lib 视频 iPerson 课程 认证 咨询 工具 讲座 Modeler   Code  
会员   
 
   
 
 
     
   
 订阅
  捐助
元数据驱动设计:创建用户友好的企业级DSL
 
 作者: Aaron Kendall 来源:infoq 发布于 2016-07-21
  2249  次浏览      25
 

IT部门与其软件的用户之间已经在不知不觉中形成了一种互惠互利的共生关系,他们的共同努力可以结合在一起,成为一种集体性的工作。在最理想的场景 中,软件开发者与架构师能够充分理解,甚至是预测出项目干系人的需求。最终为用户创建出各种实用的应用,通过各种工具发挥客户的价值,并让人们的生活充满 了活力。

我们通常会将这些工具设想为某种简单的应用程序,它将帮助我们完成每日的数据维护工作,例如某个提供各种CRUD功能的GUI,或是可以完 成某种不相关任务的简单脚本(面向高级用户)。不过,如果我们能够稍微拓展一下想象力的话,其实完全可以为客户创建某些即强大而又不同寻常的东西。

有些时候,我们甚至不需要去想象出某种全新的东西,而只需将一些已经存在多年的东西进行一些简单的转变即可。例如某个业务规则引擎这样常见的技术。

对于那些无法被传统的IT数据管道所理解的内容来说,通常需要经过一系列的步骤,才能够从外部导入生产系统中:

这些具体步骤可以分解为多个流程或是合并为一个流程,不过我们现在只需专注于Record Processor这个模块即可。该模块将对当前记录进行一系列步骤操作,在将其提交给持久化的存储引擎(例如数据库表)之前对其进行校验,或是对数据进 行操作。具体来说,我们最感兴趣的是那些将以特定的业务规则处理这部分数据的步骤。举例来说,我们的业务只会保存某种特定类型的产品记录,因此如果某个产 品的类型为“Death Star”,就直接拒绝该记录。

对于File Parser这样的模块来说,我们可以创建一个GUI应用,让它将数据文件加载到某个电子表格中,用户可以将某些表格列映射为某个数据结构中的属性。如此 一来,用户对于整个管道就有了一定的控制权。但对于其他模块(例如Record Processor)来说,用户对于后端架构的直接控制权非常少,乃至于完全没有。为了对Record Processor进行必要的调整,用户必须像更传统的方式一样为开发者提供规格说明,让开发者直接将调整的内容写在代码中。在这种场景中,强制实施这种 业务规则(例如对业务数据进行校验或调整的规则)的功能包的开发过程往往都是痛苦的。

当然,这几十年来,也有一些老派的公司,例如ILLOG和Pegasus Software在规则引擎方面推出了一些实用的、有价值的工具。但是,通过这种方式实现的产品对于那些掌握公司独有数据的实际业务知识的人来说是无法直 接使用的,这些工具无法让他们直接操作那些业务规则。

我们能否创建一种语言,让非技术专家也能够轻松掌握,同时又能够保证那些规则在业务领域中的应用?我们能否将一些明显而必要的处理过程进行抽象,让他们能够隐式地应用于语言的表达式中,
使 该语言只需要对最重要的内容进行明确地表达?我们能否在运行时对该语言的某个片段进行解释与执行,而无需对系统进行重新编译或重新部署?我们能否真的能够 为非技术背景的业务人员创建一个可维护的领域特定语言(DSL)吗?正如以下这个简单的示例所表现的一样,对这些问题的回答都是Yes。

虽然有人认为一般来说不应当将如此重要的一部分功能授权给用户,但每家公司总有一小部分人具有较高的权限(例如超级用户),因为他们掌握了较多的业 务知识。如果这些人能够自行维护业务规则,而无需开发者的参与,则他们的生产力将得到提高。为了上他们具备这样的能力,我们可以尝试着为他们传授一些更基 础的编程知识,甚至为他们介绍某种流行的脚本语言。

如果你日常的工作平台是基于Java语言的,则可以选择为这些超级用户教授JavaScript,因为Nashorn JavaScript Interpreter已经出现在Java 81上,并将逐渐加深与JDK的集成。因此,让我们暂时确定为超级用户们传授JavaScript的方案,使他们能够自行编写一些业务规则。最终编写的业务规则可能是与以下片段相类似的代码:

首先,假设上文的图示中所表示的Record Process模块是用Java与Nashorn包所编写的,它即将执行某个特定JavaScript文件中的ExecuteBusinessRules 方法。这段JavaScript代码只是对日期进行了简单的调整。ExecuteBusinessRules函数接收两个包含登记数据的 JavaScript对象,并比较两者的PublicationDate属性(业务规则引擎本身将负责为该脚本提供IncomingBookData和 CurrentBookData数据)。如果输入的日期晚于当前日期,则首先将输入的日期减去4天,再提交至Record Persistor模块。

即便只是为了完成这个简单的任务,我们也必须编写近100行JavaScript代码,包括以上代码段中没有显示的对象(例如 IncomingBookData)与函数(例如ConvertToJSDate)的定义。为了进行适当的错误检查,可能还要加入更多的代码。除了需要开 发者帮助实现这些通用的库之外,为了实现ExecuteBusinessRules方法体,超级用户还必须熟练掌握JavaScript。

除了代码量过多之外,开发者也没有什么简便的方法能够自动检验超级用户的代码。因此,为了对代码进行调试,他们不得不熟练掌握 Firebug等工具。即便如此,也没有什么方法可以检查与系统相关的其他类型的错误(例如把“PublicationDate”这个关键的名称写错 了)。而不幸的是,这种方式还可能成为滋生错误的温床,因为超级用户们可能会无意(或有意)创建一些对系统造成灾难性后果的JavaScript代码。总 的来说,在干系人选择的这条不寻常的路上,可能会有各种陷阱与苦难等待着他们。

如果我们愿意为超级用户提供真正的主动权,为他们创建一个模拟沙箱,那么我们应当设法创建一种简单而强大的领域特定语言,让它成为业务规则 语言的基本语法。因此,我会为该项目选择元数据驱动设计的开发方式。那么,什么是元数据驱动设计(MDD)呢?正如在一篇关于构建MDD架构的文章2所 描述的一样,MDD是一种基于领域驱动设计方法所构建的设计方法。通过这种设计方法,用户在创建系统(或子系统)时将通过元数据决定数据模型与功能。通过 使用无固定形式的数据结构,我们可以创建一个具有可塑性的基础,并在这个基础上构建其他软件层。只需在元数据中加入新的行,就能够对系统进行培训,使其能 够理解新的数据以及处理这些数据的方式。

这种类型系统的实现需要在内部包含一个词典,以提供某个领域中的实用术语。在某些情况下,元数据所隐含的功能还可作为某种语法的基元。虽然这种语法非常有趣,但请让我们暂时忽略它。我们现在主要的关注点在于之前所说的词典,它是我们自定义DSL解决方案的基础。

当MDD成为核心的设计方法后,我们就能够克服超级用户在使用JavaScript等脚本语言时所遇到的多种障碍了。由于MDD只是领域驱 动设计(DDD)方法的一个渐进的版本,因此我们已经有一个现成的术语表以描述目标系统中的各种数据属性,并且能够利用他们为自定义规则引擎创建一种 DSL(在这个特定的用例中,我们并不打算考虑使用这种引擎的某个特定实现细节,例如它是否应当使用Rete算法。而仅仅关注于输入引擎的语言文件,以及 对其执行过程的期望)。

如果你还不熟悉DSL背后的思想,建议你去看一看Martin Fowler所做的报告3,他将DSL描述为“为了 解决某种特定类型问题的计算机语言的有限的形式”。在这篇影响深远的演讲中,Fowler描述了如何使用XML配置数据作为一种用于Java程序与框架的 简单DSL(当然,这种使用XML的方法如今已经广泛用于各种Java框架了,例如Sprint和Struts)。Fowler在演讲中提到了使用这种 DSL的多个优点,包括在许多场合下无需重新编译代码,以及对普通的业务用户也具备可用性。

而这些优点正是我们所需要的。由于这种方式不需要进行重新编译,因此非常适合用于MDD的实现,因为MDD的主要目标之一就是尽量少改动代码。其次,我们对于可用性有着强烈的渴望,因为我们的
预期目标就是能够方便地编写业务规则(尤其是通过某种简单而对用户友好的工具帮助实现这一点)。如果现代化的Java编程已经大量地使用了这种方法,我们为什么不通过这种策略,为那些掌握了业务知识,却缺乏技术背景的干系人创建一个健壮的解决方案呢?

因此,让我们简单地回顾一下在我的第一篇MDD文章中所提到的内容,尤其是用于描述某个特定系统的数据属性的电子表格:

我们可以看到,这段元数据描述了我们的数据属性的各种重要特质。由于这一格式使我们能够通过简单地调整元数据,或是在元数据中加入新的行的方式为系 统提供动态功能,因此超级用户们可以通过创建一种简单的DSL的方式暴露这些元数据的强大功能。正如Fowler在他的演讲中讲解的一样,我们可以充分利 用XML的内在结构以及多种能够解析这一格式的可用工具。现在,我们已经在元数据中准备好了以XML形式表现的基元词典以及语法,可以尝试实现新语言基础 功能的第1个迭代了。我们选择了将上文所述的包含了PublicationDate的JavaScript代码进行移植,作为原型的基础:

与JavaScript版本的实现相比,这一个XML片段更简短并且更直观,它忽略了在多数语言中所必须包含的各种细节(例如对数据格式进行校 验)。当然,这些细节以及RuleTree的执行过程必须在其他部分实现,事实上,他们将作为引擎本身的代码而实现(尽管这个规则引擎及其解释器的实现已 不在本文的范围内,但我们稍后仍将对此进简单的介绍)。

与JavaScript版本的示例相似,Business Rules Engine需要暴露一个Java接口,其中定义了一个ExecuteBusinessRules方法。该方法将由Record Process模块进行调用,并提供具体的Current与Imcoming对象。这部分代码将对业务超级用户隐藏,因为他们无需了解(很可能也不关心) 这些特性。

除了代码的尺寸减小之外,更重要的一点在于,对那些必须维护与改进这个架构的开发者来说,这种架构也带来了大量的优点。首先,通过使用 XML对业务规则进行结构设计,使语法的验证得到了简化,让我们能够放心地交给超级用户进行编辑。如果他们需要帮忙修正某些错误,有许多现成的应用(包括 免费与具有专利的应用)可以对XML的结构进行调试,并且修复大多数语法方面的错误。其次,通过使用电子表格中所展示的属性名称,我们就可以将实现的规则 引擎与上文所描述的MDD架构进行整合,在对业务规则进行校验与执行时使用其中的元数据。

当然,我们仍然需要创建某种基础的语法分析器以理解各种标记中的表达式,例如<eval>等等,而这些基础表达式(例如“IS BEFORE”)是十分实用的工具,其调用与开发都非常简便。不过,这种方式真正的价值来自于可访问的元数据。正如我们通过XML校验规则的结构一样,我 们现在可以通过对上述属性在它的应用上下文中进行检测,以校验及评估各种表达式。

首先,一旦创建了DSL文件之后,我们就可以为进行编辑工作的超级用户创建一些简单的校验工具,以确保某些值以他们特定的类型存在。举例来 说,“PublicationDate”属性可以结合使用“IS BEFORE”这个操作符,但如果对“PriceValue”属性使用了相同的操作符,我们的校验工具就会向超级用户发出一条警告信息,表示某个无效操作 被应用在“double”类型的属性上。其次,在Business Rules Engine的Java代码中,“IS BEFORE”表达式的代码可适用于对日期进行比较,只需为该操作添加所必须的辅助性代码(例如错误检查)。

以上示例展现了MDD怎样针对实际数据执行简单的业务规则,而如果我们能够将元数据中的更多维度带到DSL中,就能够创建更多的功能。以 2015年2月发表的文章中所描述的MDD架构为例,它的设计包含了一个audit层,这一层是由名为Fields的元数据结构所组成的,在Fields 中描述了权限与锁的信息。我们可以在项目中加入一些经过认真计划的功能,从而在DSL中编写其他类型的表达式,以对产品数据进行更进一步的评估:

现在,除了对数据本身进行修饰以及操作之外,我们还可以利用附加信息对记录进行操作。正如以上示例所述,我们现在可以评估某个产品的当前 PublicationDate是否已锁定(即不允许进行修改)。在这个MDD架构的后续迭代中可以向它的格式元数据中引入更多的维度,而每个新引入的维 度都可以包含在DSL中。

那么,我们如何创建这个Business Rules Engine呢?我们必须为此付出巨大的精力吗?其实上,这可能没有你想象的那么糟糕。在这一点上,我们同样不打算过度深入探讨某种现有的实现,但我们将 展示它的基本思想。首先,正如本文开头的图中所展示,DSL Rules Interpreter模块需要读取规则文件,并为其内容创建一个有效的表现。在这个示例中,我们需要将所有信息加载到某个RuleTree类中。这个 RuleTree将由一系列名为RuleSet的逻辑块所组成,以下代码展示了一个RuleTree的基本定义:

在上述DSL示例中,以该XML片段所生成的RuleTree将包含某个RuleSet的两个实例:其中一个示例的Description对应 “PublicationDate Rules”(即<if>标记),而另一个示例对对应其子标记(即<leaf>标记)。如果我们需要一些更复杂的应用,可以以嵌 套RuleSet的方式创建层次性的RuleTree。当RuleTree生成之后,需要将其传递至Business Rules Engine,并且对两个业务对象(或是这个用例中的产品记录)应用该RuleTree。Business Rules Engine模块随后将生成一个RuleTreeReport对象,其中包括了执行RuleTree所生成结果的详细信息(其中包括一个状态码,以表示执 行成功或是出现了严重错误)。虽然这些简短的步骤只是对必要的工作进行了简单的小结,但重点在于:只需最多几千行Java代码,就可以设计出一个可运行的 规则引擎了。

当然,对XML及一些基本的自定义表达式(例如“IS BEFORE”)的结合使用或许只是基于MDD进行DSL设计的第1个迭代的内容。虽然这个主题已经超出了本文的范围,但我们确实能够进一步改进这一架 构,为表达式提供标准的实用特性(例如操作符)。要实现这种功能,可以选择一种流行的语言作为我们的基础(JavaScript、Perl、Python 等等),为其实现一层额外的功能以结合元数据的使用。我们甚至可以将“IS BEFORE”等操作符进行移植,以元数据的方式将它的执行过程绑定在某个特定的库或是JIT代码块中,让操作符也变成动态的!请尽情发挥你的想象力,只 要保证将MDD作为设计的基础,你可以找到各种方式去改进你的DSL。

   
2249 次浏览       25
相关文章

用户故事与用例
交互设计师之精益画布篇
数据分析之用户画像方法与实践
如何快速建立用户模型?
 
相关文档

用户界面设计
给企业做大数据精准用户画像
用户体验和交互设计
大数据下的用户画像
相关课程

用户体验&界面设计
用户体验、易用性测试与评估
用户研究与用户建模
用户体验的软件UI设计最佳实践
最新活动计划
LLM大模型应用与项目构建 12-26[特惠]
QT应用开发 11-21[线上]
C++高级编程 11-27[北京]
业务建模&领域驱动设计 11-15[北京]
用户研究与用户建模 11-21[北京]
SysML和EA进行系统设计建模 11-28[北京]

从手机登录页面设计想到的
如何把无意识引入交互设计中
交互设计的真相
当视觉设计师遇上产品经理
手机交互设计原则
用户体验之网页板块设计
更多...   

以用户为中心的设计
可用性评估
Desktop及Web-based视觉设计
认知原理与设计应用
手机用户界面设计

北京 以用户为中心的界面设计
北京 用户体验& 界面设计
上海 华为 用户体验& 界面设计
深圳 用户体验& 界面设计
爱立信 以用户为中心的设计
北京 用户体验与界面设计
福州 以用户为中心的界面设计
更多...