Web应用架构的新趋势

系统架构:Web应用架构的新趋势---前端和后端分离的一点想法

 

  最近研究servlet,看书时候书里讲到了c/s架构到b/s架构的演变,讲servlet的书都很老了,现在的b/s架构已经不是几年前的b/s架构,其实b/s架构就是web应用开发,对于这样的架构我们现在应该考虑的是前端和后端的分离(注意:这里的后端是指服务端)。

  Web前端现在是一个独立的技术工种,这个工种的产生主要是针对互联网行业的需求,我在以前的文章里曾经讲到过,一个大型互联网网站,例如想淘宝网,它绝对不是一个Web项目,而是一群web项目的集合,那么如果不在前端进行整合,这么多web项目前端开发一定存在大量重复劳动,并且运维时候也存在难以统一管理的问题。本文假想一个面对需要前端资源整合的组织,如何做到前后端分离的解决思路。本文详情如下:

 

(一) 前后端分离的目的和作用

 

  做Web开发也可以说是B/S架构开发,B端和S端从技术体系角度而言异构性很大,换而言之就是B端使用的技术和S端使用的技术不适于同一个体系,这样的结果导致实际开发中,很难做到专业分工,如果项目开发过程中管控不到位,这样的问题可能会影响到整个项目的开发质量,因此前后端分离的目的之一就是要做到专业化分工,提高项目的质量和开发效率。

 

  随着技术的发展,当下的Web开发形势已经和以前有了很大的不同,早期的Web项目是一个封闭的项目,用户从浏览器里看到的页面直到后台数据库都是在一个项目里集成的,而现在Web系统的规模越来越大,中大型的Web系统是一个开放式的系统,开放型的系统用户在浏览器发起的请求可能会转发到外部的系统里进行处理,或者是本地的系统和外部系统一起完成请求的处理,此外有的请求可能不会直接请求数据库,而是请求缓存服务器,这些变化几乎都是发生在Web系统的服务端,前后端耦合度很高的Web系统服务端的复杂度提升必然带来了Web前端的复杂度的提升。因此Web前端从系统架构的角度也需要更加专业的管控,管控的作用之一就是前后端进行分离,降低前端对服务端的依耐性。

 

  富客户端应用的普及导致Web前端技术开发更加专业化,Web前端工程师成为一个独立的技术岗位,Web前端开发技术的难度也越来越高,前后端的分离就是为Web前端开发营造一个良好的开发环境,不要让前端工程师被一些不可控的外在因素所影响(例如:前后端的耦合性),最后导致前端不能专心致志做出更加好的作品。所以,前后端分离是让前后端更加专业化,在技术和管理上将前端角色更加明确,更深入的挖掘前端开发的价值。

(二) 现有系统架构给前后端带来的问题以及解决方法

  上图是目前大部分系统的架构图,虽然有些系统采用分布式架构,层与层之间使用了远程调用框架,但是本质上都逃不开上面这个架构设计。这张图是一张比较合理的图,在实际开发里最常发生的事情就是控制层(Control)越过服务层(Service)直接处理下面的资源。

  前后端耦合的问题主要发生在控制层(Control),控制层是前端和服务端交互的边界,但是在开发过程中控制层(Control)和服务层(Service)常常混淆不清,这就是前后端耦合度高的重要原因。

  因此要前后端解耦,就是要划清控制层的边界,控制层到底该属于前端还是服务端,在MVC模式里控制层作用是调度,控制层不是写业务逻辑的地方,因此将大量业务逻辑写到控制层其实是违背了MVC模式的思想,同时控制层是前端和服务端通讯的桥梁,其实控制层是参入了前端的工作任务,既然控制层要剥离业务操作同时控制层也要参入前端应用的开发,那么将控制层归为前端的一部分是完全合情合理合规的。

  把控制层剥离了业务逻辑处理可能会让人不知道如何开发了,我觉得有这种想法的人是开发时候没有理解透MVC模式思想,编程随意性大养成了坏习惯,这个就需要这些人一点点去适应技术新趋势的发展。

  前后端分离的终极目标应该是前端和服务端是完全独立的项目,前端项目包含上图里的浏览器和控制层,服务端项目包括服务层、DAO层等等,前端项目和服务端项目以高效的远程调用框架做通讯介质,项目开发时候前端项目做前端的事情,服务项目做服务端的事情,这样就让服务端开发的人员没有机会在控制层乱写代码了,保证了Web前端环境的纯粹性,最后生产发布也要独立部署,这样就达到了前后端真正解耦,但是前后端的沟通机制也是不可或缺的,我觉得它们之间的沟通使用高性能的远程调用框架,前后端相互约定通讯报文格式。.

  其实不管服务端还是前端宏观流程无非是输入数据à数据处理à输出数据,但是服务端要把心思花在数据处理上,前端要更多关心的是输入输出数据时候的用户体验操作,服务端开发最大的问题就是违背MVC原则,代码编写的随意性,而前端不管出于安全还是性能考虑,最好是尽量少牵涉业务。前端和后端通讯层的独立,会将前后端进行真正的解耦,前面我讲到前后真正问题就是前端和后端技术路线不一致,但是传统Web开发里前后端又要融为一体,这就导致前后端很难做到专业化分工,对于前端应该尽量弱化通讯级别的开发工作,前端通讯编程只要知道调用哪个接口,传什么参数,怎么处理响应信息就行了。这样就能让前端和后端实现真正的专业化。

  做到了这些,就不会发生开发时候前后端边界不清的问题了。

(三) 前后端分离的一些想法

  本文主题应该是前后端分离,我上面的建议是个彻底方案,要革以前系统的命,对存量系统那该如何处理,答案还是重构代码,想方设法逐步减少已经发现的前后端耦合度高的问题,这个跟我之前的建议就是小重构和大重构的区别,如果有人觉得我上面建议合适,前端组应该马上提供一套这样的框架出来,这样后面的新系统就不会在循环前面的错误了。我觉得搭建这样的框架不会太复杂的。

  我上面的前后端分离的目的就是将前端资源整合为一个整体,理清前后端的边界,这些做完后,前端组里该如何提升自己的能力了?

  这时候要让前端的东西项目化,工程化,前端技术不能再当做开发者的玩具,它也是需要大量的系统架构,开发规范,自动化压缩混淆,自动化发布,前端监控和分析,前端优化等等。

  上面这些问题都很重要,也很专业,如果我有机会能参入这样的事情,我还有个特别的建议,具体如下:

  在一个企业内部,Web前端的组件,不管这个组件是UI层级,还是javascript开发层级,都脱离不了该企业业务产品的模式,其实看看像网易,新浪这样的门户网站的前端应用组件,它们用于做门户很合适,但是用它来做企业应用软件可能就不是太好使用,因此对于组件要有一个清晰的认识,我觉得可以把组件按业务场景分类,这里我可以举个例子,如果这个企业有给门户使用的组件,而这个组件很适合门户,应该把它归为门户组件,如果某些组件适合做网站后台管理的,那么就列为后台管理组件,如果某些组件能跨多了业务场景则标记为通用组件。

  做分类的原因是为了理清组件的应用边界,这样我们可以有针对性的积累和完善这些组件,有意识的开发相关的组件,最终形成一个针对某个业务组件的组件仓库,这样等新需求过来,Web前端的项目经理或Web前端的技术经理可以通过场景分析该需求需要使用那些现有的技术,需求里的那些场景是要进行开发,新场景里有没有新开发的代码能生成新的组件,这就可以做到有计划有次序的积累。

  Web前端的核心人员应该花更多精力去设计、积累、整理各种组件,通过实际业务需求去完善和丰富这些组件,最终达到组件可以覆盖到全公司绝大多数场景,最终通过组件积累形成完善的Web前端开发规范,这样的规范覆盖面广更加易于操作,对于企业而言Web前端开发流程就可以做到标准化,从而达到简单培训一些技术能力不高的开发人员就能完成相关的开发任务,同时也让Web前端核心人员也能很好的把控项目的质量和进度。

  以上就是我的一些前后端分离的想法,它是一个很宏观的想法,没有太多技术实现细节,如果这个想法如果针对存量系统,的确是一个颠覆性的方案,如果Web前端允许一切重头来做,我个人觉得这还是很好的一个思路。前后端分离是Web前端专业化的万里长征第一步,如果这一步做好,前端就有一套专属自己的优质环境,那时候Web前端就会有更大的余力做更优秀的工作,这就是我的愿景。

  当然我的构想也许并不太正确,如果有大牛看了本人文章还请多多指教。

  今天是腊月23号,听说在北方23号就要过小年,呵呵,年味浓了,所以今天在这里祝关注博客园的童鞋们:新春快乐,新年大发哦!

 
 
分类: 系统设计与架构

设计模式学习之建造者模式(Builder,创建型模式)(6)

假如我们需要建造一个房子,并且我们也不知道如何去建造房子,所以就去找别人帮我们造房子

第一步:

新建一个房子类House,里面有房子该有的属性,我们去找房子建造者接口HouseBuilder,我们要建造一栋平房,就去找PingFangHouseBuilder,该类继承自HouseBuilder,里面有具体建造房子的方法各种方法,比如造地板makeFloor,造墙makeWall等

第二步:

光有会建造房子的人还不行,我们还需要专门的设计师HouseDirector来调用这个建造房子的方法才行

第三步:

客户只知道建造平房,只能 找设计师去调用建造者去建造房子,然后从建造者那里得到房子

代码如下:

House.java

复制代码
package com.designpattern.builder;

/**
 * 房子类
 * @author yxl
 *
 */
public class House {
    //地板
    private String floor;
    //
    private String wall;
    //屋顶
    private String housetop;
    public String getFloor() {
        return floor;
    }
    public void setFloor(String floor) {
        this.floor = floor;
    }
    public String getWall() {
        return wall;
    }
    public void setWall(String wall) {
        this.wall = wall;
    }
    public String getHousetop() {
        return housetop;
    }
    public void setHousetop(String housetop) {
        this.housetop = housetop;
    }
}    
复制代码

HouseBuilder.java

复制代码
package com.designpattern.builder;

/**
 * 造房子的建造者,是个抽象的
 * @author yxl
 *
 */
public interface HouseBuilder {
    /**
     * 建造地板
     */
    public void makeFloor();
    /**
     * 建造墙
     */
    public void makeWall();
    /**
     * 建造屋顶
     */
    public void makeHousetop();
    
    public House getHouse();
}
复制代码

PingFangHouseBuilder.java

复制代码
package com.designpattern.builder;

public class PingFangHouseBuilder implements HouseBuilder {

    private House house = new House();
    
    
    @Override
    public void makeFloor() {
        house.setFloor("平房建造--地板");
    }

    @Override
    public void makeWall() {
        house.setWall("平房建造--墙");
    }

    @Override
    public void makeHousetop() {
        house.setHousetop("平房建造--屋顶");
    }
    
    public House getHouse(){
        return house;
    }

}
复制代码

HouseDirector.java

复制代码
package com.designpattern.builder;

/**
 * 设计师类
 * @author yxl
 *
 */
public class HouseDirector {
    /**
     * 设计师调用建造者的盖房子方法就行
     * @param houseBuilder
     */
    public void makeHouse(HouseBuilder houseBuilder){
        houseBuilder.makeFloor();
        houseBuilder.makeWall();
        houseBuilder.makeHousetop();
        
    }
}
复制代码

MainClass.java

复制代码
package com.designpattern.builder;

public class MainClass {
    public static void main(String[] args) {
        //如果是这种实现方式,那么客户必须知道如何建房子才行,必须知道细节,所以客户就去找人HouseBuilder去建造房子
//        House house = new House();
//        house.setFloor("建造地板");
//        house.setWall("建造墙");
//        house.setHousetop("建造屋顶 ");
//        System.out.println(house.getFloor());
//        System.out.println(house.getWall());
//        System.out.println(house.getHousetop());
        
        
        //这样实现是客户直接找建造者建造房子,客户还必须调用建造者去造房子,这样客户还是必须知道如何造房子才行,
        //所以建造者必须找一个设计师(HouseDirector)来调用建造者的造房子方法
//        HouseBuilder houseBuilder = new PingFangHouseBuilder();
//        houseBuilder.makeFloor();
//        houseBuilder.makeWall();
//        houseBuilder.makeHousetop();
//        House house = houseBuilder.getHouse();
//        System.out.println(house.getFloor());
//        System.out.println(house.getWall());
//        System.out.println(house.getHousetop());
        
        //将调用建造者造房子的方法交给设计师去调用,客户只需要找一个设计师就可以了,等房子造好之后就问建造者去要
        HouseBuilder houseBuilder = new PingFangHouseBuilder();
        HouseDirector houseDirector = new HouseDirector();
        houseDirector.makeHouse(houseBuilder);
        House house = houseBuilder.getHouse();
        System.out.println(house.getFloor());
        System.out.println(house.getWall());
        System.out.println(house.getHousetop());
        
        
    }
}
复制代码

一、什么是建造者模式

Builder模式也叫建造者模式或者生成器模式,是由GoF提出的23种设计模式中的一种。Builder模式是一种对象创建型模式之一,用来隐藏复合对象的创建过程,它把复合对象的创建过程加以抽象,通过子类继承和重载的方式,动态地创建具有复合属性的对象。

二、建造者模式应用场景

- 对象的创建:Builder模式是为对象的创建而设计的模式

- 创建的是一个复合对象:被创建的对象为一个具有复合属性的复合对象

- 关注对象创建的各部分的创建过程:不同的工厂(这里指builder生成器)对产品属性有不同的创建方法

 
 
分类: 设计模式
 

系统权限的设计之简单设计

工作时间也不长,但是总想写一些自己的收获。公司利用的技术也比较单纯,asp.net,js也不怎么需要用,唯一写的多的就是sql语句。

好了,废话不多说了,开始谈谈我在做项目中一些对系统权限的收获,不过很多都是项目中看到的,我就想自己重新做一遍。也许会有

很多的问题和考虑不全的地方,但是我还是要写出来,当做自己的一种学习罢了。

设计思路

    (1)用户表

           权限是根据登陆者不同而不同的,用户表就比较简单了,简单的表设计如下

    [user_no] [nvarchar](20) NOT NULL, //用户ID,主键
    [user_name] [nvarchar](20) NULL,
    [user_password] [nvarchar](20) NULL,

(2)权限列表

设置所有的权限,例如新增,修改,查询

    [action_id] [nvarchar](20) NULL,
    [action_name] [nvarchar](20) NULL

(3)功能表

          系统的功能是存在数据库中的,根据权限来获取部分功能,并展示。表设计如下:

复制代码
    [function_id] [nvarchar](20) NOT NULL,//功能id
    [function_brother_id] [int] NOT NULL,//相同功能的不同页面
    [function_name] [nvarchar](50) NULL,
    [function_url] [nvarchar](500) NULL,
    [function_level] [int] NULL,
    [function_sort] [int] NULL,
    [function_action] [nvarchar](100) NULL,
    [function_parent_id] [nvarchar](20) NULL,
    [function_inmenu] [bit] NULL,//是否在菜单中
复制代码

为什么有[function_brother_id],一个菜单里面的一个功能可能会有多个页面。例如一个用户页面,可能在功能里面叫用户信息,但是会涉及多个页面,每个页面都需要权限。
[function_inmenu]就是判断同功能里面菜单里链接是其中哪个页面(只能有一个)。

[function_action]是根据权限列表给每个功能赋给权限

(4)权限组

我们的权限是根据组来区分的。一个用户在一个组里面,那个组里面有哪几个功能,其中每个功能有哪几种权限。这就是我们的系统权限的核心了,不过比较简单。

    [group_id] [nvarchar](20) NOT NULL,
    [group_name] [nvarchar](50) NULL,

这里面存储权限组的基本信息,一般会默认有系统管理者和一般使用者。

(5)用户权限组

设定用户在哪个群组。我们目前的系统设计的是一个用户可以有多个组,但是我觉得一个用户设定一个群组就ok了。如果一个用户没有设定在哪个组,默认为一般使用者。

    [group_id] [nvarchar](20) NULL,
    [user_no] [nvarchar](20) NOT NULL,

(6)群组功能

设定每个群组里面有哪几个功能,每个功能有哪几种权限。

注意,功能表里面的权限与此处不同,可能一个功能有查询和删除权限,但是本群组里面的这个功能只有查询权限。

这个权限必须是在功能里面此功能所有的权限中。

    [group_id] [nvarchar](20) NOT NULL,
    [function_id] [nvarchar](20) NOT NULL,
    [group_action] [nvarchar](100) NULL,

表设计到此ok,算是比较简单的。

使用过程

 (1)登陆时

      验证通过时,

             > 根据用户id去用户权限表里面取得此用户所对应的群组

             >根据群组对应的所有功能去功能表里面找到相应的在菜单中的功能,这里可以拼成xml来组成功能列表,

                 这样就可以实现有些功能在菜单,有些没权限的则不在,也可以查到此功能有哪些权限(比如只有查询权限,或是还有新增权限)

       进入页面时的处理

          群组功能里面无此页面功能时,就算是直接输入连接也会被拒绝访问

          根据页面找到功能id,再找到登入者所属群组此页面有哪些权限,根据权限来实现一下页面元素的隐藏和显示(如没有新增权限就把新增按钮隐藏)

          这样就实现了登入者的权限,进入页面的权限和页面上一些操作的权限了。

 
 
原文地址:https://www.cnblogs.com/Leo_wl/p/3531841.html