最近在作一图(GIS)文(MIS)一体化办公系统的架构,在这里将其架构设计写出来,供大家板砖板砖。系统采用DotNet作为其开发语言,C/S结构,分多层开发模式。以下部分为系统在进行详细分析前所作的架构。
一、大框架
三层结构,界面层,逻辑层,数据层。其中界面层命名为:UI,逻辑层又分为几层,业务外观(BusinessFacadeProjects)、业务规则(BusinessRuleProjects)、数据访问(DataAccessProjects)三层,其实算起来,系统已经不是严格的三层模式了,已经算作是多层系统了。
设计思想说明:界面层与业务外观层进入数据交互,业务外观允许对业务规则或者数据访问层进行数据交互,业务规则进允许对数据访问层进行交互。也就是说用户所对界面进行的所有操作,都先通过业务外观层进入,不管逻辑层如何处理,最后都是通过数据访问层与数据库进行交互,同理,不管要从数据库取任何数据,先通过数据访问层再经过逻辑层的处理,再从业务外观层“丢”给界面层。为何如此设计?多层架构思想在这里就不多说了,本人在这里考虑的是C/S与B/S的统一,各种数据库的统一,例如,如果我的系统是C/S,我想改为B/S,那么由于有业务外观层统一为数据“入口”,那么我要改的东西仅仅是添加B/S界面层的代码;又,如果你把你的数据库从Sql Server改为Oracle,那么你只要导入数据库,然后改一下数据访问层的代码,就可以了,当然了,这里的前提是你的所有Sql语句都得写在数据访问层上。
二、层架设
1.界面层:这个没有什么好说的,UI就是一个项目(工程?包?)管它怎么叫,反正编译出来就一Exe文件。
2.业务外观:为了方便起见,在引用的时候只要引用一个则可,所以就一个工程BusinessFacade,当然里面你可以包含多个类,然后在UI里面using或者Import。
3.业务规则:这里分多个工程,一个为数据处理,一个为工作流引擎,一个为字符处理,另一个为项目规则定义。
数据处理我们命名为DataOption,这里的意思是只要是与数据有关的,且要进行相应处理的,我们就先经过这里处理一下。
工作流引擎命名为WorkFlow,由于太大块,也太复杂,有时间再分出来讨论。
字符处理在这里命名为StringFormat,这一个工程是用来干什么的呢?其实很简单的,比如将一些Sql语句拆散了再组合,又或者将一些字符串经过一些特定的处理再返回,这些东西在大系统里面经常用到,所以在这里把它分出来,另外编译成一Dll,也方便以后重用。当然在后面的开发中,要求程序员要有正则表达式的相关知识。
项目规则命名为ProjectRule,又是什么东东呢?这里只要是对一些数据进行标准化的处理规则,比如,我们要生成一个列表页,一般是返回一个DataTable,然后绑定到DataGrid中,当然,如果你仅仅有DataTable,在UI上你就分不清每列代表什么东西,除非你的DataTable命名非常规范。所以在这里你还得返回一个Title(列头组合),为了美观,你当然还得返回Width(列宽)的集合,然后在UI里地这些东西进行处理,让用户看起来是一个可读的且美观的列表。这些东西既然都是通用的,你为什么不把他们再定义呢?也就是说把Title,Width,DataTable定义为一个类,比如定义为DataList,里面包括一DataTable,两个字符串数组,我们以这个类为标准进行数据交互,那么UI层的开发人员,你就不用管别的层是如何开发的,总之你的处理就是按这个标准进行开发,同时,数据处理层的人员也是,不管你UI层如何搞,我只按这个标准生成数据集合,如此一来整个系统的数据列表的开发就统一起来了。同时由于有了这个标准,每一层的开发人员都最大限度地考虑了在这个标准的基础上可能出现的错误处理,可以使整个系统容错性更好更健壮。当然在这里不仅仅是一个DataList,还要分很多的“Rule”,只是在这里暂时就不一一列出了。
4.数据访问:分两个工程,一个工程只对数据库进行直接的操作,提供各种各样的接口,我把它命名为DataBaseOption。另一个不进行对数据的直接操作,只是所有的与Sql语句有关的都放在这里,然后通过DataBaseOption进行操作数据库,我在这里命名为DataAccess。把这一层分离出来的好处是,如果客户突然间想把系统改为另一种数据库进行数据的存储,那么你只要改DataAccess的代码及DataBaseOption的少量代码则可全盘搞定。
三、层详细架设
1.UI界面层:界面层大概主要由表单,菜单,按钮等组成,当然这些东西还有统一的样式。然后用户对数据的操作能做的大体就是添加删除修改等功能,由于是在详细分析前,所以当然别的不通用的功能在这里暂时先不讨论。由于以上部分是通用的,所以在这里我们先做一个基类,在这里,我们命名基类时前面带Base字样,一般的Form我们前面带Frm,这个基类的命名则为BaseForm.vb/BaseForm.cs,它主要的功能是定义通用的数据操作及界面样式生成,例如此系统要实现类似于Office的样式功能,且从2000-2003是可选择的,那么这些处理的方法就定义在这个Form里面,当用户改变样式时,它的任务是对每一个MDI子窗口的样式改为原先定义好的样式。所以,在这里我们得定义一个void或者sub类型的方法:
public void SystemStyle(enum office)
{
这里包括窗体框架的样式和窗体内输入表单的样式。
}
office枚举为2000/XP/2003/default,至于方法中如何处理,我们在这里暂时不管它,留给编码者思考。 为了在MDI子窗体中能实现统一的删除/修改/添加功能,在这里我们在基类里面定义添加/编辑/删除三个方法,且这三个方法在继承后是可以override的,也就是说,为了防止在父窗体中点击添加/修改/删除按钮都不会出问题,我们必须所有的子窗口都应该从这里继承,为了通用起见,在这里的BaseForm中必须是没有操作的,也就是说仅仅是一个空的方法体。
/// <summary>
/// 保存,可重写
/// </summary>
public virtual void Save()
{
}
/// <summary>
/// 删除,可重写
/// </summary>
public virtual void Delete()
{
}
/// <summary>
/// 添加新的东东
/// </summary>
public virtual void AddNew()
{
}
在这个系统中,我所定义的BaseForm.cs原则是所有的窗体都应该继承的基类,但是所有的窗体中又可以再分出类来,所以在这个基础上,我们得再写基类。比如,在系统中,输入表单的Form占了系统的很大一部分,所以,在这里我得给它们抽象出一个比较通用的操作方法,所以在这里定义了一个BaseInputForm.cs,这个类用来干什么呢?我在这里的设想是只要开发人员在开发输入窗体时,主要继承了这个类,那么他的工作就是设置一个TableName属性为相对应的数据表名,把须要的控件拖进窗体,把控件的Name属性设置为相应的字段名,则可实现数据的添加/更新/删除。为了能达到这个功能,在这里先继承了BaseForm.cs,然后定义一个Hashtable,key用来保存输入表单的Name属性值,Value则保存表单的Text/Value值。
一个Hashtable,我们如何才能把当前窗体的输入表单值全部保存进去呢?这个就得写一个递归的循环当前窗体的所有控件的方法了。因为窗体的控件不仅仅是可以输入的,所以在这里还得写方法筛选,代码如何写,在这里就不给出了。 在这里,TableName是一个属性,这个属性我们用来保存当前被操作的数据表的表名,同时我们还得到了一个带有字段名及对应该输入值的Hashtable,所以在这里我们得在业务规则(BusinessRuleProjects)层写一个规则,这个规则包括一string字段(表名),一Hashtable字段(字段及对应值),一enum字段(添加/修改/删除),然后我们的各层就通过这个规则作数据交互。例如,点击保存按钮->Save()->构造规则->调用相应该的数据访问层的方法,实现数据更新。
这个Form除了定义表单外,我们还得定义按钮(Menu,Bar等)的自动生成,在这里如果想实现与权限的对应,就得把按钮的信息写在数据库里,然后根据不同的登录名不同的权限,生成不同的对应该操作菜单,在这里由于时间限制,不作详细讨论了。
有了以上的东东之后,那么我们的程序员在设计输入表单的时候,主要实现排版及设置好Name属性后就一切OK,则于BaseInputForm.cs继承了BaseForm.cs,所以在这里程序员根本就不用考虑样式,会自动处理(当然你的控件要支持多样式功能,VS自带的是不支持的,所以你得找一套支持的,如果你的水平够强,自己写一套也可,我在这里的C/S系统就是用的一套国外开发的,而B/S的则是本人自己开发)。也不用考虑如何添加进数据库的问题,因为这些问题不是属于界面层的问题,至于此部分的设计,以后有时间再作分析。
除了输入表单的基类外,我们还考虑了一个列表页的基础,这个基类在这里不作详细说明了,其规则为上面ProjectRule的例子说明。
(明天要出差去了,以下部分不知何时才有时间补上。由于“你的砖头就是我的动力!”,所以,如果砖头来得比较猛烈的话,估计小梦在出差期间只好再“砌砖”了)。