mis系统:浅谈MIS系统架构

  这里讲是对MIS系统架构些局部位置设计思路也是我个人想法不敢以偏概全不过包含了很多要素:权限、验证、流程、行为、结构、内容还有表示层如何和业务层分离

  写在前面

  这是对以前项目整理总结作为个在别人架构系统上写开发人员对系统架构提出新想法我想这没什么可争议

  我经历了两个业务需求相似项目个项目我看不到架构影子看到不少用工具根据数据库生成看起来很优秀代码花了几个月修改bug但最后项目还是黄了这次失败给每个人都上了堂课但我到公司时候负责架构人和多个开发人员已经不在公司了那时写代码工作已经接近尾声主要就是测试、改bug和报表我是和剩下起共事我到公司后就做报表用DevExpressControl控件做报表以致我对DevExpressXtraReport报表很熟悉的后在第 2个项目中负责报表并写了有关XtraReport报表篇随笔 做了个多月报表就去改bug了改bug很容易在我印象中发给我bug我都能改好并不会产生其他bug而且还对原有不合理进行了优化但遗憾是在的后几次演示中系统都没能完全毫无正确走完其中客户很挑剔有关这个我是理解如果我们去买房子当然希望房子好出乎我们想像项目无功收场很多同事离去了曾经大家起吃住欢笑日子不在有了特别是我们经理他离开让我很意外当初就是他面试我进公司面试时只是让我做个类似新闻发布系统我甚至没有做出来他看了下代码和我平时瞎捣鼓东西到会议室问我期望待遇约好时间我就来上班了他走时我送了本书给他现在元旦时他还会给大家发条祝福短信

  这次和剩下几个人和另组人进行了第 2个项目我从这边转到那边时架构已经做好了当时我当心架构会跟前个项目到了的后看到比前个大大改进了这个项目架构很好特别是界面层界面进行了统或者说是标准化了下排按钮整齐紧挨着排列每个按钮放在个lebel上设置这个lebel不可见后面按钮会紧挨着跟上来这点做太棒了它能给界面层设计带来很多启发这点是我没想到这个架构将很多操作定义好有时候像是定义死区别操作它会执行区别代码并传区别参数给个虚思路方法派生窗体重写这写虚访法完成对业务层访问(业务层往往是存储过程)负责某个模块开发人员要做工作就是:写好各操作存储过程;定义业务层对象;选择正确窗体基类(主要有带树形列表窗体、列表窗体和明细窗体)派生个新窗体在这个窗体中使用业务层对象和些公共对象做权限设置等化操作以及验证复写虚访法很多地方都是用虚访法如验证基类事件中去这些虚访法还有访问业务层对数据操作也是在虚访法中去访问基类会去这个虚访法开发人员要写很多语句并且大量代码都在界面层去做业务层显得及其单薄当然架构人员当初肯定不是这样想实际工作中开发人员在没有指导和参考例子情况下违背了架构人员初衷这个项目最后是成功

  学会整理总结争取每次架构比前个架构更进

  简单而繁琐工作——开发人员

  个好架构可以让开发人员有个赏心悦目体验他可以简单愉快面对繁杂业务问题将精力放在困难但能给人带来挑战性和成就感问题上但往往不是这样

  我们经常在化界面时要根据权限设置可见和可用用了语句在业务层也要根据权限判断是否继续执行接下来代码同样验证也是有语句每当写这样时脑子里要想到有那些东西要设置权限是可见还是可用是那些关系表达式组合生怕漏掉或是写错任何当发现时我们得看着长长而且相似代码时找不到位置虽然这些工作很简单但很快会有人说:“嗯这个地方需要改进

  给窗体设置内容时个Control控件选择在属性中设置各个参数特别是表格Control控件需要对每个列设置文本值各参数甚至还有事件代码中可能还有权限它需要会儿是复制张贴会儿是鼠标选择焦点切换来切换去会让思维停滞或转移区区零点几秒累计起来可是不小数字似乎我们很多代码不是写出来而是画出来除了直观没有其他好处这里不是否定可视化可视化操作也是产生代码只要把结构设置好内容是可以根据业务层数据自动完成

  还有操作我们要添加很多事件去业务层思路方法如果业务层没有你要思路方法就要去业务层加个似乎界面层上控制是事先知道业务层相应思路方法是做什么用耦合度问题?

  问题分析和解决

  对于MIS系统会遇到下几个东西:结构、内容、权限、验证、流程、操作和数据

  结构:好比框架区域

  内容:结构中内容般是Control控件

  权限:我们总是用分支语句来做权限上每个分支语句其实都可以看成个关系表达式或关系表达式组合就像“1>2”或“(1>2||3<8)&&99”经常是当前登录用户是否等于要处理业务对象中用户属性、当前登录用户是否有该操作权限等等

  验证:仔细分析可以看到它跟权限是只是它往往是验证要处理业务对象中数据和些值关系

  流程:不知道大家系统中遇到流程是什么概念是业务对象个字段叫“状态”它从0开始往后加每个值就标识业务对象所处状态连串起来就是流程在区别状态下它权限和验证都不这样就可以把它合并到权限和验证中去做了如:“状态0&&(权限关系表达式)”

  操作:任何位置按键或鼠标点击都可能会有些行为这些行为都是根据内容来界面有内容所以在内容上输入才有意义这样我们可以把界面层内容和在该内容上操作在业务层设计成关联就是内容包含操作

  先来看看业务层数据访问层已经很简单并很成熟了这里就避过了

  1.    既然有关系表达式我们就做这样个类Restrict(我这里用限制不用权限词是验证也会用到它)来帮我们计算单个关系表达式(即:1>2这种形式)真假值个类来专门做这个是为了能够计算我们自定义类型关系如图1GetRestrict思路方法计算并返回单个关系表达式真或假这里只写了5个静态关系操作符并且命名太过于直接了实际开发时当然不能这样大家能看懂就行

浅谈MIS系统架构

  图1

  2.    我们要得到个关系表达式组合真假值这样我们会有很多Restrict和个表示它们组合关系表达式看图2这里有个实现了.NET类库IList接口Restricts类它有个属性RestrictExpression就是表示组合关系举例介绍说明这个属性(1>2||3<8)&&99像这样关系表达式组合在这个属性里表示为”( [0] Restrict.|| [1]) Restrict.&& [2]”相信大家已经明白它含义了中括号为Items接下来任务就是Parse思路方法事情了由它去解析这样表达式并返回布尔值这个类中还要有验证RestrictExpression属性是否正确代码这些实现上细节问题就不多说了还要实现IList接口这些思路方法在这个类图中就省去了这样做是为了让它着更方便操作它

浅谈MIS系统架构

  图2

  3.    个操作就比如个按钮它有在它上显示文本、它在什么情况下可见(Visible)和什么情况下可用(Enabled)、个要执行思路方法以及思路方法执行前验证似乎操作(行为)类就这样定义:如图3.这个比较头痛行且这样

浅谈MIS系统架构

  图3

  4.    我们再来考察内容就像个Control控件它有在它上面显示文本、绑定到它数据以及它在什么情况下可见(Visible)和什么情况下可用(Enabled)我们是不是漏了什么?对了Control控件都有输入(如鼠标点击、键盘按下)这些输入会转化为事件这就是我们操作因此每个内容都有系列操作我们内容已经有个显示文本属性操作类中Text就省了还有可见权限和可用权限也是看看图4在前面图中已经画类这里就不画了其中Behavior类属性Action是个委托接受来自领域对象思路方法如保存委托DisplayRights接受来自Content类中DisplayRights.Parse思路方法委托EnableRights同理思路方法Operate为传给界面层作为事件思路方法它先DisplayRights和EnableRights执行为真后OperateValidate.Parse验证为真则执行Action可以看到事件思路方法e参数类型用了反射根据Type值获得类名可以发现把所有功能都集中到了Content类中我想用它可以描述很多东西了只要结构标准统连串Content对象就可以表示所要呈现各种东西就像Win32编程中窗口概念打开个界面是窗口个Control控件也是窗口完善Content类就可以等同于这样窗口概念

浅谈MIS系统架构

  图4

  5.   我们会有个业务层基类它是抽象类由它来统访问数据访问层如图5

浅谈MIS系统架构

  图5

  6.    接下来就是和我们要面对问题有关事情了就是领域模型由于我们面临问题可能很复杂这里无法做到概括所以问题域只是举个例子:单据如图6我们用个抽象基类Bill来定义共有属性和思路方法每个具体单据继承它并加载数据、设置权限验证操作等等属性当我们需要对数据库进行操作时(即序列化等问题)需要知道数据库或XML设计因此我们设计了类似ORM框架让它帮我们做对象关系映射和序列化当修改了数据库只要修改它就OK了其中接口不应仅仅这些需要有更多思路方法帮助我们知道对象关系映射信息和对数据库操作同时要传IDa对象给它注:我没有去了解过任何现有成熟ORM框架这里设计思路和它们没有任何关系只是为了实现这样架构而有了它不能把它当成ORM框架来看它应该有个基类以完成些共有属性和思路方法这里没有仔细去研究它

浅谈MIS系统架构

  图6

  对于界面层基类就是要设计好所需要结构和根据业务层对象添加相应Control控件并设置属性和事件如图7对于结构相应基类画好就行要显示什么内容就是遍历Bill类MainContents : Content、DetailContents : Content和Behaviors : Content 3个属性来设置相应Control控件事件就是这 3个中Operations : Behavior属性个Control控件会有多个事件根据Behavior类Type : BehaviorType属性确定是那个事件这在BehaviorType类中约定将Behavior类Operate思路方法赋给事件

  区别结构设置Control控件属性区别区别窗体又有区别结构因此设置Control控件属性工作就放到了具体结构窗体构造中(如FormDetail、FormList和FormTree)由具体单据窗体构造

浅谈MIS系统架构

  图7

  这样就很好将业务层和界面层相分离了界面层只负责将内容种结构去显示至于是显示什么内容是业务层当我们要显示某列名字有改变时只是要去修改Content对象将问题集中在个对象里面

  好了整个结构思路讲完了大家可能已经看到大多数类属性都是共有这里是为了表达自己观点是概念层东西并没有过多去考虑实现上细节图8是张完整类图

浅谈MIS系统架构

  图8

  被解救出来开发人员

  现在开发人员工作就更加明确和清晰了我们以订单这个东西来简单体验下这个架构

  首先我们定义个实现IListable、ISavable和ISubmittable接口类OrderPL它要做数据库表到DataSet数据类型(你也可以用你自定义类型)映射订单拥有操作保存和提交等等

  然后根据需求我们会定义个订单类名字就是订单主要内容可能是订单号、订货日期明细内容是所订货物它有货物名称、数量、价格订单有保存和提交两个操作根据这些我们Order类大致这样写(注意代码中注释)

Order:Bill
{
  privateILoadable_loadPl;
  privateISavable_savePl;
  privateISubmittable_submitPl;
  publicOrder
  {
    OrderPLpl=OrderPL;
    _loadPl=pl;
    _savePl=pl;
    _submitPl=pl;
    Data=_loadPl.Load;
    Text="订单";
    
    MainContents=Content{
      Content(
        "订单号",
        "DDH",
        //在任何情况下都显示
        null,
        //流程状态为0时可编辑
        Restricts(Restrict{Restrict(Data.Table[0].Row[0]["ZT"],0,Relative.)},"[0]"),
        //没有针对该Control控件事件操作
        null
      ),
      Content(
        "订货日期",
        "DHRQ",
        null,
        Restricts(Restrict{Restrict(Data.Table[0].Row[0]["ZT"],0,Relative.)},"[0]"),
        null
      )
    };
    DetailContents=Content{/*货物名称、数量、价格*/};
    Behaviors=Content{
      Content(
        "保存",
        "",
        //保存只能是在状态为0、订单所有者是当前登录用户并且当前登录用户有订单保存操作权限下可用
        //这里假定系统对订单保存操作权限用0表示(每个操作都要有个值来表示)同时这个值应该放到个枚举中
        //如果你还有其他复杂权限可以添加相应类去做
        Restricts(
          Restrict{
            Restrict(Data.Table[0].Row[0]["ZT"],0,Relative.)
            Restrict(Data.Table[0].Row[0]["OWNER"],User.ID,Relative.)
            Restrict(UserRights.GetRights(0),True,Relative.)
          },
          "[0]&&[0]&&[0]"
        ),
        //同上
        Restricts(
          Restrict{
            Restrict(Data.Table[0].Row[0]["ZT"],0,Relative.)
            Restrict(Data.Table[0].Row[0]["OWNER"],User.ID,Relative.)
            Restrict(UserRights.GetRights(0),True,Relative.)
          },
          "[0]&&[0]&&[0]"
        ),
        //保存可拥有多个操作
        Behavior{
          //按钮被按下操作
          Behavior(
            //表示是按钮点击操作
            BehaviorType.ButtonClick,
            //验证订单号不能为空
            Restricts(Restrict{Restrict(Data.Table[0].Row[0]["DDH"],"",Relative.!=)},"[0]"),
            //保存数据
            _savePl.Save,
            Restricts(
              Restrict{
                Restrict(Data.Table[0].Row[0]["ZT"],0,Relative.)
                Restrict(Data.Table[0].Row[0]["OWNER"],User.ID,Relative.)
                Restrict(UserRights.GetRights(0),True,Relative.)
              },
              "[0]&&[0]&&[0]"
            ).Parse,
            Restricts(
              Restrict{
                Restrict(Data.Table[0].Row[0]["ZT"],0,Relative.)
                Restrict(Data.Table[0].Row[0]["OWNER"],User.ID,Relative.)
                Restrict(UserRights.GetRights(0),True,Relative.)
              },
              "[0]&&[0]&&[0]"
            ).Parse
          )
        }
      ),
      Content(
        "提交",
        "",
        Restricts(
          Restrict{
            Restrict(Data.Table[0].Row[0]["ZT"],0,Relative.)
            Restrict(Data.Table[0].Row[0]["OWNER"],User.ID,Relative.)
            Restrict(UserRights.GetRights(0),True,Relative.)
          },
          "[0]&&[0]&&[0]"
        ),
        Restricts(
          Restrict{
            Restrict(Data.Table[0].Row[0]["ZT"],0,Relative.)
            Restrict(Data.Table[0].Row[0]["OWNER"],User.ID,Relative.)
            Restrict(UserRights.GetRights(0),True,Relative.)
          },
          "[0]&&[0]&&[0]"
        ),
        Behavior{
          Behavior(
            BehaviorType.ButtonClick,
            Restricts(Restrict{Restrict(Data.Table[0].Row[0]["DDH"],"",Relative.!=)},"[0]"),
            _submitPl.Submit,
            Restricts(
              Restrict{
                Restrict(Data.Table[0].Row[0]["ZT"],0,Relative.)
                Restrict(Data.Table[0].Row[0]["OWNER"],User.ID,Relative.)
                Restrict(UserRights.GetRights(0),True,Relative.)
              },
              "[0]&&[0]&&[0]"
            ).Parse,
            Restricts(
              Restrict{
                Restrict(Data.Table[0].Row[0]["ZT"],0,Relative.)
                Restrict(Data.Table[0].Row[0]["OWNER"],User.ID,Relative.)
                Restrict(UserRights.GetRights(0),True,Relative.)
              },
              "[0]&&[0]&&[0]"
            ).Parse
          )
        }
      )
    };
  }
}




  接下来就是界面层大量工作都交给业务层了这层主要工作就是在构造基类构造

FormOrder:FormDetail
{
   publicFormOrder(bill:Order)
   {
       bese(Order);
   }
}


  写在后面废话

  在08年5月份回到合肥时候就准备写这篇东西当时还写了标题的后就没有继续现在发现写东西是要先写提纲再写正文的后才确定标题那时想法肯定比现在要多正是想到东西在脑子里会慢慢流失“今天事不要留到明天去做”所以乘着还没正式跨入09年把东西整理总结下不管是好是坏也是对自己个交代

  我没有去验证这里所表达思想可行和优劣等问题个纸面东西脱离了实战就会显得惨白没有说服力

  标准建模语言UML教程中将静态图分为 3个层次:概念层、介绍说明层和实现层这篇文章可能连概念层都没达到如果要到实现层这短短篇幅是远远不够更不可能把.NET那么多特性都写进去

  最近不知道是吃了什么总是fart如果你赞同本文观点那自然是对我莫大鼓舞如果你有区别见解或者认为有什么缺憾请指正否则就请当我吃多了消化不良吧!



Tags:  信息系统架构 系统架构 mis系统是什么 mis系统

延伸阅读

最新评论

发表评论