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

49 天前
 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


3450 次点击
所在节点    程序员
27 条回复
raphaelsoul
49 天前
很好的分享 技术栈和时间都和你差不多。
但我这几年下来的感想是 不能再花太多精力在注重技术栈和翻来覆去的重构重写了。
产品优先 出活优先
cuijiajun
49 天前
学到了
qsnow6
49 天前
行业铁律“过早优化是原罪”,技术是为产品服务的,技术层面花里胡哨产品却不好好设计,最终就是被市场淘汰。
Leviathann
49 天前
js/ts 的生态确实是现在开发 web app 的最优解
S4msara
49 天前
非常棒的分享,充分的说明了“技术以业务导向,技术为业务赋能”👍
zzdgfv
49 天前
强,涉及太多了
june4
49 天前
我这几年也全用 typescript 前后端一把梭,统一语言优势太大了。何况 js/ts 语言本身写起来很舒服。
唯一缺点内存占用比 rust/go 大一些,对小鸡要放一堆程序不友好。
horizon
49 天前
真能折腾啊。。怎么坚持下来的
你的 CRM 有链接吗,可以试用一下吗?
CRM 中有工作流、审批流之类的吗,怎么实现的
C0dEr
49 天前
首先佩服 OP 的坚持和能力,但能不能至少给个架构,功能说明啥的,方便理解先?
sunchuo
48 天前
为啥不用 PHP 呢。
sunchuo
48 天前
看下来感觉 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
48 天前
@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
48 天前
@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
48 天前
@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
48 天前
@qsnow6 有道理,但是被市场淘汰的是企业、公司,而不是折腾技术的个人,不折腾技术的个人反而会被行业淘汰。如果有公费折腾的机会,就应该好好利用。
wenerme
48 天前
@raphaelsoul

> 产品优先 出活优先



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

这样的论调都是类似的,都是站在公司的角度,而不是个人的角度。我觉得两者是相辅相成的。
horizon
48 天前
@wenerme
你这个仓库挺难看懂的。。。说实话
就你发了这么多链接,我发现还是你现在这个帖子还能看懂
demo 里啥也没有啊。。。
ixixi
48 天前
我也写过 crm 你们卖的咋样
mark2025
48 天前
确定了要先 GRPC 的方式开发
========
为啥呢?
wenerme
48 天前
@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