phodal / clean-frontend

Clean Frontend Architecture:整洁前端架构
https://phodal.github.io/clean-frontend/
824 stars 53 forks source link

自己研究的一套微架构,在Angular项目里用,拿出来探讨探讨 #3

Closed Eusen closed 6 months ago

Eusen commented 5 years ago

目前在写一个大型ERP项目,因为一直用Angular,所以对架构这块也是非常感兴趣的。

你有一个观点是,这套理论很难被开发者应用起来,其实不然,你提出的这种方式确实复杂了一些(个人感觉有点回到了写Java的时代),但让开发大规模用起来也不难,就是用脚本(CLI)生成整套代码。

说出来你可能不信,我现在页面结构,路由,乃至整个项目,都是由一堆脚本来维护的。如果我将来继续从事Angular,这将是我着力要做的事,就是前端自动化运维——把重复的复杂的烦人的工作都交给脚本一键生成/处理。

自研微架构

好了,说回我的设计。说起来也惭愧,本人所在的地区Angular人才极少,所以我也很难与同事探讨一些架构方面的问题,所以闭门造车是免不了的,我的想法也许有问题,但我不怕丢人,说出来我自己反而好受一些。

Store

从 Redux / Vuex 中吸取了一些精华,感觉Angular也不是不能搞这种东西,而且感觉跟Rxjs很像,所以就用Rxjs实现了一套简单的 Store + Status + Mapper 模型:

export class Store {
    // 池子里都是用BehaviorSubject装起来的值,一个键只能对应一个值
    static pool: {[key: string]: BehaviorSubject<any>} = {};

    // 用来注入键值
    static set(key, value) {}

    // 可以直接获取值,一般用来不需要观察的业务逻辑里
    static get<T = any>(key): T {}

    // 用来追踪某一个值
    static track<T = any>(key): BehaviorSubject<T> {}

    // 创建一个状态,返回 T 是希望能够保留它的类型,code的时候很是方便
    static createStatus<T = any>(prefix: string, obj: T): T {}
}
export const AppStatus = Store.createStatus('app', // 前缀,因为直接拿key去存,所以需要用前缀做区分
{
    fullscreen: false, // 默认状态
    loading: false ,
    users: [],
});

这样就算转起来了,在项目里用着也是相当巴适。

DAO 层

这块是重点,我觉得前后端啊,还是不能完全脱离,必须得有一个东西可以把他们有机得结合起来。

所以我就自研了一个DAO层。

首选说一下组成:

export class Dao<T = any> {
    changed = new EventEmitter(); // 每当某些对数据产生影响的行为触发时 会关联触发这个事件

    // 创建对象
    create(data: T) {}

    // 删除对象
    remove(id) {}

    // 更新对象
    update(data: T) {}

    // 获取单个对象
    get(id) {}

    // 获取列表,DaoQuery 是架构无关的查询条件格式
    list(criteria?: DaoQuery) {}
}

这样一来,一套模型,就可以复刻出千千万万个子模型

// 在视图层就可以直接这样调用 class HomePage { // 这样写可能会造成接口重复调用,不推荐 // users$ = this.dao.User.list(); users$ = Store.track<User[]>(AppStatus.users); users: User[];

constructor(private dao: DaoService) {}

ngOnInit() {
    this.dao.User.changed.subscribe(params => {
        this.dao.User.list(params).subscribe(resp => {
            Store.set(AppStatus.users, resp.results);
            // 或者直接赋值
            this.users = resp.results;
        });
    });
    this.dao.User.changed.emit();
}

addUser() {
    // 添加用户
    this.dao.User.create({...}).subscribe(resp => {...})
}

}



- DaoStatus
    - 全局Dao的状态,可以存一些全局的数据

## 小结
通篇来看,发挥作用的有两个重要的点
- 前端与后端的约定 > 前端自己再造新的模型(好的后端才能有好的前端)
- 重复的工作交给脚本去做,这样你能省掉大量的时间

嗯,目前就这些,非常欢迎大家能一起探讨一下~ 献丑了~
phodal commented 5 years ago

Haha,

我挺欣赏这种自己造轮子的做法。只是呢,自己造轮子有一个坑,就是如何让其他/她人能接受这些规则?这也是为什么我们偏向于使用重复代码的原则,大家学习成本比较低。

能分享一下相关的经验吗?

Eusen commented 5 years ago

嗯,关于让别人接受这一点,我想说,不管是哪个行业,只要想让大家接受你的观点/产品,都需要用心运营。

就跟打造一个爆款产品一样,得去宣传,类似于小米那种。且不说能不能让大家接受,起码能让更多的人看到你的观点,而且还得持续性输出,最好能有大咖带带节奏。

其实为什么说选框架得先看爸爸是谁,爸爸不好的并不一定真的不行,而是公信力不够。所以接不接受,还是得看运营~

phodal commented 5 years ago

哈哈,有机会可以试试,你那有相关的 Demo 应用吗?

Eusen commented 5 years ago

关于我的框架还有很多其他层面的设计,比如插件机制(第三方库分包),异模块实体组件共享(可以在任意模块创建任意组件),http封装,等等。

我也挺愿意学习一些新的东西,可以一块讨论讨论。

Eusen commented 5 years ago

Demo得抽空写一下,我也这两天可以把代码提到github上

Eusen commented 5 years ago

@phodal 感觉大家好像没有理解你的意思啊,理解的太片面了。

文章里提到的理念,主要是为了更规范地分流代码,让非视图层的架构能更通用一些。 我觉得这种想法挺好的,尤其是在一个项目中可能会有多种技术栈存在的情况下。 唯一让我觉得不好的地方就是有点繁琐。 架构这种事,个人主观意愿很重,除非你能完全解决他们的问题,否则他们就有理由再造一堆新轮子。

phodal commented 5 years ago

哈哈,有没有大部分人理解并不重要。关键是自己的总结和改进。

PS:作为一个作者,我知道:大部分人是不会认真去文章的。有时候,他/她评论的是文章的标题,而不是文章的内容。

artshell commented 5 years ago

做为一个Android开发看到这个项目,曾经趟过到坑历历在目

phodal commented 5 years ago

@artshell 来分享一下你的坑

artshell commented 5 years ago

@phodal 你文中已经提到了,MVP那块, Android这边基本不采用 “Android-Clean-Boilerplate” 里边的设计方法,都是自己实现一套MVP,也如@Eusen 提到的 RxJs(Android这边是 RxJava)

artshell commented 5 years ago

android官方去年提供了一套 Lifecycle + ViewModel + LiveData + Room + WorkManager 现在社区基本使用这套方案了, MVP也在用

Eusen commented 5 years ago

感觉总想自己搞一套能解决痛点的框架出来,但是越深入越发现这里面的自由度太高了,就像下围棋一样,每次都能发现新的套路。

artshell commented 5 years ago

@Eusen 是的,oop思想就是这样,越深入就会发现大局观很重要

loveholly commented 4 years ago

实际上关于前后端交互这一块,确实完全是模板化的代码,如果后端是使用类似swagger这种工具的话,完全可以自动生成requestresponse对应的实体信息,以及基于HttpClient的请求层的代码,我们之前在这一块就是基于阿里巴巴的pont工具做了所有请求层代码的自动生成和同步(重写了生成模板,使其与angular风格保持一致)

像下面截图里面的代码全是依赖于swagger和pont的vscode插件自动生成 image