多年 CRM 开发的心路历程: Java -> Golang -> NodeJS

2024-08-23 16:38:28 +08:00
 wenerme

v1

:::tip 总结

非常简单的尝试,很快就失败了,目标并不明确。 对 CRM 并不了解,过于盲目。 堆砌了一些基础前端组件。前端开发能力尚不成熟。

:::

v2

:::tip 总结

后端

后端尝试构建基于 Schema 的 CRM ,但过于动态,过于灵活导致逻辑开发复杂。 对 NodeJS 后端开发并不足够了解,目标更像是一个 low-code 后端,但是支持 CRM 实体。 这个阶段对 CRM 逻辑有了一些了解。

前端

前端选择 Antd ,发现阻力越来越大,实现定制化很难。 这时的前端开发能力还相对欠缺,对生态还不够了解。

:::

v3

:::tip 总结

这个阶段是历时最长的,除了核心的 CRM 还完成了其他的一些附属模块。

后端

后端使用 Golang 开发上进行了一些探索,前期 gorm+restful 方式代码量大且重复。 之后选择 ent+gqlgen+自定义生成代码。

前端

前期基于 Blueprint 快速实现大多功能,但还是因为经验不足,很多东西实现有缺陷。但实现了初期原型,达到了想要的结果。

:::

v4

v5

v6


4707 次点击
所在节点    程序员
27 条回复
raphaelsoul
2024-08-23 16:50:00 +08:00
很好的分享 技术栈和时间都和你差不多。
但我这几年下来的感想是 不能再花太多精力在注重技术栈和翻来覆去的重构重写了。
产品优先 出活优先
cuijiajun
2024-08-23 17:00:01 +08:00
学到了
qsnow6
2024-08-23 17:12:23 +08:00
行业铁律“过早优化是原罪”,技术是为产品服务的,技术层面花里胡哨产品却不好好设计,最终就是被市场淘汰。
Leviathann
2024-08-23 17:14:11 +08:00
js/ts 的生态确实是现在开发 web app 的最优解
S4msara
2024-08-23 17:29:28 +08:00
非常棒的分享,充分的说明了“技术以业务导向,技术为业务赋能”👍
zzdgfv
2024-08-23 17:33:03 +08:00
强,涉及太多了
june4
2024-08-23 17:40:19 +08:00
我这几年也全用 typescript 前后端一把梭,统一语言优势太大了。何况 js/ts 语言本身写起来很舒服。
唯一缺点内存占用比 rust/go 大一些,对小鸡要放一堆程序不友好。
horizon
2024-08-23 17:53:55 +08:00
真能折腾啊。。怎么坚持下来的
你的 CRM 有链接吗,可以试用一下吗?
CRM 中有工作流、审批流之类的吗,怎么实现的
z67nnciQnb7r8bLf
2024-08-23 19:06:24 +08:00
首先佩服 OP 的坚持和能力,但能不能至少给个架构,功能说明啥的,方便理解先?
sunchuo
2024-08-23 21:42:56 +08:00
为啥不用 PHP 呢。
sunchuo
2024-08-23 22:02:27 +08:00
看下来感觉 op 是想后端尽可能少写代码,前后端尽可能少的重复劳动。所以一开始朝着 lowcode 方向走了。
我做过几乎一样的事情。

但是后来发现。真正的业务逻辑千奇百怪,很难做到「不写代码」,哪怕是有 lowcode 平台能实现这些复杂业务逻辑,那配置的过程就相当于「写代码」了。可能要做一些取舍。不能沉迷于全部自动实现。😂


我的实现大概是这样:

1. 定义数据结构,包括:字段校验;状态机;筛选、搜索字段、字段变更事件、字段监听事件、持久化方式等。
2. 基于数据结构,自动生成齐备的 curd restful 接口;也可以自定义接口,请求响应的 schema (可引用数据结构)。
3. 可以通过定义的接口直接生成 openapi3.1 的 schema ,进而生成文档。
4. 自动实现路由、参数校验、权限校验等。
5. 任意接口可以自己接管、实现更具体的业务逻辑。
6. 可以自动生成前端请求接口的 sdk 。前端不用调试接口,直接用。
7. 基于接口 schema ,生成描述表单、列表、详情的 jsonschema ,然后前端实现类似 react-form-schema 、amis 的渲染引擎;也实现了前后端不分离的,生成 html 的服务端渲染引擎。

8. 具体的业务逻辑还是手写。在合适的地方引用列表、表单、详情的组件(引擎渲染 jsonschema + sdk 的数据)。
wenerme
2024-08-24 02:22:13 +08:00
@horizon 如果写代码不折腾,不有趣,就只剩下 996 了。
这是前端部分公共的内容 https://github.com/wenerme/wode/tree/main/packages/console
这是后端部分的公共内容 https://github.com/wenerme/wode/tree/main/packages/nestjs

这是一个假的 demo https://wode.vercel.app/console fake 的账号密码 admin admin
wenerme
2024-08-24 02:29:42 +08:00
@sunchuo

> 看下来感觉 op 是想后端尽可能少写代码,前后端尽可能少的重复劳动。所以一开始朝着 lowcode 方向走了。
我做过几乎一样的事情。

是这样的,但 lowcode 根本不可满足业务需求,只能通过大量的代码定义去减少重复的工作内容。

例如

```ts
export const LeadResource = defineResource({
name: 'Lead',
idType: 'lead',
title: '线索',
icon: <ActiveToggleIcon icon={BsTelephone} iconActive={BsTelephoneFill} />,
metadata: {},
});
```

这样能通过扩展和维护这个 Resource 构建大多元素,例如

```ts
defineMetadata(LeadResource, ResourceListViewSelectorMetaKey, {
views: [
{
label: '开放线索',
value: 'open',
query: {
filters: [`state = "${LeadStatusType.state.Open}"`],
},
},
],
});
```


不少内容和你的实现都有类似的地方,比较有意思, 只不过我大方向选择的 GQL 。

但我会尽量避免生成,而是通过动态去创建,目前主要用到生成的是 ts 的 interface 生成 zod 、typebox ( jsonschema ,但是有类型)。

> 但我会尽量避免生成,而是通过动态去创建

主要是方便修改复用,生成时怕的是生成后改不动会形成包袱。动态构建例如

```ts
export function createListPayload<T extends object>(Type: Constructor<T>): Constructor<PageResponse<T>> {
let name = getObjectName(Type);
let key = `${name}ListPayload`;
return computeIfAbsent(getTypeCache(), key, () => {
@ObjectType(key)
class ListPayload {
@Field((type) => Int)
total!: number;
@Field((type) => [Type])
data!: T[];
}

return ListPayload;
});
}
```

对查询方法也适用,可以按需增加查询方法,例如 https://github.com/wenerme/wode/blob/f846c2158ff83ad7fcde781abd29ef7505f11258/packages/nestjs/src/type-graphql/resource/withBaseQuery.ts#L11
wenerme
2024-08-24 02:33:48 +08:00
@C0dEr

> 但能不能至少给个架构,功能说明啥的,方便理解先?

我的仓库里大多都是笔记性质的 https://wener.me/story/how-i-note / https://www.v2ex.com/t/1058208#reply2
我一般用笔记来索引这些信息。

> 架构,功能

一般我会以总结的方式形成类似 Design XXX 这样的,design 目录下有不少这样的内容,我一般主要参考学习别人现有的,然后总结沉淀自己的。

https://wener.me/notes/dev/design/schema
https://wener.me/notes/dev/design/erp
https://wener.me/notes/dev/design/ao-factory
wenerme
2024-08-24 02:35:51 +08:00
@qsnow6 有道理,但是被市场淘汰的是企业、公司,而不是折腾技术的个人,不折腾技术的个人反而会被行业淘汰。如果有公费折腾的机会,就应该好好利用。
wenerme
2024-08-24 02:37:33 +08:00
@raphaelsoul

> 产品优先 出活优先



> 行业铁律“过早优化是原罪”,技术是为产品服务的,技术层面花里胡哨产品却不好好设计,最终就是被市场淘汰。

这样的论调都是类似的,都是站在公司的角度,而不是个人的角度。我觉得两者是相辅相成的。
horizon
2024-08-24 10:31:01 +08:00
@wenerme
你这个仓库挺难看懂的。。。说实话
就你发了这么多链接,我发现还是你现在这个帖子还能看懂
demo 里啥也没有啊。。。
ixixi
2024-08-24 10:48:57 +08:00
我也写过 crm 你们卖的咋样
mark2025
2024-08-24 13:50:39 +08:00
确定了要先 GRPC 的方式开发
========
为啥呢?
wenerme
2024-08-24 14:41:36 +08:00
@mark2025

> 确定了要先 GRPC 的方式开发
> ========
> 为啥呢?

因为当时很 buy in buf[1] 那一套, 其实现在也还是能接受,如果是需要 rpc/server to server ,我还是可能会考虑 grpc ,或者实现一个简单的 rpc ,但目前减少了 server to server 这一层,部分逻辑还是保留,目前以 gql 直接暴露给前端为主。


[1]: https://github.com/bufbuild/buf

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/1067291

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX