系统融合

系统融合,简单的说就是把多个系统合并成一个系统。

  • 组件化方式:是在服务化的拆分基础上,提取可独立部署和多次服用的部分。一个是合并,一个拆分,看似矛盾,但两者却可以同时使用,相得益彰
  • 插件化方式:解决解耦后的易扩展问题,面向的是单个服务或组件

组件化

组件化”的结果就是把系统作为一个个“组件”独立部署并对外服务,我理解的系统“组件化”,其实是对系统 “服务化”或 “微服务化”的另一种称呼罢了。区别在于“组件”是对外的“服务”,有些“服务”是私有的不能对外。



这里封装了一个组件名称为“组件1”,包含3个子服务系统,其中A服务对外开放,B、C服务是为了支持对外的A服务而存在的,但不对外开放。这里采用了“微服务”的思想把“组件1”拆分为三个子系统,有点类似java里的public方法和private方法,A系统对应public方法可以对外服务,B、C系统对应private方法只能在“组件1”内部被调用。这里所谓的服务都是通过RPC框架搭建的子系统。

小前台 + 大中台

新增一个“前台业务”,只要“中台系统”足够强大,新业务可以通过调用各个公共的“组件”采用类似搭“积木”的方式,快速完成一个新业务系统开发。这应该就是阿里所谓的“小前台”、“大中台”理论基础。
好处就是快速上线、快速试错,“前台系统”只需要投入少量人力成本,就可以快速完成新产品的研发和上线,根据市场的反应再做调整。

“组件化”与项目“微服务化”

前面提到的“前中台系统”建设,是站在公司组织架构层面来划分的。个人认为 在各自所在的项目组,也可以采用这种“组件化”的思路来进行子系统拆分,在项目组内有自己的“前中台”子系统,不管这个项目是否在组织架构上属于“前台”还是“后台”。在具体项目内部进行“前中台”子系统拆分,其实有点类似“微服务化”拆分



上图中的“jsf服务子工程集”中的每个子工程都可以作为“组件”来看待(只是这个组件只有1个工程,但根据业务需要对每工程还可以继续模块化拆分),属于“中台系统”。

上图中的“web服务子工程集”其实就对应各种业务系统,通过调用各种基础服务堆积而成,属于轻量化的“前台”系统。只要“jsf服务子工程集”中的“组件化”做得足够强大,我们就可以在项目组最大化的复用这些公共组件,更少的人力投入,快速的实现业务开发。

项目“组件化”架构案例

在这个项目“组件化”之前,是按照业务对系统进行划分,分为pc店铺、pc活动、m店铺、m活动,系统划分如下:



采用组件化的思想对系统架构进行改造,分别对前、后端都进行“组件化”提取,把公共的功能模块提取为“组件”单独部署。具体的业务系统调用这些公共组件达到复用的目的。改造后的系统架构如下:


插件化架构

todo

融合模式

两个系统融合,最大的困难就是接口不统一
比如同样是商品接口,A、B两个公司的接口名可能不同,商品类的定义也不同。这时为了让外部系统调用这两个接口无感知,就需要一个统一的接口,这就产生了适配器模式。

适配器模式

使用适配器模式进行系统融合

在“系统融合”的场景中会为同一个接口创建多个Adapter适配器(这里是两个),分别对应多个类似业务。这里以A、B两个电商系统融合为例,两套系统有数十个接口我们需要在A、B两个系统之上新建一个“适配器”系统。为了顺应现在的“前中台系统建设”潮流,设计架构上对前中台进行区分,整体架构调整如下:


在A、B两个系统没有融合前,他们都各自对应自己的前台系统,架构说明如下:

1、A、B两个公司合并前,都有各自对应的前台系统和中台系统。如图中“绿色箭头”所示。

2、现在A、B两个公司合并,为了降低维护成本,以及增加用户体验,只维护一个前台系统。为了在系统融合期间,外部用户可以正常访问A、B前台系统,这里增加一个“新前台系统”。

3、同时为了兼容老数据,A、B两个系统保持原样不变,新增一个“适配器系统”,对A、B两个系统中的公共业务接口进行适配。接口调用流程,如上图中“红色箭头所示”,统一后的“前台系统”首先调用“适配器系统”,根据参数适配到A或B系统中。

4、A、B两套系统在融合前 虽然业务类似,但也就自己的个性化业务,统一后的“前台系统”直接调用A、B系统原接口即可。如上图中的“紫色箭头”所示。

5、当“新前台系统”开发完成并上线后,即可关闭两个老的前台系统。只维护一套“新前台系统”即可

通过上述系统架构,即可快速完成新系统的融合,又不影响老系统的访问,为了防止老客户对新系统的不适应,还可以让“三个前台系统”并行运行一段时间。是不是有种“酷毙了”的赶脚。

这个强大的系统架构设计的核心就是设计新的“适配器系统”,这个系统里设计有多个数据接口(A、B系统公共的接口),每个接口都是采用“适配器模式”对A、B两个系统的接口进行封装,让“新的前台系统”以为是一个接口。

下面就以“商品接口”为例,对“适配器模式”进行讲解。

适配器示例--融合“商品接口”

根据上述新系统架构,主要分为4个系统:“A系统”、“B系统”、新“适配器系统”、新“前台系统”。作为示例不会把4个系统都搬出来,这里使用一个java application程序进行模拟,如下:


其中两个老系统的商品类ProductA、ProductB业务很类似:

public class ProductA {  
    private Integer a_id;//A系统 商品id  
    private String a_name;//A系统 商品名称  
private String a_category;//A系统 商品分类  
   
//省略其他setter、getter方法  
}  
   
public class ProductB {  
    private Integer b_id;//B系统 商品id  
    private String b_name;//B系统 商品名称  
    private String b_category;//B系统 商品分类  
private Integer venderId;//商家Id  
   
//省略其他setter、getter方法  
}  

ProductB中多一个成员变量venderId(商家Id)。现在要在新“适配器系统”中,定义新商品类Product,需要包含两个系统中所有业务,定义如下:

public class Product {  
    private Integer id;//新商品id  
    private String name;//新商品名称  
    private String category;//新商品分类  
private Integer venderId;//商家Id  
   
//省略其他setter getter方法  
}  

新商品对象定义完毕,现在进行接口“适配”,这里以A系统商品接口为例(B系统类似);已有的被适配角色ProdcutManagerA(接口)、ProdcutManagerAImpl(实现类):

public interface ProdcutManagerA {  
    ProductA getById(Integer id);  
}  
   
public class ProdcutManagerAImpl implements ProdcutManagerA{  
    public ProductA getById(Integer id){  
        ProductA productA = new ProductA();  
        productA.setA_id(1000);  
        productA.setA_name("A系统彩电");  
        productA.setA_category("A系统_家电类");  
   
        return productA;  
    }  
}  

新接口:新接口返回类型是新商品类Product:

/** 
 * 新商品管理接口类 
 * Created by gantianxing on 2017/11/4. 
 */  
public interface ProdcutAdapter {  
    //新商品接口  
    Product getById(Integer id);  
}  
/** 
 * A系统商品接口 适配器 
 * Created by gantianxing on 2017/11/4. 
 */  
public class ProdcutAdapterAImpl implements ProdcutAdapter {  
   
    private ProdcutManagerA prodcutManagerA;  
   
    public ProdcutAdapterAImpl(ProdcutManagerA prodcutManagerA){  
        this.prodcutManagerA = prodcutManagerA;  
    }  
   
    public Product getById(Integer id) {  
        ProductA productA = prodcutManagerA.getById(id);  
        if(productA != null){  
            Product product = new Product();  
            product.setId(productA.getA_id());  
            product.setName(productA.getA_name());  
            product.setCategory(productA.getA_category());  
            return product;  
        }  
        return null;  
    }  
}  

可以看到ProdcutAdapterAImpl适配器,把“A系统”商品接口 转换为“新前台系统适配的”接口。

结合RPC框架进行解耦

但在真实的系统中通过引入RPC框架和Spring IOC注入,“新前台系统”只会依赖一个“适配器”接口类:ProdcutAdapter;同时新建的“适配器系统”只依赖老A、B系统的接口类:ProdcutManagerA、ProdcutManagerB。如下图所示:


聚合类型兼容性问题

在两个系统融合过程中,还经常遇到另一种情况:A系统返回的商品列表是ArrayList类型,B系统返回的商品列表是数组类型。

//A系统获取商品列表接口  
public List<Product> getProdoucts(){  
//省略业务代码  
}  
//B系统获取商品列表接口  
public Product [] getProdoucts(){  
//省略业务代码  
}  

这就是所谓的“聚合类型兼容性问题”。这时为了统一接口类型,可以在“适配器系统”把ArrayList转换成数组,或者把数组转换成ArrayList。但这不是最优雅的方式,我们还可以使用“迭代器模式”对两个接口进行兼容。Java中得聚合类型:数组、List、Set、Map等。

迭代器模式

迭代器模式提供一种顺序访问一个聚合对象中的各个元素的方法,而又不暴露其内部的表象。把遍历聚合中各个元素的任务移交到“迭代器”上,满足OO设计原则中的“单一责任原则”。另外具体的“迭代器”都实现自一个统一的接口(Iterator),可以兼容不同的聚合类型遍历(这就是解决本文开头“兼容性”问题的关键)。

简单的理解,就是把聚合类型中遍历每个成员的任务剥离出来,生成“迭代器”,这些迭代器都实现自同一个接口。类图关系:



从类图上看,该模式主要有4类角色:

抽象的聚合:AbsAggregate,可以是抽象类 也可以是接口。一般都会定义一个抽象方法,获取迭代器。

具体的聚合:ConcreteAggregate,实现或继承自AbsAggregate。一般都会实现AbsAggregate中的抽象方法,获取具体的迭代器。

抽象的迭代器:Iterator可以是抽象类 也可以是接口。一般最少有两个抽象方法,hasNext()和next()方法,用于遍历聚合中的元素。

具体的迭代器:ConcreteIterator,实现或继承自Iterator。对hasNext()和next()方法进行具体的实现。其构造过程依赖“具体的聚合”,也就是说每个“具体的聚合”,一般都会对应一个自己 “具体的迭代器”。

示例展示

回到文章开头,开始使用“迭代器模式”对A、B两个系统融合过程中,对两个不同的获取商品列表接口进行融合。为了方便理解,实现过程按照“迭代器模式”的4类角色 分类进行:

抽象的聚合:本示例中抽象的聚合是ProdcutAdapter接口,里面只定义了一个createIterator()的抽象方法:

public interface ProdcutAdapter {  
   
    //批量获取商品接口  
    Iterator<Product> createIterator();  
}  

具体的聚合:
public class ProdcutAdapterAImpl implements ProdcutAdapter {  
   
    public Iterator<Product> createIterator() {  
        List<Product> products =getProdoucts();  
        Iterator iterator = new ListIterator(products);  
        return iterator;  
    }  
}

public class ProdcutAdapterBImpl implements ProdcutAdapter {  
   
    public Iterator<Product> createIterator() {  
        Product [] array = getProdoucts();  
        Iterator iterator = new ArrayIterator(array);  
        return iterator;  
    }  
}

抽象的迭代器:Iterator,这里只定义迭代器的两个核心方法 hasNext和next:
public interface Iterator<E> {  
    boolean hasNext();  
    Object next();  
}  

具体的迭代器:本示例中有两类聚合,List和数组。对应的会创建两个具体的迭代器:ListIterator、ArrayIterator
public class ListIterator<E> implements Iterator{  
    private List<E> products;  
    int pos=0;//当前位置  
   
    //外部迭代器,构造函数需要传入一个待迭代的集合类型  
    public ListIterator(List<E> products) {  
        this.products = products;  
    }  
   
    public boolean hasNext() {  
        if (pos >= products.size()) {  
            return false;  
        } else {  
            return true;  
        }  
    }  
   
    public Object next() {  
        E product = products.get(pos);  
        pos = pos + 1;  
        return product;  
    }  
}  
   
public class ArrayIterator<E> implements Iterator{  
    private E [] products;  
    int pos=0;//当前位置  
   
    //外部迭代器,构造函数需要传入一个待迭代的集合类型  
    public ArrayIterator(E[] products) {  
        this.products = products;  
    }  
   
    public boolean hasNext() {  
        if (pos >= products.length) {  
            return false;  
        } else {  
            return true;  
        }  
    }  
   
    public Object next() {  
        E product = products[pos];  
        pos = pos + 1;  
        return product;  
    }  
}  

Java中的迭代器:Java的API中对大部分的聚合类型都已经默认实现了自己的迭代器,统一实现自接口java.util.Iterator,相比本示例中定义的Iterator,java.util.Iterator多了一个remove方法。
Java api中几乎已为所有的聚合类型创建了自己的迭代器,并且都实现自java.util.Iterator接口。如果要扩展自定义聚合类型的迭代器,直接实现这个接口即可,这样做的好处是可以跟java api中的聚合类型的迭代器完全兼容。

Ref:
https://moon-walker.iteye.com/blog/2393310
https://www.jianshu.com/p/cbde4b00b7c6

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,560评论 4 361
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,104评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,297评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,869评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,275评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,563评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,833评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,543评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,245评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,512评论 2 244
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,011评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,359评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,006评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,062评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,825评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,590评论 2 273
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,501评论 2 268

推荐阅读更多精彩内容