最近业务上遇到一些场景产生的疑问:
假如说我有一套领域模型,定义在 domain/model
,一个模型就是一个 struct ,上面绑定了用于业务的函数(方法)
但这些模型持久化的时候在数据库里可能是另一种格式,这就可能需要另一套模型(数据库模型),可能在 db/model
。
然后开始写接口,然而 request 和 response 又是另一套格式,那这里是不是又需要单独定义一组 struct 用来序列化我的 domain/model
?** 假如 response 结构和原本的 model 只有很小的区别,也需要定义一个新的 struct 用来做序列化吗? **
类似这样的代码一般是怎么组织的?
1
ch2 2022-05-04 18:12:58 +08:00
字段写注解,序列化为 json/yaml 时重新命名
|
2
honkew 2022-05-04 18:14:10 +08:00
注解
|
3
lanlanye OP |
4
Aoang 2022-05-04 18:21:20 +08:00 1
对于结构体的玩法,其实就是 Golang 中的组合与嵌套,和面向对象截然不同。
假定你已经知道并使用过 `omitempty` 等等内容 - 比如最常见的,需要在序列化时忽略一个字段 ```golang type User struct { Username string Password string } func Marshal() { json.Marshal(struct { *User Password string `json:"-"` }{}) } ``` - 添加额外的字段 ```golang type User struct { Username string Password string } func Marshal() { json.Marshal(struct { *User Token bool }{}) } ``` - 字段改名 ```golang type User struct { Username string Password string } func Marshal() { json.Marshal(struct { *User Password string `json:"-"` PasswordHash string }{ User: &User{ Username: "admin", Password: "123456", }, PasswordHash: "123456", }) } ``` |
5
Aoang 2022-05-04 18:23:56 +08:00
@lanlanye #3
未导出字段可以用第三方包,例如 https://github.com/json-iterator/go/blob/master/extra/privat_fields.go#L10 |
7
Aoang 2022-05-04 18:31:48 +08:00
@lanlanye #6 呃呃呃,也不是这个意思。
Golang 写业务算是很麻烦的... 意思其实是,有很多办法可以解决这个问题,但是并不是说那就是最佳实践。 至于类的问题,Golang 的结构体真和类差距很大。个人也不喜欢类,或者说不喜欢 Java 中的类。 代码组织良好的情况下,Golang 的组合能让代码看起来更舒适。也会带来更多的问题,例如,如果你需要写 API 文档,用 swagger 的话,你会发现匿名结构体就是灾难。所以我选择不用 swagger 甚至不写 API |
8
lessMonologue 2022-05-04 19:37:08 +08:00
和 OP 组织代码的方式相同,但是 struct 里的字段都是大写,每个模型都有 DTO 、DO 和 VO ,
|
9
lanlanye OP @Aoang 感谢,我再尝试一下哪种方案更好用吧。
@lessMonologue 全大写的话意味着破坏了封装性,不过如 Python 之类的语言同样也淡化了这方面的限制,我不太清楚这样做是否会带来什么问题。 |
10
TinyKube 2022-05-04 20:29:26 +08:00 via iPhone
我现在用的方案是 request 和 entity 区分开,复杂的 response 自定义 Marshal 方法
|
11
TinyKube 2022-05-04 20:32:49 +08:00 via iPhone
@TinyKube 不过现在从 gorm 转到 ent ,或许可以考虑自定义 Annotation 和 Template 的方案
|
12
janxin 2022-05-04 20:42:44 +08:00
首先这个要看你的实践原则是如何的。如果是 API (Design) First 方式,request/response 是一定需要单的结构体进行,甚至还需要 json schema 验证避免带来的破坏性更新。
如果没有这个前提,API 字段随时可能面对各种变化,那么这个前提下使用相同结构体自然没有什么问题。但是这随时可能带来潜在的破坏性更新。不过这种方式缺点也很明显,会给其他人明显的前置条件阻碍,可能产生臃肿的 API 字段(无用的结果输出),缺少文档之类的。 我个人建议团队的实践是根据不同的作用域使用单独的模型,也就是 Java 中常说的 DTO 之类的概念。这样可以有效的把风险隔绝在自己的作用域中,有效防止一次修改到处救火的问题。 |
13
lanlanye OP @Aoang
@lessMonologue @TinyKube 谢谢各位回复,最后在翻阅标准库的注释时找到了比较官方的解决方案:实现 Marshaler 和 Unmarshaler 接口即可,也就是说为每个 struct 定义 MarshalJSON 和 UnmarshalJSON 方法,之后即可使用标准库 json 进行自定义的序列化和反序列化,也解决了私有属性的序列化问题。 |