全栈工程师修炼指南
熊燚(四火)
Oracle 首席软件工程师
32206 人已学习
新⼈⾸单¥59
登录后,你可以任选4讲全文学习
课程目录
已完结/共 46 讲
全栈回顾 (1讲)
加餐 (1讲)
全栈工程师修炼指南
15
15
1.0x
00:00/00:00
登录|注册

08 | MVC架构解析:模型(Model)篇

包含数据和逻辑
逻辑被解耦到无状态 Service 中
返回结果,不改变数据状态
改变数据状态
基础设施层
数据访问层
Service
Facade
面向对象的设计
包含数据和逻辑
逻辑被解耦到无状态 Service 中
模型实体不包含逻辑,只包含状态
关注领域范围内模型实体之间的关系
查询对象设计模式
SQL基础
Martin Fowler 的文章
命令和查询分离
充血模型
贫血模型
查询
命令
命令和查询分离
分层方式
充血模型
贫血模型
领域模型
对真实世界问题的抽象和简化
扩展阅读
CQRS 模式
贫血模型和充血模型
CQRS 模式
内部层次划分
贫血模型和充血模型
模型
总结思考
概念
MVC架构解析:模型(Model)篇

该思维导图由 AI 生成,仅供参考

你好,我是四火。
在上一讲中,我们了解了 MVC 这个老而弥坚的架构模式,而从这一讲开始,连同第 09、10 讲共计 3 篇,我将分别展开介绍 MVC 三大部分内容。今天我要讲的就是第一部分——模型(Model)。

概念

首先我们要了解的是,我们总在谈“模型”,那到底什么是模型?
简单说来,模型就是当我们使用软件去解决真实世界中各种实际问题的时候,对那些我们关心的实际事物的抽象和简化。比如我们在软件系统中设计“人”这个事物类型的时候,通常只会考虑姓名、性别和年龄等一些系统用得着的必要属性,而不会把性格、血型和生辰八字等我们不关心的东西放进去。
更进一步讲,我们会谈领域模型(Domain Model)。“领域”两个字显然给出了抽象和简化的范围,不同的软件系统所属的领域是不同的,比如金融软件、医疗软件和社交软件等等。如今领域模型的概念包含了比其原本范围定义以外更多的内容,我们会更关注这个领域范围内各个模型实体之间的关系
MVC 中的“模型”,说的是“模型层”,它正是由上述的领域模型来实现的,可是当我们讲这一层的时候,它包含了模型上承载的实实在在的业务数据,还有不同数据间的关联关系。因此,我们在谈模型层的时候,有时候会更关心领域模型这一抽象概念本身,有时候则会更关心数据本身
确认放弃笔记?
放弃后所记笔记将不保留。
新功能上线,你的历史笔记已初始化为私密笔记,是否一键批量公开?
批量公开的笔记不会为你同步至部落
公开
同步至部落
取消
完成
0/2000
荧光笔
直线
曲线
笔记
复制
AI
  • 深入了解
  • 翻译
    • 英语
    • 中文简体
    • 中文繁体
    • 法语
    • 德语
    • 日语
    • 韩语
    • 俄语
    • 西班牙语
    • 阿拉伯语
  • 解释
  • 总结

本文深入解析了MVC架构中的模型(Model)部分,重点介绍了贫血模型和充血模型的概念及其在面向对象设计中的应用和优劣势。此外,还探讨了CQRS(Command Query Responsibility Segregation,命令查询职责分离)模式在模型设计中的应用,将命令和查询行为解耦开的设计方式。通过比较贫血模型和充血模型的特点以及CQRS模式的应用,全面解析了MVC架构中模型部分的重要概念和设计原则。读者可以从中深入理解MVC架构中模型层的内部结构和设计原则,以及CQRS模式的优势和适用场景。文章内容涵盖了贫血模型和充血模型的理解、CQRS模式的应用以及模型层的内部层次划分方法,为读者提供了有益的参考和思考。

仅可试看部分内容,如需阅读全部内容,请付费购买文章所属专栏
《全栈工程师修炼指南》
新⼈⾸单¥59
立即购买
登录 后留言

全部留言(20)

  • 最新
  • 精选
  • Luciano李鑫
    置顶
    1.大型、复杂项目用贫血模型多一些,小型、简单项目用重写模型多一些。 2.在命令查询的时候,针对add可以返回自增id。在update的时候返回修改记录数。

    作者回复: 关于 2,你说的返回自增 id 是一种很好的方法,很常用,但是它破坏了 CQRS 对于 Command 的 fire-and-forget 的要求。 当然,也有一些其它的办法,比如将 id 在客户端生成(这个生成可以是客户端自己生成,也可以是调用服务端的某一个生成 id 的接口生成),但这却又破坏了 REST 接口中 Create 方法不指定 id 的规约。 再有一个办法,是在客户端生成一个非主键、但被索引的特殊 id,例如 GUID,这样,生成了记录之后,客户端可以使用这个 GUID 去服务端获取这条数据,当然,缺点是需要额外的一列来放置 GUID,且要保证 GUID 的唯一性。 所以,上面介绍了三种方法,各有利弊,都比较常用。当然,也有一些其它的方法。

    2019-09-29
    9
  • Luciano李鑫
    请问,为什么说在贫血模型中,service增是无状态的,而model增是有状态的,这个“状态”是怎么定义的呢?

    作者回复: 状态,其实就是数据。 Service 提供了一系列的过程方法,入参进,结果出,但是 Service 并未发生变化。 Model 则相反,可以创建、修改、删除,这就是状态的变化。

    2019-09-29
    10
  • tt
    赞,这是我订阅的极客时间的课程中为数不多的重度偏向概念与逻辑的课程。 我本人很少做WEB开发,只是使用过面向对象,这节课一下子就让我领悟了面向对象、面向服务、面向过程、贫血和充血模型。 程序就是数据结构加算法,业务就是数据加逻辑。从数据和逻辑这两个纬度可以画一个二维四象限图: | 数据 | 逻辑 ------------------------------------------------ 数据 | 承载模型状态,开 | 面向对象 | 放操作状态的接口 | 充血模型 ------------------------------------------------ 逻辑 | 面向对象 | 面向服务 | 充血模型 | 无状态,restful 虽然很少做WEB开发,但了解过一些内部使用的框架,都有Service和DA,今天知道了,它的名字是贫血模型。 那从本节课出发,微服务也是把服务拆分,一个目的就是可以快速水平扩展,那应该就大量使用RESTFUL风格。 但逻辑和数据终归要碰面,ORM就是二者之间的桥梁,对外展示不同的面孔。

    作者回复: 感谢回复。 关于你提到的“概念与逻辑”,做个说明。 我是这样认为的,学习全栈技术比较忌讳仅仅学习单个的具体技术,之前的文章和回复中我也多次提到过,毕竟技术种类花样繁多,我们还是需要适当做一些抽象,理解一些通用和共性的东西,既包括一些概念,也包括一些套路(模式)。当然,我们需要通过许多例子来理解它们,这是没错的。

    2019-09-27
    5
  • 就叫Hugo也行
    在计算机行业的dargon中,“数据”的同义词是“状态”。“逻辑”的同义词是“服务”、“行为”、“函数”或“功能”。

    作者回复: 👍

    2019-10-12
    4
  • 零维
    请问老师,如果 CQRS 的命令操作有了返回值会有什么弊端吗?就像举的改进版的示例,如果直接让服务端在 add 操作之后返回 id 会有什么不好的影响吗?

    作者回复: 好问题。我的理解是,返回值就一定程度上意味着解耦可能不够彻底,因为 Command 的理想状况是 fire and forget,所以它才是要求无返回的,存在返回值意味着有一部分逻辑可以拿出去变成 Query。客户端只知道执行成功了,或者执行失败了(有异常抛出)。但是我们在应用到实际系统中的时候,不一定能够做到那么纯粹。

    2019-10-22
    3
  • Geek_89bbab
    cqrs中发送command服务方还是需要返回一些成功失败应答的吧,不可能啥都不返回吧。 这里有些疑惑:发送一个创建对象的command,然后立刻去查询,发现没有查询到怎么办,可能是内部服务挂掉,也可能是数据同步慢?对于前端怎么显示比较好。

    作者回复: 严格的CQRS来说的话,命令要返回执行结果,但是不返回写入成功的完整对象。 我认为,在CQRS的情况下,如果写入操作返回成功,写入后的查询没有查询到,无论可能是数据一致性的问题(bug),还是数据访问竞争的问题(非bug),对于该次查询来说,都属于查询操作无法找到数据的场景,按照它结合业务来设计。

    2020-06-11
    1
  • 易儿易
    看完这讲之后,把上一节给老师提的一个问题自行明白了~ 贫血模型和充血模型区别是Model类是否包含业务逻辑,我之前理解错了,错误的以为是以service层是否包含记录数据的成员变量为区分的(其实这个叫做有无状态) 那这样理解起来的话,平时的springMVC使用action-service-dao三层的都是贫血模式,我在使用面向对象实现业务时把javabean设置为scope=property并把业务逻辑也写在javabean中、不使用三层结构的模式应该属于充血模式。 另外有个小疑问:贫血模式下Model层还剩下什么,参数对象?pojo?返回值对象?Model是不是已经名存实亡了?

    作者回复: 无论是贫血模式还是充血模式,Model 层包含的内容都是一致的:逻辑+数据,区别是前者把它们分开、分别容纳在无逻辑的 Model 实体对象和无状态的 Service 中,后者则是统一存放在有状态且有逻辑的 Model 实体对象中。你的疑问是贫血模型下脱离 Service 之后的 Model 实体对象吧,它们只剩下数据,而很少包含,或者不包含逻辑,特别是业务逻辑。

    2019-09-30
    2
    1
  • 编程爱好者
    1.两者都有过,我个人更喜欢贫血模型,职责分明些。 2.既然不知道,可以让服务器来告诉你。类似于tcp中的syn,ack机制,可以为这次请求增加一个消息编号,然后服务器可以通过这个消息编号告诉方法调用者。 思考作者为什么这么设计课程,感觉很多内容是架构设计里面涉及的内容-权衡与取舍。

    作者回复: 关于第 2 问,消息编号的想法很好,但是,消息编号是消息编号,而写入数据库对象的 id 是对象的 id,二者并没有必然联系啊。

    2019-09-28
    2
    1
  • paperen
    1、贫血与充血都有用,但贫血更多,就是封装到service中实现跟逻辑相关的动作 2、对于CQRS模式add方法想获取新增的bookid,是不是可以对book模型增加一个方法叫getLastAddId来获取最近添加的bookid?但我还是觉得不需要做得那么极尽,可能还没深刻理解,觉得add保留返回bookid对解耦并没关系呢

    作者回复: 关于 #2,这个getLastAddId谁来调用?如果是用户侧来调用,怎么保证在新增book之后,调用这个方法之前,没有别的book添加?

    2020-06-01
  • pyhhou
    1. 平时实现业务没有特别涉及到面向对象的概念,都是设计一个个分离的函数,最多是将职责相近的函数放在同一个目录下,不会包装成类,model 层的操作(DB 操作)也都在一个个函数里面完成,这应该算是充血模型? 2. 看了一下评论以及老师的回复,感觉各种做法都有利弊,思考分析了一下: i) 直接返回所需要的 id:这样是方便,但是违反了 CQRS 的原则 ii) 通过设置全局变量进行交互:如果遇到并行处理的情况会比较麻烦 iii) 命令下达后再调用查询,通过时间戳来找到最近的一次操作:同样要考虑并行处理,而且如何找到最近的一次时间戳也是一个值得思考的问题 iv) 交由客户端处理:会产生其他的问题 总之是要根据具体的场景进行权衡

    作者回复: 关于 1) 这个理解不对。你可以再阅读一下文章,里面有说贫血模型和充血模型是根据什么来区别的。

    2019-10-02
收起评论
显示
设置
留言
20
收藏
沉浸
阅读
分享
手机端
快捷键
回顶部