:::tip 总结
非常简单的尝试,很快就失败了,目标并不明确。 对 CRM 并不了解,过于盲目。 堆砌了一些基础前端组件。前端开发能力尚不成熟。
:::
:::tip 总结
后端
后端尝试构建基于 Schema 的 CRM ,但过于动态,过于灵活导致逻辑开发复杂。 对 NodeJS 后端开发并不足够了解,目标更像是一个 low-code 后端,但是支持 CRM 实体。 这个阶段对 CRM 逻辑有了一些了解。
前端
前端选择 Antd ,发现阻力越来越大,实现定制化很难。 这时的前端开发能力还相对欠缺,对生态还不够了解。
:::
:::tip 总结
这个阶段是历时最长的,除了核心的 CRM 还完成了其他的一些附属模块。
后端
后端使用 Golang 开发上进行了一些探索,前期 gorm+restful 方式代码量大且重复。 之后选择 ent+gqlgen+自定义生成代码。
前端
前期基于 Blueprint 快速实现大多功能,但还是因为经验不足,很多东西实现有缺陷。但实现了初期原型,达到了想要的结果。
:::
1
raphaelsoul 125 天前 2
很好的分享 技术栈和时间都和你差不多。
但我这几年下来的感想是 不能再花太多精力在注重技术栈和翻来覆去的重构重写了。 产品优先 出活优先 |
2
cuijiajun 125 天前
学到了
|
3
qsnow6 125 天前 1
行业铁律“过早优化是原罪”,技术是为产品服务的,技术层面花里胡哨产品却不好好设计,最终就是被市场淘汰。
|
4
Leviathann 125 天前
js/ts 的生态确实是现在开发 web app 的最优解
|
5
S4msara 125 天前
非常棒的分享,充分的说明了“技术以业务导向,技术为业务赋能”👍
|
6
zzdgfv 125 天前
强,涉及太多了
|
7
june4 125 天前
我这几年也全用 typescript 前后端一把梭,统一语言优势太大了。何况 js/ts 语言本身写起来很舒服。
唯一缺点内存占用比 rust/go 大一些,对小鸡要放一堆程序不友好。 |
8
horizon 125 天前 1
真能折腾啊。。怎么坚持下来的
你的 CRM 有链接吗,可以试用一下吗? CRM 中有工作流、审批流之类的吗,怎么实现的 |
9
C0dEr 125 天前
首先佩服 OP 的坚持和能力,但能不能至少给个架构,功能说明啥的,方便理解先?
|
10
sunchuo 125 天前
为啥不用 PHP 呢。
|
11
sunchuo 125 天前
看下来感觉 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 的数据)。 |
12
wenerme OP @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 |
13
wenerme OP @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 |
14
wenerme OP @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 |
16
wenerme OP @raphaelsoul
> 产品优先 出活优先 和 > 行业铁律“过早优化是原罪”,技术是为产品服务的,技术层面花里胡哨产品却不好好设计,最终就是被市场淘汰。 这样的论调都是类似的,都是站在公司的角度,而不是个人的角度。我觉得两者是相辅相成的。 |
18
ixixi 124 天前
我也写过 crm 你们卖的咋样
|
19
mark2025 124 天前
确定了要先 GRPC 的方式开发
======== 为啥呢? |
20
wenerme OP @mark2025
> 确定了要先 GRPC 的方式开发 > ======== > 为啥呢? 因为当时很 buy in buf[1] 那一套, 其实现在也还是能接受,如果是需要 rpc/server to server ,我还是可能会考虑 grpc ,或者实现一个简单的 rpc ,但目前减少了 server to server 这一层,部分逻辑还是保留,目前以 gql 直接暴露给前端为主。 [1]: https://github.com/bufbuild/buf |
21
wenerme OP |
24
yrj 124 天前
敢于折腾,并把过程总结出来,值得佩服。
|
25
xiaoshan5733 124 天前
佩服,思考总结值得学习。我也经常在技术选型上花费大量时间,有时候确实容易陷入技术思维。OP 在做技术选型时一般会考虑哪些因素呢?
|
26
wenerme OP @xiaoshan5733
> 在做技术选型时一般会考虑哪些因素呢? 先做个方面了解,逐步筛选,锚定一些核心的,再围绕核心的技术去做选择。 一般我们说单有了锤子,看什么都是钉子。 然后再增加不同领域的锤子,逐步叠加就好。 例如数据库,刚开始的时候,数据库都是 mysql ,后来自己能做决定了,开始去了解,了解到 pg ,发现真的很不错。然后需要做分析的时候,也了解了一些例如 clickhouse 之类的等等,但是最终要得到一个 **trade off**,每个技术方向都是有价值的,但是做技术选择是有自己的背景的,多积累这样的 trade off ,会帮助之后做选择。 因此大约在 pg 10 用到了现在 15 、16 ,当需要 OLAP 场景的时候,也尝试通过 pg 解决,当需要搜索,需要做 AI/向量搜索,也是在通过 pg 解决。这里锚定的点就是 pg ,锚定了这个基本点后,可以更加深入的去使用 pg 的一些功能,例如我不会介意直接在 pg 里写 js (用 pg v8 )。 例如 OS ,选择了 alpine ,之后 os 相关的场景 物理机、镜像、容器等 全都是 alpine ,得到的回报值得投入。 前端后端之类的同理,只是这些领域锚定的内容可能还会有变化,例如前端一开始 antd ,然后变为 tw 。后端确定 nodejs 后,一开始 nestjs rest 然后 gql 反复,但最终能 settle down 到一些固定的技术栈。这些也都是自己的技术投资,非常乐意沟通和分享这些内容。 这些核心的点是不会变的,但是围绕这些点的技术是会不断发展的,新的的东西也在不断涌现,保持好奇和兴趣,从 程序员 变为 工程师。搞这些真的好玩 😄。 到现在,原意分享,帮助别人去用 alpine 、pg 这些也是一种乐趣,非常有意思。 |
27
hutoer 93 天前
RPC 我倒是觉得 https://github.com/moleculerjs/moleculer 比较简单
|