DAO 层和 ORM,能区分,但又不完全能区分,我裂开了

2021-05-06 14:22:54 +08:00
 waibunleung

desc:是这样的,小弟从 php 转到 go 一个月时间不够,项目的目录结构需要自己搭建。于是就找网上的项目组织目录参考,发现有一部分目录结构里有 dao 层的分层概念。这个在 java 里面比较常见,在 php 中用得比较多的是 orm,不知道 go 中是不是也适合这样搞。

于是引起了我一顿梳理 dao 和 orm 之间的关系。但仍有如下疑问:

  1. 究竟项目使用了 orm 之后,还需不需要再分一层 DAO 层出来?如果要分,此时的 DAO 层里面写的是什么逻辑?求 demo 举例
  2. ORM 不是正好替代了 DAO 的角色吗?还是说 DAO 可以通过 ORM 来实现?此时 DAO 层的实现是怎么样的?在 ORM 外面包一层,意义在哪里?
  3. 在项目代码分层上(语言无关),DAO 和 ORM 分别是什么层面上的东西?

真的纠结了三四天了,每天看完都有不同的结论,求大佬们指点一下你们在项目中是怎么划分的~

7527 次点击
所在节点    程序员
78 条回复
zjsxwc
2021-05-06 17:20:01 +08:00
labulaka521
2021-05-06 17:25:48 +08:00
dao 差不多就是包装了 curd 的一些方法吧,比如每次查询都会自动加上一些 id 、deletetime 等信息,然后接口更加的具体化,比如说 saveUser updateUser getUser 等
waibunleung
2021-05-06 17:32:00 +08:00
@zjsxwc 如果要说 Repository 就是 dao 的实现的话,那我们之前只是将 Repository 层的函数,放到了 model 层去,没有分这一层出来,这样 model 就 = model + dao 了,那为什么不将 dao 合并到 model 这一层呢?
waibunleung
2021-05-06 17:33:14 +08:00
@labulaka521 这样看起来这些接口是业务驱动的,而不是用 orm 再封装一层
newtype0092
2021-05-06 17:40:04 +08:00
@waibunleung #18 是的,正常来说,把所有数据操作封装在一起( DAO 类),好处远远大于开销,比如:

能方便的复用之前的逻辑,不用再复制粘贴一遍
可以快速查看在当前数据集上有哪些操作
调整下层数据结构或添加删除参数时不用去找每一个调用的地方
需要整个替换掉 ORM 时改动不会太大
测试时方便 mock 数据
等等等。

而开销仅仅是多几个 DAO 类。

哪怕只是为了让代码结构更清晰,这几个类都是有必要的。
newtype0092
2021-05-06 17:48:43 +08:00
@waibunleung #23 简单的项目结构里 model 充当 DAO 的设计很合理,轻便且能满足大部分需求
zjsxwc
2021-05-06 18:14:58 +08:00
@waibunleung
用 repository 当然是为了通过依赖注入容器搞黑魔法了,比如运行 aop 面向切面编程。

如果都写到实体类 entity (也就是你说的 model )中
zjsxwc
2021-05-06 18:16:08 +08:00
就相当于写死了,有 1 万个实体类,难道注入同样的东西一万遍?
zjsxwc
2021-05-06 18:19:18 +08:00
如果不用实体类实例方法搞,而用是在实体类的中用静态方法就更惨,要修改静态方法的行为怕是得 jvm 底层修改了,而显然我们动不了 jvm 代码。
waibunleung
2021-05-06 18:27:58 +08:00
@zjsxwc repository 通过依赖注入到容器,有没有例子呀?
zjsxwc
2021-05-06 18:37:33 +08:00
@waibunleung

这个你去看看 springboot 或者 symfony 的容器相关文档吧
waibunleung
2021-05-06 19:22:42 +08:00
@zjsxwc 不对啊,如果像你说的要用 repository 依赖注入到容器,那容器调用的时候不需要统一 repository 的接口方法吗?比如我 UserRepository 有 queryList 方法,ContentRepository 却没有 queryList 方法,那注入到容器的时候,不就会报错?
zjsxwc
2021-05-06 19:40:43 +08:00
@waibunleung
不同的类注入,当然是各自的类型了,不会有你说的情况。
di94sh
2021-05-06 20:06:52 +08:00
dao 还是很有必要的,项目大了后 orm 对象满天飞 的话无法重构,单元测试也不好写,业务逻辑也不容易理清
waibunleung
2021-05-06 20:24:01 +08:00
@zjsxwc symfony 的 容器注入,不是没有类型吗?
zjsxwc
2021-05-06 20:51:25 +08:00
@waibunleung
手动注入需要类型 class
自动注入 autowire 会根据参数类型自动确定
fkdog
2021-05-06 21:59:27 +08:00
@Rwing 大部分的项目其实短平快方式效率更高。

设计模式、项目架构一类东西有时候过于工业化和笨重了。

特别是在国内,大部分互联网公司并不会太过于在意项目的可扩展性和维护性。为了跟进对手或者抢占市场都是以先实现功能为主,反正用的 java,代码写的再烂也能勉强调试维护下去。真的维护不动了就推翻重构或者重写。

如果项目竞争不过对手的话,这个项目可能还没到需要重写的地步可能就被关闭了。所以在国内的话,什么代码整洁、项目结构合理之类的东西并不能产生价值。

国外的话情况可能会不一样做项目就跟建房子一样,一步一步稳稳的来。像美帝那边的大厦都上百年了,国内的话可能三四十年就拆了重建吧。
THP301
2021-05-06 22:01:02 +08:00
生搬硬套不一定合适
janus77
2021-05-06 22:05:43 +08:00
ORM 基本上是单纯的从 db 里面拿数据
有些情况下需要对数据进行组装和变换,这些操作按规范是不放在 ORM 的,于是就多出一层 Dao
但是由于 java 的“过于规范化”(这点我觉得不完全是坏事,因场景而异),所以不需要 dao 处理的情况也需要先经过一层 dao,这是为了统一。
FreeEx
2021-05-06 22:41:40 +08:00
前段时间为了学习 Go 写了个开源项目,分了 service 层和 repository 层,repository 主要是用来操作某一个表的,例如根据 ID 查询可能会很多地方用到,repository 可以写一个 FindById 函数,参数是 ID,返回是数据库表的结构体,这样就不必每次都去拿全局 DB.Where("id = ?", id).First(&User{}) 查询了,也更方便检索有哪些地方用了这个函数。service 层主要也是写一个会复用到的函数,不复用的直接就用 repository 操作了。
项目地址是: https://github.com/dushixiang/next-terminal
代码写的比较水,楼主可以参考下。

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

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

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

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

© 2021 V2EX