新项目,不妨采用这种架构分层,很优雅!

这篇具有很好参考价值的文章主要介绍了新项目,不妨采用这种架构分层,很优雅!。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

大家好,我是飘渺。今天继续更新DDD&微服务的系列文章。

在专栏开篇提到过DDD(Domain-Driven Design,领域驱动设计)学习起来较为复杂,一方面因为其自身涉及的概念颇多,另一方面,我们往往缺乏实战经验和明确的代码模型指导。今天,我们将专注于DDD的分层架构和实体模型,期望为大家落地DDD提供一些有益的参考。首先,让我们回顾一下熟悉的MVC三层架构。

1. MVC 架构

在传统应用程序中,我们通常采用经典的MVC(Model-View-Controller)架构进行开发,它将整体的系统分成了 Model(模型),View(视图)和 Controller(控制器)三个层次,也就是将用户视图和业务处理隔离开,并且通过控制器连接起来,很好地实现了表现和逻辑的解耦,是一种标准的软件分层架构。

在遵循此分层架构的开发过程中,我们通常会建立三个Maven Module:Controller、Service 和 Dao,它们分别对应表现层、逻辑层和数据访问层,如下图所示:

新项目,不妨采用这种架构分层,很优雅!

(图中多画了一个Model层是因为 Model 通常只是简单的 Java Bean,只包含数据库表对应的属性。有的应用会将其单独抽取出来作为一个Maven Module,但实际上它可以合并到 DAO 层。)

1.1 MVC架构模型的不足

在业务逻辑较为简单的应用中,MVC三层架构是一种简洁高效的开发模式。然而,随着业务逻辑的复杂性增加和代码量的增加,MVC架构可能会显得捉襟见肘。其主要的不足可以总结如下:

  • Service层职责过重:在MVC架构中,Service层常常被赋予处理复杂业务逻辑的任务。随着业务逻辑的增长,Service层可能变得臃肿和复杂。业务逻辑有可能分散在各个Service类中,使得业务逻辑的组织和维护成为一项挑战。
  • 过于关注数据库而忽视领域建模:虽然MVC的设计初衷是对数据、用户界面和控制逻辑进行分离,但它在面对复杂业务场景时并未给予领域建模足够的重视。这可能导致代码难以理解和扩展,因为代码更像是围绕数据库而不是业务需求进行设计。
  • 边界划分不明确:在MVC架构中,顶层设计上的边界划分并没有明确的规则,往往依赖于技术负责人的经验。在大规模的团队协作中,这可能导致职责不清晰、分工不明确等问题。
  • 单元测试困难:在MVC架构中,Service层通常以事务脚本的方式进行开发,并且往往耦合了各种中间件操作,如数据库、缓存、消息队列等。这种耦合使得单元测试变得困难,因为要在没有这些中间件的情况下运行测试可能需要大量的模拟或存根代码。

在深入探讨MVC架构之后,我们将进入今天的主题:DDD的分层架构模型。

2. DDD的架构模型

在DDD中,通常将应用程序分为四个层次,分别为用户接口层(Interface Layer)应用层(Application Layer)领域层(Domain Layer)基础设施层(Infrastructure Layer),每个层次承担着各自的职责和作用。分层模型如下图所示:

新项目,不妨采用这种架构分层,很优雅!

  1. 接口层(Interface Layer):负责处理与外部系统的交互,包括UI、Web API、RPC接口等。它会接收用户或外部系统的请求,然后调用应用层的服务来处理这些请求,最后将处理结果返回给用户或外部系统。
  2. 应用层(Application Layer):承担协调领域层和基础设施层的职责,实现具体的业务逻辑。它调用领域层的领域服务和基础设施层的基础服务,完成业务逻辑的实现。
  3. 领域层(Domain Layer):该层包含了业务领域的所有元素,如实体、值对象、领域服务、聚合、工厂和领域事件等。这一层的主要职责是实现业务领域的核心逻辑。
  4. 基础设施层(Infrastructure Layer):主要提供通用的技术能力,如数据持久化、缓存、消息传输等基础设施服务。它可被其他三层调用,提供各种必要的技术服务。

在这四层中,调用关系通常是单向依赖的,即上层依赖下层,下层并不依赖上层。例如,接口层依赖应用层,应用层依赖领域层,领域层依赖基础设施层。但值得注意的是,尽管基础设施层在物理结构上可能位于最底层,但在DDD的分层模型中,它位于最外层,为内部各层提供技术服务。

新项目,不妨采用这种架构分层,很优雅!

2.1 依赖反转原则

依赖反转原则(Dependency Inversion Principle, DIP)是一种有效的设计原则,有助于减小模块间的耦合度,提高系统的扩展性和可维护性。依赖反转原则的核心思想是:高层模块不应直接依赖低层模块,它们都应该依赖抽象。抽象不应该依赖具体的实现,而具体的实现应当依赖于抽象。

在 DDD 的四层架构中,领域层是核心,是业务的抽象化,不应直接依赖其他任何层。这意味着领域层的业务对象应该与其他层(如基础设施层)解耦,而不是直接依赖于具体的数据库访问技术、消息队列技术等。但在实际运行时,领域层的对象需要通过基础设施层来实现数据的持久化、消息的发送等。

为了解决这个问题,我们可以使用依赖翻转原则。在领域层,我们定义一些接口(如仓储接口),用于声明领域对象需要的服务,具体的实现则由基础设施层完成。在基础设施层,我们实现这些接口,并将实现类注入到领域层的对象中。这样,领域层的对象就可以通过这些接口与基础设施层进行交互,而不需要直接依赖于基础设施层。

2.2 DDD四层架构的优势

在复杂的业务场景下,采用DDD的四层架构模型可以有效地解决使用MVC架构可能出现的问题:

  1. 职责分离:在DDD的设计中,我们尝试将业务逻辑封装到领域对象(如实体、值对象和领域服务)中。这样可以降低应用层(原MVC中的Service层)的复杂性,同时使得业务逻辑更加集中和清晰,易于维护和扩展。
  2. 领域建模:DDD的核心理念在于通过建立富有内涵的领域模型来更真实地反映业务需求和业务规则,从而提高代码的灵活性,使其更容易适应业务的变化。
  3. 明确的边界划分:DDD通过边界上下文(Bounded Context)的概念,对系统进行明确的边界划分。每个边界上下文都有自己的领域模型和业务逻辑,使得大规模团队协作更加清晰、高效。
  4. 易于测试:由于业务逻辑封装在领域对象中,我们可以直接对这些领域对象进行单元测试。同时,基础设施层(如数据库、缓存和消息队列)被抽象为接口,我们可以使用模拟对象(Mock Object)进行测试,避免了直接与真实中间件的交互,大大提升了测试的灵活性和便利性。

接下来看看如何在代码中遵循DDD的分层架构。

3. 如何实现DDD分层架构

为了遵循DDD的分层架构,在代码实现时有两种实现方法。

第一种是在模块中通过包进行隔离,即在模块中建立4个不同的代码包,分别对应领域层(Domain Layer)、应用层(Application Layer)、基础设施层(Infrastructure Layer)和用户接口层(User Interface Layer)。这种方法的优点是结构简单,易于理解和维护。但缺点是各层之间的依赖关系可能不够明确,容易导致代码耦合。

新项目,不妨采用这种架构分层,很优雅!

第二种实现方法是建立4个不同的Maven Module层,每个Module分别对应领域层、应用层、基础设施层和用户接口层。这种方法的优点是各层之间的依赖关系更加明确,有利于降低耦合度和提高代码的可重用性。同时,这种方法也有助于团队成员更好地理解和遵循DDD的分层架构。然而,这种方法可能会导致项目结构变得复杂,增加了项目的维护成本。

新项目,不妨采用这种架构分层,很优雅!

在实际项目中,可以根据项目规模、团队成员的熟悉程度以及项目需求来选择合适的实现方法。对于较小规模的项目,可以采用第一种方法,通过包进行隔离。而对于较大规模的项目,建议采用第二种方法,使用Maven Module层进行隔离,以便更好地管理和维护代码。无论采用哪种方法,关键在于确保各层之间的职责分明,遵循DDD的原则和最佳实践。

在DailyMart项目中,我最初打算采用第一种方法,通过包进行隔离。然而,在微信群中进行投票后,发现近90%的人选择了第二种方法。作为一个倾听粉丝意见的博主,我决定采纳大家的建议。因此,DailyMart将采用Maven Module层隔离的方式进行编码实践。
新项目,不妨采用这种架构分层,很优雅!

4. DDD中的数据模型

在DDD中,我们采用特定的模型来映射和处理不同的领域概念和责任,常见的有三种数据模型:实体对象(Entity)、数据对象(Data Object,DO)和数据传输对象(Data Transfer Object,DTO)。这些模型在DDD中有着明确的角色和使用场景:

  • Entity(实体对象): 实体对象代表业务领域中的核心概念,其字段和方法应与业务语言保持一致,与持久化方式无关。这意味着实体和数据对象可能具有完全不同的字段命名、字段类型,甚至嵌套关系。实体的生命周期应仅存在于内存中,无需可序列化和可持久化。
  • Data Object (DO、数据对象): DO可能是我们在日常工作中最常见的数据模型。在DDD规范中,数据对象不能包含业务逻辑,并且位于基础设施层,仅负责与数据库进行交互,通常与数据库的物理表一一对应。
  • DTO(数据传输对象): 数据传输对象主要用作接口层和应用层之间传递数据,例如CQRS模式中的命令(Command)、查询(Query)、事件(Event)以及请求(Request)和响应(Response)。DTO的重要性在于它能够适配不同的业务场景需要的参数,从而避免业务对象变成庞大而复杂的"万能"对象。

在DDD中,这三种数据对象在很多场景下需要相互转换,例如:

  1. Entity <-> DTO:在应用层返回数据时,需要将实体对象转换成DTO,这一般通过一个名为DTO Assembler的转换器来完成。

  2. Entity <-> DO:在基础设施层的Repository实现时,我们需要将实体转换为DO以存储到数据库。同样地,查询数据时需要将DO转换回实体。这通常通过一个名为Data Converter的转换器来完成。

当然,不管是Entity转DTO,还是Entity转DO,都会有一定的开销,无论是代码量还是运行时的操作来看。手写转换代码容易出错,而使用反射技术虽然可以减少代码量,但可能会导致显著的性能损耗。这里给用Java的同学推荐MapStruct这个库,MapStruct在编译时生成代码,只需通过接口定义和注解配置就能生成相应的代码。由于生成的代码是直接赋值,所以性能损耗可以忽略不计。

新项目,不妨采用这种架构分层,很优雅!

在SpringBoot老鸟系列中我推荐大家使用 Orika 进行对象转换,理由是只需要编写少量代码。但是在DDD中不同对象都有严格的代码层级,并且一般会引入专门的Assembler和Converter转换器,既然代码量省不了,必然要选择性能最高的组件。

各种转换器的性能对比:Performance of Java Mapping Frameworks | Baeldung

5. 小结

本篇文章详细介绍了DDD的分层架构,并详细解释了如何在项目代码中实现这种分层架构。同时,还详细DDD中三种常用的数据对象:数据对象(DO)、实体(Entity)和数据传输对象(DTO)。这三种数据对象的区别可以通过下图进行精炼总结:

新项目,不妨采用这种架构分层,很优雅!

至此,我们已经深入解析了DDD中的核心概念。同时,我们的DailyMart商城系统已完成所有的前期准备,现在已经准备好进入实际的编码阶段。在接下来的章节中,我们将从实现注册流程开始,逐步探索如何在实际项目中应用DDD。

最后,欢迎关注公众号 Java日知录 ,获取最新的文章和源码更新。文章来源地址https://www.toymoban.com/news/detail-475634.html

到了这里,关于新项目,不妨采用这种架构分层,很优雅!的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • idea新项目上传git

    关于idea新项目怎么上传到git,今天整理一下操作的步骤。来做一个记录! 1.首先要在项目上右击打开终端或者在idea的下方有一个快捷按钮 按照上面的截图进行操作,打开终端窗口 2.然后在终端窗口中输入 git init 命令,会出现如图样子 3.然后右击项目会出现git的选项,然后按

    2024年02月12日
    浏览(38)
  • 本地新项目推送至gitlab仓库

    1. gitlab上新建一个空白项目 gitlab上点击new project按钮,新建一个项目   新建空白项目  项目名称与本地新建项目名称相同,其余根据具体需要选择 2. 初始化本地仓库并commit项目   进入本地项目根目录下,右击 git bash here打开命令窗口  初始化本地仓库: git init  提交至暂存

    2024年02月13日
    浏览(35)
  • gitlab合并新项目和分支切换

    1、创建空白项目   2、先创建一个群组    3、编写群组信息  4、创建群组完成以后新建项目 1、初始化 2、关联gitlab地址 3、查看是否关联成功 4、添加文件 5、提交到本地仓库 6、进行推送(注意推送分支) 7、查看结果 记得选择对应的分支 1、修改默认分支, 将默认分支修改成

    2024年02月12日
    浏览(42)
  • 搭建新项目 前端环境 及启动项目前的相关配置

    ** ** 提示:这里可以添加本文要记录的大概内容: 搭建新项目 前端环境 下图所示为开发时前端所用的编辑器 提示:以下是本篇文章正文内容,下面案例可供参考 注意:在配置时 有时候 localhost 可能 不太好用,所以我们 最好配置 成 127.0.0.1 指向我们的电脑 代码如下(示例

    2024年01月23日
    浏览(44)
  • 新项目如何选择vue和react

    vue 和 react 的区别 Vue 和 React 都是流行的前端框架,它们有以下不同点: 语法和模板:Vue 使用类似于 HTML 的模板语法,而 React 使用 JSX 语法,在 JavaScript 中嵌入 HTML 标记。 组件化:Vue 和 React 都采用了组件化的思想,但是 Vue 的组件化更加彻底,一个 Vue 组件包括了 HTML 模板

    2024年02月02日
    浏览(46)
  • flowable流程移植新项目前端问题汇总

    flowable流程移植到新项目时,出现一些前端问题,汇总如下: PS F:khxmNBCIO_VUE yarn run serve yarn run v1.21.1 $ vue-cli-service serve  INFO  Starting development server...  ERROR  Error:  Vue packages version mismatch: - vue@2.6.11 (F:khxmNBCIO_VUEnode_modulesvuedistvue.runtime.common.js) - vue-template-compiler@2.7.14 (F:

    2024年02月12日
    浏览(28)
  • Go新项目-Go安全指南(8)

    目录 1 通用类 I. 代码实现 1.1 内存管理 1.2 文件操作 1.3 系统接口 1.4 通信安全 1.5 敏感数据保护 1.6 加密解密 1.7 正则表达式 2 后台类 I. 代码实现 1.1 输入校验 1.2 SQL操作 1.3 网络请求 1.4 服务器端渲染 1.5 Web跨域 1.6 响应输出 1.7 会话管理 1.8 访问控制 1.9 并发保护 1.1 内存管理

    2024年01月19日
    浏览(41)
  • OpenAI又火一个新项目,已开源...

    大家好,我是 Jack。 OpenAI 又有新动作了,开源发布 Shap-E。 今天,我继续手把手教学。 算法原理、环境搭建、效果测试,一条龙服务,尽在下文! Shap-E 算法的功能,简单来讲就是根据一段文字描述,生成对应的 3D 模型,一起看几组效果。 输入文字: A chair that looks like an

    2024年02月03日
    浏览(41)
  • 本地新项目上传到git的详细步骤

    提示:你本地的项目目录里要记得添加.gitignore忽略文件,免得把一些无用的文件提交 ,内容如下,可直接粘贴: 在远程gitlab上创建新项目springcloud-001,默认分支为main。 如:git remote add origin git@gitlab.com:springcloud5521407/springcloud-001.git 如果提示 fatal: remote origin already exists(远程

    2023年04月08日
    浏览(43)
  • Go新项目-为何选Gin框架?(0)

    先说结论:我们选型Gin框架 早在大概在2019年下旬,由于内部一个多线程上传的需求,考虑到Go协程的优势; 内部采用Gin框架编写了内部的数据上传平台BAP,采用Gin+Vue开发,但前期没考虑到工程化思维,导致代码后期维护程度变得很复杂,硬编码内容过多,重复内容过多;

    2024年01月17日
    浏览(47)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包