欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

前置数据模型的业务系统开发架构

程序员文章站 2022-06-12 20:25:01
...

前言

已第二次使用AngluarsJS来进行业务开发.虽然队友们对此报有一些情绪与意见.认为比较难用,主要集中在性能方面的考虑.但是我却认为我们从根本上改变了整个前端开发的模式.更多引出了许多前的系统设计方案.起码从开发来说,开发效率,代码可维护性,简易性,条理性都有本质上的提升.

其中AngluarsJS提供了在前端MVC分层的可行性.(其实未必一定使用AngluarJS,只要能让项目以MVC的模式分层则可)改变以往使用jQuery之类的库时,大量的业务代码与业务逻辑代码有着高度的耦合.基本上每个页面的js代码只服务一个业务,无法有效的进行复用业务内容.然而这些情况已经被AngluarJS改变,通过框架提供的Service服务,IOC,Controller,数据双向绑定等,已经可以有效的划分业务代码与操作逻辑,其实也就是MVC,由此业务代码可以独立编写,带出了很多专于某业务的业务代码.
最终,在前端已有MVC框架情况下(常规来说不叫MVC,准确地应是MV*,但这里先此当作MVC来进行讨论),我想直接将这种思路发展得更极限与彻底:服务端是一个可以持久化的json对象,前端仅需要与持久化json对象进行交互。
 
 
思路说明
将服务端抽象成一个持久化Json对象存储器.前端通过ajax来对持久化对象来进行操作,包括增加,修改,删除,查询等方式。
服务端操作则是通过对前端对象操作事件的一观察者,并且所有的输出直接返回给前端,而与前端一样,都是是持久化对象。
最终是将数据模型(MODEL)向前移,前端可直接访问。
 
 
详细说明
上文可能觉得比较难理解。现在详细说说。
以往开发会在服务端定义若干个服务接口,调用具体业务的接口方法,返回对应的结果数据。各方法间是独立的。每个接口都会定义一套具体的参数。返回结果的具体形式,各结果具体的意义是什么。严格说,这种写法属于接口驱动型,或是业务方法驱动型。
然而现在模式不同, 不是独立地提供接口,而是直接提供一个对象,准确地说是一个虚似的json对象。服务端说明这个对象中各成员变量的意义则可。
 
分析实例
在微博平台中
功能1:一个用户向一微博进行评论。
功能2:收藏一个微博。
 
前端面向接口做法
定义一个评论接口:
ResponsesetCommentInTwitter(LongTwitterId, String context);
方法会定义一个微信的id,与文档的参数。然而为了灵活调整参数,会定义如下方法
ResponsesetCommentInTwitter(CommentAddForm commentAddForm);
通过CommentAddForm类来封装需要提交的参数,如果需要增加参数而直接在Form类里头增加成员。
 
如果需要增加功能话,则服务端需要增加业务方法
例如现在需要增加用户收藏微博功能,则需要如下
Response collectTwitter(Long twId);
这里参数就比较简单,但是需要服务端增加对应的方法。
 
*前端面向对象的做法
假设现在服务端的存储数据结构如下:
{
    twitters:{
            "1":{
                    context: "我快没有水啦,各位赶紧给我加水!!!",
                    createTime:"2015-12-01",
                    comments:{
                            "1":{
                                context:"你是个水桶?",
                                createUser:{$ref:"user.1"},
                                createTime:"2015-12-01"
                            },
                             "2":{
                                context:"头脑里头是有水的",
                                createUser:{$ref:"user.3"},
                                createTime:"2015-12-01"
                            }
                    }
               }
        }
    users:{
        "1":{
          account:"zhengmx",
          email:"[email protected]",
          nickname:"代码牧",
           favor:{
                "1":{$ref:'twitters.1'}
            },
          createTime:"2015-12-01"
        },
         "3":{
          account:"yangsl",
          email:"[email protected]",
          nickname:"杨圣琳",
          favor:{},
          createTime:"2015-12-01"
        }
    }
    $login:{
        curUser:{$ref:'users.3'}
     }
}


例如现在增加评论,则可以在前端js代码如下操作:
var twitter = ...//传入微博对象,假设id为1
var twitterComment = {
     context:"的确是一个大水桶"
}
//传入的微博评论内容

var objSer = ObjService.getService(); //提到与服务端对象对接的封装方法,简化前端的使用
//用户提交评论
objSer.push(twitters_id+'.comments',twitterComment,function(resp){
    console.info('resp',resp);//将返回结果打印。
} );


此时服务端数据如下:
"twitters.1":{
                    context: "我快没有水啦,各位赶紧给我加水!!!",
                    createTime:"2015-12-01",
                    comments:{
                            "1":{
                                context:"你是个水桶?",
                                createUser:{$ref:"user.1"},
                                createTime:"2015-12-01"
                            },
                             "2":{
                                context:"头脑里头是有水的",
                                createUser:{$ref:"user.3"},
                                createTime:"2015-12-01"
                            },
                           "3":{
                                context:"的确是一个大水桶",
                                createUser:{$ref:"user.3"},
                                createTime:"2015-12-01"
                            }
                    }
               }


//收藏一个微博
objSer.push('$login.curUser.favor',twitter,function(resp){
    console.info('resp',resp);//将返回结果打印。
} );


此时服务端数据如下:
 "usesr.3":{
          account:"yangsl",
          email:"[email protected]",
          nickname:"杨圣琳",
          favor:{
              "1":{$ref:'twitters.1'}
            },
          createTime:"2015-12-01"
    }



 

实例分析
通过后者来开发的话,服务端则不需要增加接口。也就是说,前端基本上可以直接操作数据库(json对象),不需要后端程序编写加入评论,收藏业务。
在当前场景下,直接操作当前用户的信息,不需要做统计类的信息数据时,可以直接操作业务对象, 在前端进行业务方法的编写。
其中配合AngluarJS,可以将大量方法以Service的方式进行封装。
如TwitterService,UserService。
而Controller则负责将数据分解,将Service的返回的数据转成对应的VO对象。此时可以大量减少前后端之间的代码开发,前端开发时可以直接考虑数据模型的对称,不需要太多地与后端接口进行沟通与对接。如果需要简单修改代码时,只需要更新前端代码,将对应的js代码更换则可,不需要重新布署服务端代码。
 
整体模式梳理
服务通过抽象出一个可以持久化的实体对象,前端可以直接操作对象,通过此来保存,查询数据。通过设置对象来建立数据之间的关系。
通过AngluarJS等框加,可以在前端对部份业务方法进行封装。
一些业务方法并非都是在前端开发,部分业务方法实则在服务端编写,而编写是围绕抽象的对象进行操作,通过监听的操作来实现后端的业务。
后端不直接向前端提供业务方法,而是通过对象的操作来向前端反馈信息。而后端的逻辑最终输出也是设入对象中,然后供前端进读取。
其实此架构方案并非说完全不需要后端代码,而是将一部份后端业务逻辑代码往后移,放在实体的后端,不直接与前端交互。
原模式:
js -> Action -> Service -> DAO -> DB(数据库)
现在型式:
js -> Obj <-> Listen(Service)
 
前端后端代码结合例子:
用户登录:
前端代码:
var = loginInfo{
curUsr: {
    account:"zhengmx",
    password:"123456"
}
};
objServer.push('login.',loginInfo,function(resp){
         $scope.loginMsg = resp.data.loginMsg;
        if(resp.data.loginCode==1){//登录成功 
            page.toIndex();//跳首页
        }else{
            
        }
});


后端代码:
//监听某个对象的写入,业务完成成将输出写入对象中,而非直接返回给前端,此方法与前端无保直接交互
@ListenSet(id="login");//注解代表的是监听set入login.curUser的动作,而非直接提供一个接口方法
public Obj login(Obj rt, Obj newValue, Obj oldValue){
    List<Obj> rs = 
            rt.find("users.name="+newValue.get("curUser.account")+"&users.password="+newValue.get("curUser.password"));
    if(rs.size()>1){
        newValue.get("curUser").ref(rs.get(0));//将新值置为引用持久化对象中用户的对象。
        login.put("sessionid", WebConf.getSession());//取得session,其实会通过cookie将session传送到浏览器中。
        login.put("loginCode",1);
        login.put("loginMsg","登录成功");
    }else{
        login.put("loginCode",2);
        login.put("loginMsg","登录不成功");
    }
    return newValue;//返回的是前端set入的对象,而是不响应的对象.
}


响应数据如下:
$login:{
        curUser:{$ref:'users.3'}
        sessionid:"D092A8DE4ED756EDFEAFE04F038B4086",
        loginCode:1,
        loginMsg:"登录成功"
     }


服务端提供的服务汇总
服务端仅提供对持久化对象的编辑,查询操作。
编辑包括:
set:对指定的id位置直接赋值。
push:新增对象,其中由服务端来生成key值。
get:通过id得到需要的对象。
find:通过查询表达式查询对象。
理论上这些方法基本与业务无关,因为在性能优化上可以针对这些方法优化,主要都是走key/value的方式,与减少了后端的业务逻辑处理。因此在性能上会有较大提升。
 
持久化对象的设计
持久化对象的数据结构为json,一个服务则仅提供一个对象,此对象称为根,简称为rt。
对象的id为对象相对于
如果一个字段是引用其他对象,则使用{$ref:"objectid"}
 
后端js化
以上例子的监听方法是java代码。为了进一步轻量化系统开发的负担。则可以将监听方法js化。
开发者使用js写好监听方法后,事件触发时调用js代码,传入对应的dom对象。其中包括有rt对象。
登录后端代码改写成js后如下:
var $listen_login = function(rt,newValue,oldValue,webConf){
 var rs = 
            rt.find("users.name="+newValue.get("curUser.account")+"&users.password="+newValue.get("curUser.password"));
    if(rs.length>1){
        newValue.curUser=rs[0];//将新值置为引用持久化对象中用户的对象。
        login.sessionid= weConf.session);//取得session,其实会通过cookie将session传送到浏览器中。
        login.loginCode=1;
        login.loginMsg="登录成功";
    }else{
        login.loginCode=2;
        login.loginMsg="登录不成功";
    }
    return newValue;//返回的是前端set入的对象,而是不响应的对象.
}


js化后,修改业务无需更去重启服务端,适合需要快速迭代开发的模式,尽早地将功能上线,而不需要走冗长的升级程序,编译等。
 
 
数据权限方案
<待编写>
 
架构优势
提高前端的业务范围开发能力,减少服务端的复杂性。
缓存优化难度减少,服务端性能可以快速得升,得更加轻量。
升级系统成本减少,部份业务可以不需要重新升级程序,时改时生效。
 
 
适用场景分析
操作的数据简单,针对当用户数据操作较多,且不需要提交大量数据;
用户对实时应响要求高,操作反馈多,逻辑复杂。
功能更新频繁,迭代次数高;
事务要求较少;
无做大量统计的统计操作;
数据模型无需要大量互相引用;
 
不适用场景
大量实时统计需求;
对事务要求较高;
权限要求细度高;
前端设备差,兼容要求高。
结语
目前这个思路还未得到充份的实践与验证,也是小弟在近段时间开发过程中想到的一些决解方案。
这种方案更多地偏像于基mongodb这种NoSQL模式的开发。然而仅仅是"像",mongodb数据库与理想中的Model的目标实现有一定的距离,在业务数据管理时并非一个较好的解决方案。
从这种思路来开发程序时,只是思路转改过来,开发企业级的业务管理系统的效率会非常的高,更多时间都是集中在前端用户体验,与业务流程上的考虑,轻量的服务端开发,更少了去考虑关系数据与服务端之间的交互,更少地去关心实体模型结构,更少地去定义接口,从而减少接口定义带来额外的沟通成本。
如果需要某某数据,则在前端代码增加需要的成员,有种“你要我就马上给你的感觉”。
后续我会对这个思路涉及到的代码不段时间,找出对应的实现方案,会开发出一个样例项目放在github中。
如果对此有兴趣,可以评论,可以私信我,更多地交流。