『Reprint』 复杂表单应用解耦,淘宝机票订单实践

背景

在web应用中,复杂表单这类web应用富交互元素多,业务逻辑复杂,犬牙交错,且需求变化频繁。及容易成为晦涩和幽暗之地,也经常是各种代码坏味道的来源。针对这种典型的复杂应用,本文以淘宝机票订单为例提出一种架构模式梳理和消化表单带来的复杂性。

模块和组件划分

解决复杂表单的的第一步,划分模块。

概念上,为了复用和解耦方便,应将模块按照功能的内聚程度进行划分。强相关,频繁沟通和交互的功能应该归为一个模块。模块间尽量不存在依赖关系。也就是常说的“高内聚,低耦合”。
如下图所示,淘宝机票订单页面主要有被分为7个主要模块。

模块划分完毕,下一步确认组成模块的组件。
关于模块和组件的区分。一般按照以下三个纬度考量。

  • 是否有业务逻辑参与。
  • 是否包含html。
  • 是否具备一定独立性。

模块”,定义为一个包含”html”、”css(图片被认为是css的一部分)“、”javascript”的代码集。模块的 应用方式多为通过web模板技术(如:velocity、freemarker、php)。因为包含了html,使得模块必须通过服务端合并加载并且最终 推送到用户浏览器。此外,“模块”还是具备一定独立业务和交互的集合,最好可以被其他页面引用。良好的独立性也可以帮助协同开发,在实际开发中可多人可以 并行开发多个独立模块,提高效率。

组件”,定义为一个仅包含”css”和”javascript”的代码集。正因为不包含html,所以组件可通过 javascript异步加载。因为这种可异步加载的特性,组件在复用方面的容易性远超模块。组件没有业务逻辑或者仅有少部分公共业务逻辑。业务逻辑越 多,组件的可复用性就越低。

模块、组件间通讯

组件/模块划分的目的是将彼此间相对独立的功能分离,前面通过模块和组件的划分解决了分离问题。实际中,模块之间存在协作关系。模块间应以一种轻量的方式协作。一般的为了更好的分离和解耦,可以考虑用广播的方式在模块间沟通,考虑使用事件的方式在组件间通讯。

如下图所示,淘宝机票订单页面的数据流向。

不同模块在后期均有可能扩展小功能。例如不定期的活动优惠等。事件广播可以让不同模块/组件间新增功能影响面缩小。在淘宝机票订单中应用中,使用广播组件通讯主要用来完成以下意图。

1、知会。
  知会的特性在于异步通讯。广播发起方只需要放出事件,无需等待其他关注者完成处理。称为异步广播。例如表单模块的内容变更需要知会到显示订单金额的模块,显示订单金额的模块接受事件后需要更改金额。
  基于这种方式的通讯,各模块之需要做好自己的事情,外部关注的事件广播出去即可。异步广播还有一个好处是系统坚固性比较强,广播发送者不会因为事件监听者的使用不当而异常。

2、请求数据
  例如,模块6(负责提交)需要在被点击后从模块2(乘机人表单),模块4(联系地址)、模块7(金额计算)。获取具体数据提交。请求数据的场景特性在于,广播发起者需要等待事件处理者完成处理后再继续下一步行为。称为同步广播。

基于此机制。提交模块只需要负责综合校验,浮层,网络请求及异常处理。而具体请求的内容由其他模块决定。对后续模块的扩充起到了很好的左右。

复杂组件拆分

模块和组件划分完毕后,可能会发现某些组件非常复杂,几乎占据了整个web应用一半以上的代码。这部分组件由纯js实现,并且使用javascript模块加载器加载。
同一个组件大量代码纠结在一起,最终还是会导致架构腐化。因此,复杂组件需要进一步拆分。在淘宝机票订单中,乘机人信息组件是一个复杂组件。如下图所示:

拆分这类输入型的复杂组件,一般来说有两种思路方式。

  1. 纵切,组件树型式。
    将组件进一步划分为更细力度的输入组件,将每个输入域作为一个单独组件。最终形成一个组件树。

这样的组织方式结构严谨层级清晰,最大的优点是很容易支持字段扩展。
但考虑如下场景,为了尽量友好的提示用户,需要在输入域外的某处增加提示帮助。

这种场景下组件树的组织方式每次在面对变化时就会略显手忙脚乱。难道把每个地方出现的tip都座位独立组件看待吗?
字段级的适普性降低了适应细节调整的能力,付出的代价在于界面体验。

  1. 横切,AOP式。
    将所有输入域抽象的看待为同一个组件。按照组件的富应用特性分层看待。在本例中,乘机人组件被按照从简单到复杂分为3个切面。

切面1-基础展现层只负责最基础的可完成输入的表单控件,及基础dom管理。
切面2-富展现层负责修饰base层的基础html控件,形成富输入控件。
切面3-校验层负责对base层的输入数据进行业务级校验。
未来,如果新增tip或者其他业务逻辑,增加一个新切面即可,完全或者很少需要修改老代码文件。

淘宝机票订单采用了AOP这种方式,从最终代码量上来看,可以看出复杂度被比较均衡的分布到不同文件中去。

同样,这种方式也有局限,如果需要扩展字段,那将是一个灾难,你有可能需要到每一个切面里面去做修改。

有句老话说的好,没有最优方案,只有最适合的解决方案,任何解决方案,都需要放到具体场景中去评判。事实上,对这个问题的进一步研究,可以发现以下规律。

对于一个组件、模块,同时追求简单设计、适普性(字段级扩充)、界面体验是不可能的。如果场景需要适应字段灵活扩展,那就采用纵切的模式。如果使用 场景需字段确定,需要更多细节控制力度,那就横切,AOP式。如果两者都要兼顾,就需要引入复杂设计,综合运用横切和纵切。但是这样形成的最终设计会很复 杂,开发和可维护性上会有代价付出。

对于淘宝机票这类互联网应用,使用了横切的方式来拆分组件,因为在这个场景中,字段的数量是相对固定的,而围绕固定数量字段的优化需求是层出不穷的。然而在企业内网应用或者网站后台web应用中,字段的变化会比较频繁。建议主要采用纵切的思路划分。

表单校验

有表单的地方就有校验。项目初期,校验的功能总是不起眼。等待项目后期时候经常会发现校验已经占据了巨大工作量并且成为海量bug的源头。因此校验是一种典型的容易被轻视单又蕴含巨大工作量的事情,需要特别对待,专门设计。

一般来说,这根据校验根据其复杂度可以分为以下两类:

  1. 格式校验
    格式校验一般是校验用户输入的格式是否满足要求,比如是否数字、电话号码、邮箱等等。此类校验的特点是校验域单一,一般只对一个input或者某个组件的 value进行检查。格式类校验应与与用户展现非常接近,一种非常好的做法是将此类校验信息直接描述在html标签属性中。html5中input的 pattern属性就是一种基于这种思想的解决方案。
  2. 逻辑校验
    逻辑校验是满足格式校验后,继续进行的与业务相关的校验,例如是否存在相同用户名,输入的生日是否和身份证号不符等等。此类校验的一般涉及多个输入域,要综合处用户的输入内容一起校验。此类校验逻辑复杂,不适合写在html中。

目前有很多流行的form校验框架解决校验问题,如何引入合适的校验框架,先从理解校验这件事的过程开始。
典型的一个校验过程如下,用户在某个input处完成输入,应用在某个时刻被触发校验,可以是失去焦点或者keyup或者其他。被触发的校验过程找到此处 input所需要的校验规则(有时候这个规则被直接写在html中)判单正确与否,如果正确,可能有提示,如果错误,可能也有提示。
从以上场景的描述中,可以找到校验的几个关键环节。这里局部采用一下管理学上经典的5w1h问题分析方法来分析问题

  • who: 哪个输入控件的内容需要校验。这是框架是解决不了的。要对哪个输入域做校验应该是应用传递进入的。
  • when: 何时被触发校验。比如说是“who”失去焦点时。变化太多,框架解决不了。只能被动触发。
  • what: 做什么校验。有时候这个”what”被写在html中。基本上,所有格式校验都是固定的,这个问题应框架解决。但框架应预留接口做更加复杂的业务校验。
  • how: 校验完毕后的动作。框架不能决定做什么,但是在校验结果出来后,框架应能知会到外部调用者。

在设计框架或者选择已有框架时,首先要区分框架的边界,简单来说,就是做什么和不做什么。框架应实现相对固定的业务流程。同时对可变部分预留足够的灵活性。

一个通用的校验框架一定是不含界面部分的。界面是多变和难以穷举的,是用tip显示错误,还是在输入域附近显示,是否需要动画,是否需要修改输入域 的视觉状态,这些可变化的部分应为框架外部内容,由更专业的tip组件或者popup来完成。框架只应该负责在校验完成时候知会相关组件完成显示错误提示 等若干事情。

基于以上的分析,校验框架应该具备以下规格
1. 解决what问题。内置了各种格式校验规则,如电话号码、e-mail等.并且能够灵活定义新的逻辑校验。
2. 解决who问题。说明如何根据输入的字符真正找到who对应的value。并且能够对于这个who使用哪些校验规则
3. 解决when问题。提供一个触发校验的方法。
4. 解决how问题。产生校验结果后能够知会外部的功能框架。

在淘宝机票订单应用中,依据上述原则自行设计了一个Validator框架,接口定义如下,Validator是校验框架对象。

  1. 在构造函数中提供表格化的校验逻辑定义型式。如下图所示,传递如下结构,定义每个字段对应的校验方式。在下图中,定义每行为一个field,每个field有若干rule,每个rule可以是框架内置的格式校验,也可以是自定义的逻辑校验,实际上是函数名。

  2. Validator框架提供validate()方法,validate方法有两个行为,如果不指定参数,将依次执行完所有field的校验,并且将最终结果返回。如果执行一个field name,框架将只校验field name对应的输入域。
  3. 一旦执行validate()方法,无论校验结果如何,框架均向其观察者发送事件’onValidate’。以便触发后续动作。
  4. 一些辅助参数,需要提供一个从field name找到输入域value的function。

总结

在处理复杂表单时,首先通过合理模块、组件划分,将复杂度分散。然后利用详细和广播机制解决分散的模块和组件间通问题。接着,过于复杂的组件要考虑进一步拆分,具体拆分的方式有纵切和横切两种,根据具体使用场景决定。最后,不要小看了校验,需要特别对待,专门设计。

在新浪微博关注我:@魏凡哲-陶清

时间: 2024-08-09 14:48:38

『Reprint』 复杂表单应用解耦,淘宝机票订单实践的相关文章

怎样在快递单软件导入淘宝订单

1.登陆淘宝账号2.在淘宝页面中点击"我是卖家" 选择点击"已卖出的宝贝" 选择点击"选择等待发货"点击" 批量导出".3.选择点击"生成报表".4.然后在弹出的窗口中点击"下载订单报表".5.登陆快打KD118快递单打印软件的账号6.点击"快递单打印"进入快递单打印页面,点击"导入"然后导入淘宝订单7.选择点击"浏览" 并且添

『ORACLE』 创建表(11g)

CREATE TABLE语句 create table [schema.] 表名 (column datatype [DEFAULT expr][, ...]); 在插入的过程中,为列指定一个默认值 ...hire_date DATE DEFAULT SYSDATE,... 默认值必须满足列的数据类型定义 create table hire_dates (id number(8),hire_date date default sysdate); create table dept (deptno

『ORACLE』 永久表空间创建设置(11g)

SQL> create tablespace ML01 datafile '/u01/app/oracle/oradata/enmo1/ML.dbf' size 15M autoextend on maxsize 25M; Tablespace created. SQL> alter database datafile '/u01/app/oracle/oradata/enmo1/ML.dbf' autoextend on next 2M;

淘宝刷单兼职靠谱吗?刷单平台哪个好?

淘宝刷单兼职靠谱吗?刷单平台哪个好?很多人感觉淘宝刷单兼职是一个非常神秘的行业,他们都觉得是一个灰色行业,就比如我上次给我的一个亲戚说起了淘宝刷单兼职,一开始我给他说每天能赚一百以上的时候他觉得很有兴趣,当我又给他说怎么做的时候他竟然觉得是一个类似传销的组织,我哈哈的大笑起来,其实淘宝刷单兼职是一个非常正规的行业,淘宝刷单兼职既然存在了这么长时间,肯定会有它存在这么长时间的理由. 并且以后淘宝刷单兼职一直会存在下去,或许应该这样说,只要网络购物平台不会倒闭,淘宝刷单兼职就不会失败,就会一直做下去

淘宝刷单系统搭建教程

做电商运营的都知道,为了提高销量加大浏览量提高转化效果,通常会采用很多方法,比如精确推广直通车,淘客返利,还有淘宝刷单/补单/放单等等.合理自然的刷单补单可以把店铺基础数据提升起来,不然做直通车或其他的除了效果无法把控还要一直投入,所以很多淘宝商家会通过补单来去平衡店铺运营. 本人团队有多年的淘宝刷单系统搭建经验,后期也开发过几个上线的淘宝刷单系统 其实淘宝刷单系统搭建并不难,接下来就和大家分享一下淘宝刷单系统搭建教程. 淘宝刷单系统搭建分四大模块: 1.淘宝刷单系统-总管理后台 资金管理.试客

如何解耦业务表单与流程

一.问题说明 使用工作流的系统,例如OA,ERP等,都会遇到当流程到达某些环节的时候,业务表单的某些字段有值的验证要求或只在该环节显示.隐藏. 二.解决方案 1.一般方案 在业务表单里使用流程环节的特征信息来对这些字段作控制,属于硬编码的方式,业务表单与流程的耦合度太高,污染了表单代码,会有点乱.可读性.可维护性.扩展性都会大大降低. 2.更好的方案 1).环节移动相关的 当前节点上的操作和数据决定了下一步怎么走,所以业务表单里的某些字段可能要拿来作判断的或临时提供一个选择器给用户,由用户来决定

『ENGLISH』

以A字母开头的词汇 英文 中文 abstract module 抽象模组 access 访问.存取 access control 存取控制 access control information 存取控制资讯 access mechanism 存取机制 access rights 存取权限 accessibility 无障碍性 accessibility information 无障碍网页资讯 accessibility problem 无障碍网页问题 accessible 无障碍的 access

谈谈前端『新』技术

技术这个行当,永远会有新东西出来,不进则退.更关键的是,前端比起整个软件工程乃至计算机科学体系来说,是个相对新生草莽的领域,近年来前端生态的发展其实都是在向其他领域吸收和学习,不论是开发理念.工程实践还是平台本身(规范.浏览器).所谓的『根正苗红』的前端,不过是整个发展进程中探索的一个阶段而已,那个时代的最佳实践,很多到今天都已经不再适用.过往的经验固然有价值,但这些经验如果不结合对新事物本身的了解,就很难产生正确的判断.这里需要强调的是,学习新事物并不是为了不考虑实际需求的滥用,而是为了获取足

php 下的表单传值

今天在做表单的时候,因为是手机网站的(老板说手机网站的表单一律用get方式传值)『因为在手机端post方式的传值会出错』(这个我等下再自己验证下) 重点是表单标签的<form action="?act=pro">这个action是可以在提交时自动在传参之前加上action内的str的,而我在定义action="?act=sch" mothed="get"方式时 地址栏怎么也获取不到act=pro.问过老板才知道get传值,action