如果说上一篇关于医疗信息可靠性的讨论还是残留了一些商业方面的味道(虽然名义上是要回归技术,但还是涉及到了一些业务方面的问题);那么,这篇打算把技术方面的牛角尖继续钻下去,讨论一下设计模式,而且:
- 要只是讨论集成领域设计模式好像还不过瘾,那就先从整个医疗行业软件的角度开个头,不过不包括嵌入式系统;
- 都是些常见的技术方案,未必是最优化的,也不会涉及任何商业机密,因为一些公开的出版物和网络社区里面都有相关描述。
- 对于这些技术方案,不会进行明确的分类,也不可能很全面,大都只是自己记忆中印象深刻的,这里毕竟只是随笔,不是论文。
需要注意的是,现在我们要考察的是这些信息系统的另外一面,这一面展现给我们的只是一些技术的抽象,并不会存在完整的业务图景。
--
在进入正题之前,还是忍不住想抱怨一下业界的浮躁,同时也是对自己的警戒。
最近去听了某位号称为软件架构大师的讲座,讲座之前看到讲义的时候我已经心存质疑,在现场听了5分钟,验证了这种质疑,讲座之后听到同事的议论,更加坚定了我的判断。不过我只做了5分钟的听众,不好妄加评论,只是感觉演讲中用到的比喻多到几乎掩盖了工程实践了。本来软件就是一个比喻泛滥的领域,从盖房子到拍电影,从飞机设计到汽车工厂,什么都拿来跟软件撤上关系。难道软件的地位真的就如此低下到要用这么多东西来撑门面吗?这回甚至把中国文化也扯进来了。
其实我听过的专门针对软件架构的讲座并不多,也没法做个全面的比较,只是感觉温昱老师还是把设计的理论和工程的实践结合得比较好的,也不乏启发性。如果要听中国文化,我还是非常推荐余秋雨的。如果某天余秋雨先生讲软件架构,我倒一定要去听听。
关于业界的浮躁,还有一个方面的表现,主要发生在销售人员和技术人员之间。这应该是程序员圈子里老生常谈的话题,这里还是忍不住想炒一下旧饭。总之销售人员总是觉得技术人员过于理想主义,技术人员又觉得销售人员太过现实,通常销售人员又比较能说会道,也比较aggressive,技术人员大部分都没这么能说,人也比较nice,这场争论的结果可想而知。
这种现象在中国也许会表现的更加明显一些,毕竟中国还是IT产业的低端。国外的情况不太清楚,但至少还有些高端的技术论坛,让技术人员自己也有个可以说话的地方。不过有个事实是大家公认的,西方文化更加尊重技术创新,东方文化更加注重人际交往。美国的不少大公司(Kodak,GE,MS等)的创始人首先都是技术人员,有些甚至是技术很牛的技术人员(很多内部资料可能会显示BillGates并不是个技术天才,那也是他转型成为商务人员之后的表现而已),正是这些人还是技术人员的时候创造了这些个商业平台,然后商务人员让这些平台最终取得了成功。
然而,在我曾经工作过的一个技术型的公司,技术和市场策略都是对的,甚至在国内是相对领先的,一些理念的正确性也不断地被行业的发展所证明,但是在实施具体的销售策略和方案的时候遇到了近乎疯狂的对手:这些对手向客户免费提供软件。注意,这跟互联网行业不同,这种免费并不是一种商业模式的创新,而是把整个市场变成一个低服务质量,高政治回报的地狱。这也就是很多销售人员整天跟我们谈论的现实。这些销售人员喜欢研究甲方组织内部人与人之间的关系,而不会去研究自己正在销售的技术将如何改变世界:而这种取舍正好创造了他们的骄人业绩,以及我们赖以活命的薪水。
还有一个有趣的现象,销售人员与IT Pro(运维人员)的一个共同点是对数字的极端敏感,前者特别注意货币的金额,后者特别注意设备的参数,这两种数字之间通常有着某种天然的映射,于是这两种人往往十分投缘。他们偶尔也会讨论技术,每每提到的时候,都会不屑于顾地说,嗨,这些都是程序员的活。
不过话说回来,上面说到的每一种人在每一个成功的组织里面都缺一不可,团队的作用就是就是让各种奇形怪状的人协同工作,最终达到1+1大于N的效果。
总结一下,每个人都应该把自己擅长的事情做到最好;每个人都应该尊重其他人的劳动,尤其是创新性的劳动。
--
我们还是先从前端应用开始,比如说各种工作站。
前不久我们要做一个支持可用性的技术方案,我查了一下SEI的书(虽然有点学院派的嫌疑),用以支持可用性的架构设计决策竟在一定程度上跟可修改性(即应用程序的灵活性)密切相关,其中的假设是在程序写好之后,用户和UI设计师还是会频繁地要求修改用户界面。回想一下,还真的蛮有道理。早期很多行业软件的用户界面,基本上是在客户化开发的阶段才能够定型的,于是一些公司也会让工程师在项目中的某个阶段常驻医院。尽管这种方式会一定程度上增加质量控制和项目管理的难度,但对于工程师来说,这是获得领域知识最好的途径。
我们熟知的MVC是用来支持可用性的最常见的设计模式之一,后来还发展到MVP和MVVM,它们的核心思想都没有改变,都是让界面和逻辑尽量地分离。我们看到的很多技术也是基于这种思想,比如MFC的文档视图模型,ASP.Net的Code Behind等。当然,也有一些界面设计方面的Best Practices,比如必要的支持信息、条理化的界面分区和Step By Step的操作等,这些通常属于详细设计时候要考虑的问题。
如果在可用性方面继续深究下去,就要走出电脑屏幕的框框,在医生的整个工作环境的角度来考虑了。比如很多影像诊断工作站提供了1+N的显示器组合;很多临床工作站则是在医生的办公桌里挖个洞,把显示器嵌在里面,医生则是趴在他早已习惯的一个玻璃板上工作。这就造成了一个有趣的结果,有些医生总是仰着头,有些则总是低着头。以前有个放射科医生还跟我抱怨,竖屏显示器太高了,看久了会得颈椎病,然而我们却没有办法卖给他一张更高的椅子。
如果说很多人还是喜欢把MVC当作一种架构模式,那么State模式应该是一个用于前端应用的典型设计模式之一了。尤其对于一些复杂的用户界面或者通信接口,比如影像工作站的看图界面上的鼠标控制,以及实现了DICOM协议的网络通信模块。这里就不往实现细节方面进一步展开了,总之用State模式来实现的状态机,一大好处就是当有新的状态,或者新的变迁要加入的时候,修改代码变得十分方便,其原理是用虚函数表取代了复杂的条件判断语句。而且State模式所提供的性能已经能够满足这个领域的需求了,在其他一些领域,比如嵌入式设备,也许面向对象的State模式会显得比较臃肿;在电脑游戏领域,有些人也许更加喜欢决策树,而不是状态机。提到用户界面,很多人还会想到command模式和strategy模式等,当然它们也很有用,很多时候可能我们一不小心就用上了。但事实上,undo的功能对于很多工作站来说似乎是一种噱头,我见过很多医生的操作习惯是,如果把图像调乱就直接按reset键回到初始状态,然后重新开始调。当然更多的时候,他们根本不愿意调,病人流量实在太大了,用默认的WW/WC或者曲线来看看就行了。医生毕竟是医生,不是平面设计师。
在以文字处理为主的HIS或者LIS工作站上,State模式也没有什么用武之处,也许这些前端应用更需要使用一些实现模式,而不是设计模式。比如在加载大规模数据的时候,使用后台线程,来实现界面的交互性,这里面也不存在太复杂的线程同步问题,只是需要注意GUI对象一般是不能跨线程访问的。因此,尽量避免多线程,是一条最基本的也是最难以遵守的设计原则。至少,如果是在窗口加载的阶段,可以把数据加载的工作放到一个timer事件里面,等零点几秒窗口画完之后,再来加载数据和不断更新界面,这样用户似乎会感觉好一点。但大多数其他的时候,后台线程还是必须的,而且现在双核的电脑越来越多了,不多开点线程还真有点过意不去。
在早期的应用中,MFC为界面程序中不同模块之间的交互提供了天生的便利,它主要是依赖Windows的消息循环,将交互的处理串行化,而不是象VB和WinForm这样,让程序员习惯于更多地进行直接和粗暴的同步调用。WPF则在这方面更进一步,它使用所谓的dispatcher来代替WinForm的Invoke,据说可以避免查找控件树从而实现更快的调用。现在我对WPF的了解还比较肤浅,只是听说它整个窗口,不管里面有多少个控件,都只占用一个句柄,并且允许开发者充分利用异步(即多线程)实现更好的交互性。从而避免了象WinForm那样,在运行长时间处理的时候,要调用DoEvent来处理界面响应。
以前我在使用WinForm的时候,就碰到过一个郁闷的问题,跟DoEvent有关。放射领域通常有病人、检查、序列、图像几级对象,在用户界面上通常需要用多级列表的方式来展现这些对象,我们一般不在医生操作的界面的过多的使用树型视图,因为它在操作大规模数据的时候,用起来没有列表来得简单。在这种分级列表上,一个常见的界面逻辑就是,在病人列表中选中一个病人的时候,在检查列表中显示这个病人所做过的检查,选中一个检查的时候又会刷新序列列表,依此类推。由于大多早期的PACS服务器都不支持多级查询,这些对象只能一级一级地展开,因此每次展开,即加载下一级数据的时候,都会发生一次网络通信,并且占用一点时间。为了在这段时间里面保持界面响应,我们最初的做法是,每加载到一个对象,就调用一次DoEvent,即把当前消息循环里面堆积的消息全部分发出去。这种囫囵吞枣的做法有一个严重的后果,如果用户在选中一个病人之后立即选用另外一个病人,那么这两个病人的检查就会以某种难以预料的方式重叠出现在检查列表中。因为你还没有处理完第一个病人的时候,调用了DoEvent,使得选中第二个病人的鼠标事件开始被处理了。
解决的办法很多。悲观的办法:就是不要使用DoEvent,在网络速度慢的时候让医生多忍受一下。无赖的办法:也还是不用DoEvent,而是让服务器支持多级查询,第一次查询的时候有点慢,展开的时候就很快。由于医生一般不会设定太宽泛的查询条件(一般都是近几天,或者指定ID),所以不会感觉到查询很慢。又由于大多PACS都会采用集中式存储,即采集到的图像都汇总到同一个技术比较先进的PACS服务器上,这种服务器一般都支持多级查询;如果医生非要把查询目的地指向另外的某个厂商的设备或者服务器,就可以顺便把皮球踢过去。真正负责任的办法:可以使用DoEvent,但是要用某种方式把列表上的鼠标选中事件排队一下,一个挨一个地处理,排队的方式很多,可以自己写,也可以使用线程池。有时.Net提供的MethodImplOptions.Synchronized锁定也可以当作一种简单易用的排队机制。
前面说了一些桌面应用方面的模式,好像我们也不应该忘记WEB。不过至今为止,区域医疗还没有普及起来,医疗信息化的重心还是在院内的企业级应用方面的。再加上医疗领域的用户界面大多交互功能都比较复杂,不管是医生写病历、下医嘱还是看影像,或者是登记员输入病人信息,象WEB这种基于无状态的基础设施来构建的用户界面,似乎难以胜任。在院内系统中,WEB通常用在几个方面,一个是让院长主任之类的人看看报表,或者是让其他部门的无关人等看看报告或者医嘱处理进度;另外一个则是让WEB来承载一些功能复杂的ActiveX控件,或者负责前端程序的部署,并美其名曰智能客户端,这些其实都是桌面应用的扩展了。
现在看来,SaaS和云计算的浪潮正在把传统的企业级应用往互联网上推,使得软件产业不得不在商业模式和技术架构上进行一次重大的调整(见前面几篇文章)。在这次浪潮中,前端应用领域的技术新秀应该非RIA莫属了。记得当时我在写“集成的故事-桌面集成”的时候,曾经表达过对ASP.Net的PostBack和Ajax的不同意见,至今为止,我还是坚持自己的看法,并且深切感觉到WPF回归DOM编程模型的良苦用心。这种多年来只是非主流地存在于浏览器端脚本中的实现模式(即标记式的界面展现脚本,以onclick之类的方式连接到实现功能的代码),也许将会成为我们下一代用户界面的基础。与这种实现模式形成完美呼应的,则是RIA这种部署模式,它把原来的富客户端程序host到浏览器中运行,它拥有本地内存,并且能够跟后台的服务进行丰富的交互,部署起来也比智能客户端更加轻量化。另一方面,RIA也会促使WEB回归到它的真正本源,PostBack和Ajax则会面临生存危机。
--
现在来看看后端应用。这里之所以用前端和后端,而不用服务器和客户端,作为区分,主要是因为前者似乎更多是指逻辑上的layer,后者更偏向于物理上的tier。而很多医疗应用,比如近几年很多国内PACS厂商赖以生存的单机版工作站和小型化的解决方案,tier的概念就远没有layer来得明显了。
不过不管是多大或者多小的PACS,只要我们考察的视线从前端的界面往后端的方向稍微推进一点,就必然会碰到一个共性的问题,即如何管理这些巨大的影像数据,它们是如此的巨大,以致于你必须考虑它的传输链条中的每一个环节,从显存,到内存,到磁盘,到网络,到服务器,到RAID,到光盘塔或者磁带库。因此,一个完整的PACS技术方案,通常由各个方面的专家通力协作完成的,包括显示器、服务器、网络、存储设备、打印设备、软件等。对于软件开发者来说,通常主要关注的是两个问题,一个是在前端界面上如何响应复杂的用户操作,并且根据这些操作的语义在显存、内存、磁盘和网络上管理这些图像;一个是在后端的传输链上,如何实现一个通常是由RIS的一些业务流程相关的信号驱动的图像预取逻辑,让图像智能化地在本机磁盘、网络、服务器的在线、近线和离线存储之间流动,并美其名曰让图找人而不是人找图。
有人曾经对PACS中的资源管理模式做过一些系统的研究,写成一本书叫做Pattern-Oriented Software Architecture: Patterns for Resource Management。遗憾的是我一直没有时间来仔细研读,好像记得里面有提到类似代理的模式,有效地管理了用户界面上的预览图和真实图像之间的关系。书中应该还有其他的模式,用到的时候再去看吧。还有一些简单的实现模式,倒是曾经用到过的,这些模式通常跟影像类型相关。一些早期的PACS都是跟设备捆绑在一起销售的,这些PACS通常叫做miniPACS,以区别于部门级和全院级的PACS。做普放设备起家的PACS厂商开发的工作站,用来打开CT/MR这些断层图像,有时会非常的慢。因为一个普放检查里面的图像通常不会不多,而且每张图又很大,医生觉得等个几秒钟似乎不是大问题,因此它们通常是把整个检查的图像加载上来,这样也便于应用悬挂协议;但对于断层图像,医生当然会希望加载到第一张图的时候就开始显示,然后可以边浏览后台边加载,全部加载完成之后再点击按钮来选择特定的悬挂协议,由于断层图像都很小,在界面上基本上不会感觉到任何的延迟。反过来,对于一些做断层图像出家的PACS厂商开发的工作站,在用鼠标对普放图像进行WW/WC调整的时候,有时也会非常的慢,感觉屏幕一抖一抖的,而且不管怎么调,相比起来还是没有其他工作站显示得好。事实上,其他工作站在用鼠标来调整WW/WC的时候,调节和显示的是一个分辨率稍微低一些的图片,这样在调节大幅图像的时候,用户响应会好一些,而且对于一些普放图像,用曲线(LUT)代替线性的WW/WC来做显示和调节,图像质量也许会好很多。另外,除了这些跟医疗领域密切相关的模式之外,只要是做图像显示和处理的程序,都会用到一些领域无关的实现模式,比如用双缓冲区来避免直接写屏导致的刷新时屏幕闪烁等。
另外,随着CT的排数越来越多,有时(比如在会诊的时候)在工作站上需要加载的图像的大小会远远超过物理内存,这时候就需要考虑如何管理超出物理内存的这部分数据(而不是理想化地通过基于关键对象的复杂流程来减少前端的压力)。对于一些没有显示出来的断层图像,暂时把它们从内存里释放掉(或者根本不加载),也许会比把它们留在虚拟内存(由操作系统来管理的一个磁盘区域)中会好一些,似乎文件系统的缓存也会帮助我们减少重新装载图像的时间。但这又会引发新的问题,对于暂时被释放掉的图像,如何保持它们的显示状态呢?对于一张就十几兆普放图像来说,文件系统的缓存能够保证足够的性能么?
最终,人们发现不同类型影像的加载和浏览方式之间的差异,以及不同医学领域的医生对同种类型的影像的使用方式之间的差异,实在难以调和。于是,我们只能做一些分解,降低一些业务无关的因素在技术实现上的耦合。具体点说,就是把不同类型的影像放到单独的模块中,每个模块有自己的加载、显示、处理、内存管理逻辑和特殊的浏览/报告工具。然后把这些模块以插件的方式组装到工作站的基础框架中。这样,在技术上,基础框架可以封装共用的模块,比如网络通信逻辑、以及前面提到的分级存储逻辑的前端实现,从而提高新功能的开发效率。在业务上,可以满足更多的图像显示和处理的要求,这样工作站就可在各种不同的领域,不光是放射科,还有骨科、外科手术室、会诊中心甚至特殊的诊所(比如牙科,或者是兽医科)等使用,不同领域的医护人员还可以使用专门为他们的领域而定制的模块。
不久前,当我看到正在讨论中的DICOM Supplement 118 (Application Hosting)的时候,实在感慨万千。记得几年前写过一篇文章An open architecture for medical image workstation,SPIE Conference 2005,说的也是类似的事情,而当时我们模仿的对象就是Syngo。
Application Hosting是个有趣的东西,它初看起来有点像CCOW。事实上,它更像是为图像工作站而特别设计的一种接口标准,让不同的厂商甚至可以在同一个进程内协同工作。如果它真的变成DICOM的第19章,对我们来说意味着什么呢?首先,主流的PACS厂商之间的竞争焦点将会集中到平台,而不是单个的产品。整体的解决方案也不再是几个产品的简单组合,而是从后台的云一直延伸到前端的插件框架的一体化平台,以及在这个平台上运行的各种下游厂商所提供的针对特定技术或者业务领域的模块。到时候,互联网上甚至还会出现一些象苹果的App Store那样的医学影像领域的模块集市,开发者和用户可以在上面直接进行交易,就像菜市场那样。
讲到这里,千篇一律的争论又开始了。现实主义者也许会问,这些东西是很好,但你的业务能活到这些东西能卖钱的那一天吗?理想主义者回应,好吧,你就用自以为足够的解决方案继续忽悠你的用户去吧,说不定哪一天,你就只能到菜市场上混了。随着技术的不断发展,这种争论会不断地继续下去,最终也不会有赢家。真正的赢家,根本就没有时间去跟你争。
当然,我还是愿意保守地相信传统的基于独立解决方案的商业模式和技术架构还会存在相当长的一段时间。尤其在国内,这种传统的模式甚至还没有完全成熟,单机工作站还在大行其道。在包括医疗在内的很多传统行业,人们甚至还在怀疑,软件真的值这么多钱吗,服务也能卖吗。
--
在一些复杂的分层架构里面,我们通常可以把界面逻辑和业务逻辑分开,前者处理和验证界面的交互,后者实现核心的业务流程。如果按照这样的定义,现在应该是让我们考察的重点进入业务逻辑的时候了。当然,在界面逻辑方面,除了PACS工作站上那些复杂的图像浏览和显示功能之外,还有其他一些基于文本处理的工作站(比如登记工作站,医生工作站等),它们也有很复杂的操作控制,以及信息显示和输入的逻辑,从技术实现的角度来看,似乎跟其他行业应用区别不大。但是,如果要说到可用性设计,医疗领域还是会有一些非常不一样的地方,比如对急诊流程的支持,如何使用模版提高医嘱、病历和报告编写的效率,如何防止医疗错误等,这里就暂时不深入了。其实,这些领域也可以挖掘出一些重要的设计模式和经验。
另外,有些技术发烧友还会说,界面逻辑和业务逻辑之间还可以有一个接口层,作为facade用来实现业务服务的封装。这层牛角尖我们就不必钻了,真正需要用到的时候再去考虑吧。
业务逻辑是信息系统的核心价值所在,但在我们看来,似乎只是系统需求的一个直接映射,没有太多技术含量。其实,这只是因为人们已经研究出了各种成熟的方法来实现它们。有些方法太过于成熟,以致于我们都感觉不到它们也可以总结为模式了。
一般来说,对于小型的系统,很多业务逻辑在存储过程里面实现都已经绰绰有余了,以致于它们看起来似乎没有太多技术含量,只不过是一些经过定制的数据库管理系统。我曾经看到某个放射科自己开发了一个基于Access的报告系统,它们自己建立一些窗体和查询(Access的术语,相当于带上用户界面的存储过程),甚至还通过文件共享的方式实现了分布式,于是也成就了某些人的博士论文或者高级职称。
稍微大一点的系统,有些业务逻辑就被放到应用程序的代码里面,并且出现了业务逻辑和数据访问逻辑的区分(后者主要由存储过程来完成),并美其名曰多层架构。事实上,当你深入考察其中的代码的时候,这种区分并不明显,业务逻辑还是散落在代码和存储过程里面。对于这一点,有些人(比如一些常驻医院的客户化工程师)认为放在存储过程中的逻辑修改起来很容易,为客户化提供了方便;有些人(比如一些项目管理规范和软件工程过程的忠实信徒)则认为存储过程的脚本难以进行管理,是潜在的trouble maker;有些人(比如数据库专家)则认为存储过程的存在增加了系统对数据库的依赖,不利于数据库的移植;有些人(比如编译语言的爱好者)则坚信存储过程一定可以极大地提升数据访问性能。众口难调啊。在那些热烈而吵杂的争论中,架构师必须做出理性的选择。
再大一点的系统,作为中间层的业务逻辑也许需要依赖于更加重量级的实现了。90年代,微软主打的信息系统架构叫做Windows DNA,中间层主要依赖于COM+。现在,包括DCOM在内的各种分布式技术已经被统一到WCF里面,WCF Service/WAS则取代了COM+/MTS,成为了.NET时代的对象容器。不管是那种容器,它的功能都是类似的,即为业务逻辑实现提供一些所谓企业级的服务质量,比如安全性、事务、消息、会话、单点登录等。当然,按照微软的风格,这些基础设施都是在不同的产品中实现的,它们有个统一的总称,叫做.NET企业服务器。
更大一点的系统,尤其是跨部门或者跨机构的系统,.NET企业服务器中的Biztalk Server就可以排上用场了。我们可以通过可视化的工具,在Biztalk上定义一些粗粒度的跨部门/机构的业务流程(当然,首先我们通常会理想化地按照SOA的风格,要求各个部门或者机构的系统基于业务职责来进行的粗粒度封装)。用医疗行业习惯的术语,Biztalk就是一个接口引擎,或者说是一个集成平台,其中还包含了HL7套件,用以实现医疗领域的企业应用集成。其实,医疗行业还有很多其他的接口引擎,有些甚至还是专门为医疗领域定制的。不过在国内,包括Biztalk在内的这些大型接口引擎应用得并不广泛,大部分的医院里,信息化的程度似乎还没有达到可以大规模应用中间件的时候。很多医院系统的集成还是停留在简单的信息转换和文档共享的层面上,通常都是由客户化工程师直接修改代码来直接访问第三方数据库;更先进一点的则是使用一些小型的接口引擎,与第三方系统进行基于HL7和DICOM的标准化通信,集成的工作可以通过配置而不是直接修改代码来实现,这种应用目前大都出现在标准化遵从做得比较好的放射领域。
在传统的企业级信息系统中,很多人会把集成分为信息/数据集成和业务/工作流集成两个阶段,前者相对比较简单,后者就有点BPM或者BPR的味道。事实上,据我目前所了解到的情况看来,二者在技术实现上并没有太大的差别,为了实现灵活的配置,两者都可以依赖于构建在工作流引擎基础上的路由机制(随着工作流技术的普及,有些复杂的业务系统内部也会使用工作流引擎来实现业务流程定制)。可能唯一不同的是,数据集成更多地关注复杂数据格式之间的转换,工作流集成则需要更多地考虑补偿/重试等复杂的工作流模型。然而,为了在分布式的数据流中保证数据的可靠传输,我们不可能中过多地使用传统的事务模型,那么补偿/重试等机制同样是必要的。不管怎么样,集成和互操作性已经被越来越多的认为是医疗信息系统的核心技术之一。
关于医疗集成方面的一些模式,前面的文章已经进行了大量的叙述,这里就不再重复了。如果还有什么值得强调的,那就是《企业集成模式》里面的一大堆东西,以及IHE Technical Framework了。后者为医疗集成提供的至今为止最好的业务模型描述。其实,不管是IHE还是HL7和DICOM的文档,都隐含了大量的业务层面的描述,它们不光可以作为集成服务工程师的工作手册,还可以作为医疗信息系统需求开发和系统设计的标准化基础。
--
终于,我们走到了信息系统软件的最后端,数据和数据访问层了。
很多人会认为这一层的实现技术,似乎是最没有医疗行业特征的一部分。因为不管那个行业领域的业务实体,最终都可以表达成关系模型,或者面向对象模型,然后进行存储和查询。这一层中的设计模式,不管是为了支持扩展性,还是为了实现更好的性能,都跟其他行业领域的模式大同小异。不过,这些模式应用的关键,还是在于对医疗业务的理解。我曾经看到有人在架构放射设备的控制台软件(由于需要强大的图像运算,这种控制软件一般运行在台式机而不是嵌入式系统上)的时候,竟然做出了在数据访问层中使用某种便于数据库移植的设计(即支持今天用SQLServer,明天用Oracle)。
当然,也有一些把其他行业的模式应用到医疗领域的正面例子。比如,要提供一个方案来让系统平稳度过医院里周期性的业务流量高峰。毫无疑问,从硬件投资方面来解决显然是一种巨大的浪费,常用的数据库性能调优方案里面也找不到什么自适应的策略,那么使用消息队列和异步处理也许是唯一的方式。再如,要提供一个方案来支持客户化阶段可以方便地增加字段,而不需要修改和重新编译代码。这个实现的方式也很多,归纳起来主要是两种策略,要么运行时动态增加列,要么把列访问转换成行访问。当然,后者可能更加适合于基于ORM的数据访问层。不管怎么样,这往往都是一个从数据库、数据访问直到用户界面的跨层的综合性方案,配合用户界面上的一些数据绑定策略,则更加如虎添翼。
事实上,后端系统的一些常见的设计目标,比如可靠性、性能和伸缩性、灾难备份和恢复,很大一部分是依靠硬件系统的选型来实现的,常见的一些术语有Web Farm、双机热备、负载均衡、RAID、NAS、SAN、光盘塔、磁带库等等。在软件设计方面通常只需要注意一点,那就是伸缩性。在传统的多视图架构中,这方面的设计通常是通过进程视图或者运行视图中的线程和进程的规划来体现。要使得系统具有scale-out的能力,我们在设计那些需要支持高吞吐量的后台服务的时候,一个常见的原则是根据业务处理的步骤来划分线程和进程,不同的步骤之间通过排队机制来实现异步调用,而永远不要根据请求来划分线程和进程;更好的实践则是充分利用线程池来减少进程创建和销毁的过程对系统资源的损耗。对于需要支持双机热备的业务处理,通常只能用进程而不是线程来作为宿主,并且把相关的状态保存到共享存储,比如RAID中。
--
最后,我还是想引用《特定领域应用框架:行业的框架体系》一书中的一段话作为总结。这本书关于医疗行业的介绍很少,只有一个所谓的“插页”,而且这段话里面提到的技术也许看起来有点过时,但是在我看来之所以可以作为一个总结,是因为它跟框架有关。
框架这个东西,也许是现在软件行业最重要和最流行的架构模式之一。跟传统的被比喻成积木或者预制板的组件和类库所不同的是,框架提供的是一个有地基,有梁有柱的半成品,甚至在这个半成品里面连一些常用的墙和屋顶都帮你预留好了,于是可以更大程度上的提高产品甚至产品线的开发效率。当然,唯一不变的还是软件行业里面强调得最多的,即重用。只不过组件和类库关注的是功能和代码的重用,框架则是关注逻辑和设计的重用。因此,面向特定行业的应用框架中的体现的设计要点,更能代表这个行业的软件设计中的最典型和最常用的模式和经验。
通常,一个框架的架构是可以分为多个纬度的,比如从前端到后端,即技术的纬度,或者针对不同的用户,即应用的纬度,或者针对不同的关键质量属性,即质量的纬度。下面引用的这段话,应该是在技术的纬度上展开的。
“面向消息的框架支持应用服务器(或数据库)端的消息定制和交换,它可以使用对象请求代理技术(如CORBA或ActiveX)来传送消息。面向服务的框架支持医疗应用服务的开发,这些服务可以充分利用对象请求代理体系结构和现有的分布式计算服务工具。可视集成框架重点处理桌面的自动化。这些框架随着市场的变化和技术的进步而不断地发展变化。”
补充一下,这里的三个框架,面向消息框架,面向服务框架和可视集成框架,分别被作者认为是适用于后端,中间层和前端的框架。正如作者所预言的那样,在今天看来,用来构建这些框架的技术已经得到长足的发展,正如前文所述。
--
回顾一下,从医疗集成的故事开篇到现在,也写了不少东西了。在这些不着边际的文字中,真正关于集成的并不多,因为一些正在进行的工作,是绝对不可能在这里说的。其实这里的自言自语,主要目的还是为了总结和整理一下自己的思路,尤其是对医疗行业软件开发的理解。毕竟这个行业领域太大了,目前有机会接触到的又太有限,加上自己记性比较差,生怕某些个细分领域几年没碰就忘得一干二净了。于是,能够积累下来的,有多少就算多少吧。