来源:HIT专家网 作者:解放军总医院计算机室 郭旭
“军字一号”自1995年立项至今已走过20余年,目前仍有上千家医院在使用,可以说它在医疗信息化水平的提升和行业人才培养等方面发挥了重要作用。
随着医院各项管理不断具体与精细,医疗业务流程不断改进,外围系统不断丰富,新业务、新技术不断涌现,我们也在不断地为“军字一号”系统增加功能、优化流程、完补漏洞,持续满足各个部门提出的新需求。但是,在信息化水平不断提高的同时,IT环境也变得日益庞大复杂,现行系统在很多方面已经面临瓶颈,这又对我们提出了新的挑战。
对该系统的再认识
“军字一号”的超强生命力在于其需求清晰、架构合理、数据规范、平台开放、交互友好,但随着应用的不断发展,目前也面临着不少迫切需要解决的问题,现就以下几方面与同行们讨论。
1. 整体架构与可扩展性
“军字一号”采用客户端-服务器(C/S)的两层架构,其优点是:系统结构简单、程序开发速度快、执行效率高,不足之处是用户界面紧密耦合于物理数据设计,这意味着只能使用限定的数据库,并且客户端数量的增长直接导致数据库承受更大的压力,使得系统的横向可扩展性受到制约。HIS对于CPU处理和本地IO的要求并不高,主要工作在于后台数据库的交互,因此,在医院IT规模不断增大后,大量的客户端就对数据库的性能提出了很高的要求。因此,在客户端数量较少、业务不甚复杂的情况下,两层架构是一个很好的解决方案,但时至今日,两层架构已经不能充分满足需求。
2. 系统的开发工具
“军字一号”系统设计之初正是Power Builder(PB)、Visual Basic(VB)与Delphi等编程语言流行之时。PB作为一款重量级开发工具,提供了数据可视化的功能,支持面向对象,支持原生SQL语句,提供了超高的数据库开发效率,选择PB作为开发语言在一定程度上对医院信息系统的成功与普及起到了很大的推动作用。现在,我们也应该看到PB的弱点:其网络开发功能比较弱、对OO的支持不够、多线程开发与调试的功能比较差、对于新的操作系统(特别是64位)支持不佳、对于代码智能提示等UI不够友好、对于源代码版本控制工具兼容性较差等。
3. 数据结构设计
“军字一号”使用Oracle数据库实现数据持久化,其数据结构设计具有以下几个特点。
第一,没有严格按照关系数据库的三范式进行设计,部分数据表不满足第三范式(3NF),即存在冗余字段。其中一部分冗余字段是执行业务过程中实际需要[1],一部分是为提高查询速度而设计的[2],还有一部分为了提高系统的容错性而设计[3]。
第二,使用有逻辑意义的一个或多个字段作为主键或联合主键。当数据变化或业务变更时,很有可能会造成主键失效,而变更主键则会给数据库系统带来较大风险与开销,并且目前大多数对象关系映射(Object Relation Mapping,ORM)工具不能很好地处理联合主键。
第三,在数据表中没有外键,完全从应用软件的层面进行数据一致性与完整性的控制。
第四,部分业务场景中使用了触发器。很大一部分原因是想保持原子系统代码不变的情况下,支持新上线的子系统,通过触发器的方式进行数据层的集成,如门诊挂号与分诊子系统间的数据集成,通过插入门诊挂号主记录触发分诊数据的生成。
第五,部分字段的长度或类型设置不合理,这个一般是由具体业务需求的变更造成的。
上述特点为“军字一号”打造了一个具备低关联性、低耦合性、高可用性和高容错性的数据框架。因此,我们认为,“军字一号”在升级的过程中必须沿用这个数据结构,甚至不惜以较大的代价实现完全兼容当前的数据结构(或者进行最小范围内的改动)。当然,触发器除外,当开发人员抱怨系统中大量的触发器的时候,这也是这个系统的瓶颈之一。
4. 软件的可复用性
随着HIS子系统不断增加,必然会出现越来越多的可复用的部分。“军字一号”最初就考虑到了这一点,设计了通用的PBL公用库,将大部分可复用的功能(如登录、输入法等)放到一个独立的运行库中,但是随着业务不断复杂,每个子系统都对公共库进行了一定程度的修改。此外,还有一部分业务上重合的需求,在子系统中是各自实现的,由于开发人员的沟通等问题,也并没有得到复用。代码级别的不规范的复用导致了系统的碎片化、数据的不一致性以及重复造轮子一般的效率低下。从软件系统发展的规律上看,大量的业务重用势必造成公共服务的出现。除了在数据库端使用存储过程外,“军字一号”的两层架构在面对大量业务重用时几乎束手无策。
5. 多用户的管理
“军字一号”在用户管理上完全依赖Oracle的管理机制,通过为用户在数据库中创建账号与授权进行访问控制。每个用户在登录应用程序时,都是使用自己的账号直接连接到数据库,这会造成数据库端需要保持较多的连接数,对数据库内存等资源消耗很大。使用工具软件直接登录数据库执行SQL语句,如不采取一定的防护措施,在数据安全性上也存在着较高的风险。
6. 系统集成与互操作
如何解决与外部系统的集成,“军字一号”并没有一个标准答案,因此,系统内部的数据操作基本上以同步的方式访问后台数据库,而系统内部的异步交互则是后期各医院或厂商在二次开发过程中自定义基于UDP的P2P消息、文本导入导出或数据库中间表的方式实现,外部系统获取数据基本依靠双方协商的数据格式与接口,通过定时或轮询的方式进行,导致系统效率低、服务器压力大,这也是HIS升级中必须考虑解决的问题之一。
系统升级的目标
具有“军字一号”特色的医院信息系统软件架构探索,既要兼容遗产系统,特别是最大程度地保证数据结构不变,又要支持新业务、新技术应用。我们认为,新系统应该实现以下几个目标。
第一,要完全保留或者最小程度地变更底层数据结构,实现对“军字一号”现有数据的完美兼容。首先,多年来积累的大量医疗数据是一笔巨大财富,不能轻易丢弃。其次,我们认为HIS的升级是一个逐步的过程,是各个新业务子系统逐个替代旧系统的过程。这是保证新旧两个系统能够顺利切换的方案之一,也是代价最小的方案。而对于系统架构设计者而言,在给定的数据结构上设计新系统,无异于“戴着脚镣跳舞”。我们认为,应当避免改动业务间共享的数据结构,可以对业务内独占的数据结构进行一定程度的变更,这样,这个业务系统上线时不会影响到其他子系统。
第二,新系统应该基于面向服务架构(Service Oriented Architecture,SOA)设计。在大型信息系统中,往往会面对大量的业务重用问题,业务重用必然导致公共服务的产生,而遵循业务驱动的SOA无疑是很好的架构方案。所以,HIS作为医院信息化的核心,应当架构在一个统一的平台上,内部有统一的服务接口,各个子系统间通过统一的接口(协议统一、数据统一)互相访问,对外提供多种服务接口进行集成。因此,有必要引入一个轻量级的企业服务总线(Enterprise Service Bus,ESB),HIS内部子系统间可以直接通过内部服务交互,也可以通过ESB交互,而HIS与外部系统间必须通过ESB进行交互。
第三,新系统应该是多层架构,并且应用层、领域(业务)层和数据层是彻底分离的。这样有利于分割重点、分而治之,更易于实现这样的分工:核心业务开发人员关注领域逻辑部分,梳理和优化业务规则与流程;数据库开发人员负责实现数据持久化接口,优化系统IO;前端开发人员关注用户界面开发,提升用户交互体验。每一层均基于接口进行设计,上层通过接口访问下层,接口可以根据实际情况实现,通过控制反转(Inversion of Control,IoC)的方式注入系统,达到灵活配置动态加载的目标。这样可以保证新系统能够支持多种数据库规格、多种数据结构、多种ORM工具等。
第四,要充分考虑上线后的运维工作。目前“军字一号”的软件维护工作,通常由若干运维工程师负责,运维工程师一般通过系统维护工具或直接登录数据库进行数据修正。在两层架构的系统中,这种维护方式简单有效,但放诸多层架构中,后台数据的修改往往会导致中间层缓存数据的错误,因此我们在设计系统架构时,必须充分考虑这一因素。
第五,新系统要继承“军字一号”高效友好的交互界面。“军字一号”系统对各种操作,特别是应用频繁的操作进行了一系列优化,诸如:挂号员普遍使用键盘操作,于是为所有操作设置了快捷键;在程序中设置了大量的业务提示、智能跳转、自动匹配等。这些宝贵的积累,在新系统中应当得到继承与发扬。
第六,新系统必须拥抱网络新技术。当前,大数据、云计算、“互联网+”、移动医疗和智慧医疗等新技术都是在互联网平台下慢慢发展壮大的,而且互联网中很多系统架构设计方案为我们提供了新思路,互联网上的众多开源组织与社区为也为我们提供了新工具,因此,我们要积极依靠互联网,不断吸收其思想,借鉴其经验,打造具备“军字一号”特色的新一代HIS。
系统架构的设计与实现
根据上文所述,我们设计了一个新的HIS,其整体架构如下图所示。
服务端是整个系统的核心,其架构借鉴了领域驱动设计(Domain-Driven Design,DDD)的思想,DDD的核心是消化特定业务领域的知识,并创建能够忠实反映该领域的软件模型。
服务端分为基础设施层、领域层和应用层,此外还有负责数据持久化的数据仓储层(上图中未画出)。基础设施层包括实体基础类、基本仓储接口和工作单元接口等;领域层包含领域模型、值对象、领域服务、仓储接口等;数据仓储层包括了工作单元、仓储的实现以及实体对象到关系数据库的映射。
领域模型是关于某个特定业务领域的软件模型,通常通过对象模型来实现,这些对象包括了数据与行为,并且能够表达准确的业务含义。
首先,实体(Entity)是具有唯一身份标识的领域模型,而值对象(Value Object)则不具有身份标识。一个实体的标识在其初始化的时候生成,并且无法更改,实体对象的其他属性无论发生什么变化,总可以通过该标识找到这个实体。具体到HIS中,可以将患者看作一个实体,那么患者主索引号(Patient ID)就是这个患者的唯一标识,当患者完成身份登记时(分配 ID),这个患者就被确定了。相同的,大多数情况下,可以将行政科室(Department)看作一个值对象,科室在初始化的时候就被赋予科室编码和科室名称等数据项,通常客户端不会对这些数据进行修改。但是,如果对于科室管理系统而言,就不能将科室看作值对象,而应将其作为以科室编码为唯一标识的实体。
其次,为了解决对象到数据库的映射问题,我们考察了Entity Framework、NHibernate、Castle ActiveRecord,以及轻量级的Dapper等,发现这些ORM工具在处理“军字一号”特色的数据结构时均有所不足。最终我们选择了Dapper和手写SQL混合的方式实现数据仓储层。
基本仓储接口包括对实体进行增删改查的声明,仓储接口则包括一些个性化的查询与处理实体数据的声明。而具体到患者仓储,首先应满足基本仓储接口:获取所有患者、获取指定ID的患者、删除指定ID的患者、新增患者、更新患者,同时也可以根据实际业务声明个性化的接口。对于数据库的连接驱动,我们选用了第三方的Devart.Data.Oracle组件,它支持64位操作系统,支持“军字一号”数据库的字符编码格式,支持Clob、Blob数据类型,支持以命名的方式传递SQL参数,支持配置数据库连接池。
在服务的编程开发环节中,采用统一的程序语言开发,能够促进开发团队在技术上进行更好地沟通。考虑到我院研发人员实际的技术熟练程度,服务端采用基于.Net框架的C#语言进行开发,目前服务可以承载于WCF或IIS上,下一步考虑使用第三方ServiceStack网络服务框架。
2. 企业服务总线(ESB)
ESB理论上应当包括以下功能:数据转换、协议转换、消息路由、服务管理以及其他相关功能,但是对于系统初期建设而言,它们太过重量级,我们考虑一个基于微软WCF技术的轻量级ESB,实际充当一个简单的服务网关(SOA Gateway)的角色,并且带有一定的数据转换功能。
初期我们不强制要求完全通过ESB进行服务访问,某些子系统之间可以直接访问,而对于外部系统而言,我们要求其必须通过ESB访问服务。
新系统的ESB支持三种服务访问模式:同步访问模式、异步访问模式和订阅发布模式。ESB服务接口规范在下文详细介绍。
3. 中间件(Middleware)
传统的ESB应该包含一部分中间件的功能,这里我们依然将中间件独立出来,主要是考虑到分解ESB的功能,实现ESB的轻量化。
新系统中主要需要的中间件包括缓存和消息队列。缓存是部署分布式服务需要的,通常用来存储需要在多个服务间共享的、频繁访问的或者不需要持久化的数据,如用户Session、公共基础数据字典以及一些中间状态的数据等。消息队列用在异步和响应时间较长的服务中,以达到系统间解耦的效果,比如在HIS中,有一些需要用户触发的批量处理(如生成门诊号表、收款员日结账等),用户在操作后无需等待服务完成,当服务结束时系统以消息的方式通知用户操作完成。
4. 服务接口
综合考虑技术发展,我们没有采用基于SOAP的Web Service,而是选择了更轻量级的基于HTTP的REST API。REST架构将“资源”作为提供给外部的数据,每个“资源”都有一个URI,通过URI定位到资源,并且声明操作方法(GET、POST、PUT、DELETE)就可以对其进行管理。而区别于传统的仅支持CURD的RESTful架构,我们需要的是基于SOA的REST架构,不仅能对HIS中的资源进行管理,而且能够以服务的方式处理用户请求。依据实际需要,我们设计了若干接口工具,此处不予细述。
5. 客户端
新系统的客户端不限定开发语言和运行平台,能够通过通用接口访问ESB服务的应用都可以作为系统的客户端。从部署与维护的工作量与难度上考虑,基于HTML5的网站或基于Web的应用应当成为客户端的首选,但是从医院实际情况来讲,一部分用户需要访问较多外设,大多数外设不能提供跨平台的驱动或SDK,出于安全的原因,通过Web的方式访问本地外设也存在诸多限制,因此我认为为此类用户提供的客户端首选基于Windows平台的桌面程序。
如果必须通过Web访问外设,有两种可行方案:其一,开发浏览器插件或扩展,如IE的ActiveX、Chrome和Firefox的Extension等,缺点是需要用户使用指定的浏览器,并且提前安装或注册插件;其二,可以在本地部署一个后台服务与外设进行通讯,然后浏览器通过WebSocket等方式访问该后台服务,这样就可以间接访问外设,缺点是要求浏览器必须支持HTML5,并且需要严格保证后台服务的稳定性。
通过基于SOA的架构,我们将客户端的开发工作完全独立了出来,这样就可以保证HIS掌握医院的核心业务流程,客户端和外围系统完全可以交由合作公司进行开发,现在很多医院与自助设备厂商、银行的合作开发方式就是这样的。
6. 安全认证
系统的安全认证包括两部分:其一是验证客户端访问服务的权限,其二是验证服务访问其它服务的权限。
虽然系统提供的服务大部分是无状态的,但对于客户端访问服务首先还是需要建立会话,用户将登录名、口令(通过可逆加密后的密文)发送给账号认证服务,会得到访问令牌(token)。用户在访问其它服务时必须使用自己的token。服务端将用户一次会话的token保存在分布式缓存中,并设置一个失效时间,后续用户访问其它服务时,通过token验证用户是否合法.当token有效时可以延长失效时间,当token失效时用户需要重新登录。
服务间的访问,我们采用了白名单的方式,信任来自指定MAC和IP地址的访问是合法的,不需要token认证。
7. 优化
(1)读写分离
领域模型在处理业务的过程中起到了至关重要的作用,但是大多数情况下,它包含了太多的业务逻辑与实现方法。我们考虑到用户访问的服务,要么是执行某种动作的命令,要么是返回某种结构的查询,因此,我们考虑将用户的查询(读)与命令(写)分别处理,这就是命令查询职责分离(Command Query Responsibility Segregation,CQRS)的思想。
(2)缓存
我们要兼容“军字一号”数据结构和维护模式,因此必须尽量少地在中间层使用缓存,但是在一些环节添加缓存无疑能够大幅度降低系统开销、提高效率。但对某些值对象的访问是需要缓存的,只是需要相关字典的维护程序在数据发生变化时通知系统更新缓存,如果维护程序没有这个功能,那么需要系统定时读取数据库刷新这部分缓存。
【小结】
本文回顾了“军字一号”的主要特点,列举了我院当前HIS面临的主要问题,说明了系统升级的必要性并给出了升级的目标要求,然后针对定目标要求提出了一个新的系统架构,满足升级过程中兼容“军字一号”现有数据结构与业务流程,保证新系统能够付诸实践,顺利上线。
我们认为,HIS的升级或切换都不是一个一步到位的过程,新业务系统在陆续上线的过程中,原有的业务系统不能停,新旧两个平台如何并行不悖,新旧数据如何完美兼容,在新系统的设计之时就应该充分考虑到。
【作者简介】郭旭,硕士,毕业于国防科大和清华大学,解放军总医院计算机室工程师。目前负责医院门诊挂号、银医系统的开发,主要兴趣方向是大型医院信息系统架构和虚拟化技术的研究与应用。
【注释】
[1] 患者主索引和门诊挂号主记录中都有费别字段,主索引中的费别表示患者建档时登记的费别,而门诊挂号主记录中的费别表示患者当次就诊的费别,两者并不一致。
[2] 患者主索引和门诊挂号主记录中都有姓名字段,逻辑上应该保证两者一致,患者不可能用不同的姓名挂号。门诊挂号主记录中添加这一字段,很大程度上是为了减少表关联,提高查询速度。
[3] 一些业务数据表同时记录了操作员的ID和姓名,是为了保证当操作员离职,其账号在数据库中被删除后,对应的用户ID已经无效,这时就可以从姓名得知当时的操作人。
【预告】下期的“HIS创新之路专栏”将发布河南省平舆县人民医院蔡磊同志撰写的《一个信息科长期望的医院信息系统》,诚恳务实地反映出了基层医院对新的信息系统的期望。
【责任编辑:谭啸】
评论前必须登录!
注册