toB 应用设计系列 - 构架分层篇

本文探讨toB应用的设计,重点介绍导航结构与分层设计原则,包括controller、service、domainService、dao等各层职责及交互,强调最小知识原则与解耦思想,适合架构师和高级开发者参考。

toB 应用设计系列 - 导航

小结

层级结构


顶层
-web
-domainService
-query
-infrastructure
-framework

web层
-pc
--login
---LoginController
---LoginService
---dto
----LoginDTO
--module
---dictType
----DictTypeController
----DictTypeService
----dto
-----AddDictTypeDTO
-----UpdDictTypeDTO
-----converter
------AddDictTypeConverter
------UpdDictTypeConverter
----vo
--component
--dictType
---DictTypeController
---DictTypeService
---dto
----GetDictTypeDTO
----ListDictTypeDTO
---vo
----GetDictTypeVO
----ListDictTypeVO
-mobile
--(和 pc 一致)
-api

domainService 层
-dictType
--IDictTypeDMService
--cmd
---AddDictTypeCMD
---UpdDictTypeCMD
--impl
---DictTypeDMService
---DictTypePO
---DictTypeDao
---DictPO
---DictDao
---AddDictTypeCMDConverter
---UpdDictTypeCMDConverter

framework 层
-exception
-log
-shiro

infrastructure 层
-file
--excel
---ExcleHelper
---ExcelUtil
-thirdApi
--element
--alibaba
--meituan

各层调用关系

层级下层同级调用说明
controllerservice禁止只调用一个service方法
servicedomainService / query禁止可调用多个 domainService / query
domainServicedao允许可调用多个dao
dao允许

dto 爆炸的问题

构架分层设计原则

  • 最小知识原则
  • 解耦的思想
  • 业务操作和查询分开

没有优先考虑效率, 业务为主导

参考 领域驱动设计 / CQRS

业务操作和查询分开

业务操作和查询是两条支线, 这两种模式完全不一样
业务操作可以非常复杂, 会更新数据, 但基本不需要返回(新增一般返回主键), 具有高度的通用性

查询则不更新数据, 但是企业应用的查询条件千奇百怪, 而且返回结果各种跨表, 基本没有通用性

业务操作分层

  • controller
  • service
  • domainService
  • dao

controller

接收 http 请求, 返回数据

service

业务层, 通过组装 domainService 领域服务层 以及调用基础设施层完成业务操作

service 层不涉及具体的业务操作, 具体的业务操作由 domainService 层处理

service 专一性很强, 不具备通用性

domain

聚合根的业务操作

domainService 层是最重的一层, 涉及到的核心业务逻辑都在这里, domainService 层不做具体的数据库操作, 具体数据库操作的交由 dao 层完成; domainService 层严格遵循最小知识原则, 业务过程对 service 层透明; 同样的, dao 层要严格遵循最小知识原则, 具体的数据库操作对 domainService 层透明

一个 domainService 对应一个聚合根, 而非对应一张表

eg: 数据字典表和数据字典类型表, 聚合根是数据字典类型表, 故 domainService 是
数据字典类型 domainService, 而且没有数据字典 domainService, 若外部想要操作数据字典, 只能通过 数据字典类型 domainService 来操作

domainService 通用性很强

dao

业务数据库原子操作

``

和数据库的原子操作 CRUD 区别在于, 业务需要的原子操作不一定是 CRUD, 对应的是业务的原子操作, 如新增订单, 修改订单, 审核订单, 删除订单等

eg: 日志dao 只有 insert 操作, 没有 update / delete 操作

数据库的修改订单的原子操作, 不一定会对应业务上的修改订单的操作, 而是可能会拆成若干个方法:

  1. update() : 修改普通属性, 对应的数据库操作为 修改 订单名称,订单明细的订单数量, 订单单价, 收货数量, 收货单价等普通的属性
  2. next() : 修改订单的流程状态, 进入下一个流程状态, 对应的数据库操作为 修改订单的流程状态

dao 通用性很强

// 若订单状态为创建状态, 则可修改 订单名称, 订单明细的订单数量, 订单单价

查询分层

  • controller
  • service
  • query

controller

和操作的一致

service

service 层不涉及具体的数据库操作, 具体的数据库操作由 query 层实现, service 层主要通过调用 query 层, 以及调用基础设施层, 以及对最后的结果进行组装, 返回接口需要的格式

事务放在service 层

query

查询的sql 语句

query 为定制化的查询语句, 不具备通用性

基础设施层 infrastructure

完成基础的操作(不含数据库操作以及业务操作)

  • 工具类 (发邮件 / 发短信 / 解析邮件 / 解析短信 / 加密 / …)

各层间交互

controller <-> service

controller 层接收 http 请求, 一般由框架转换成 dto 传输对象, 直接将 dto 作为 service 层的参数调用service, service 执行完以后, 返回view objert对象(VO), 由 controller 自行封装成统一的响应格式给 api

禁止在 controller 层写业务相关的代码

禁止在 controller 校验参数合法性, 传输参数的合法性应由 service 层校验

一个 controller 只调用一个 service 方法

service <-> domainService

service 接收 dto 传输对象, 对 dto 对象进行转换, 转换成 domainService 层接收的 cmd 命令对象, 调用 domainService, 成功则完成操作(可能需要返回主键), 失败则抛异常

注意:

一个service 可调用多个 domainService 方法

service 不允许同级调用: service方法 不调用 自己的 service 其他方法, 也不调用其他 service的方法, 构架设计来说, 是希望 service 专一化, 不具备通用性(通用性都放领域层了, service 提供专用的接口给 api 就可以了)

service <-> query

service 接收 dto 传输条件对象, 直接将部分或全部 dto 属性(或者对dto 进行加工)作为参数传给 query, query 返回查询结果, service 对所有的查询结果进行组装, 组装成 api 需要的格式, 返回

注意: 一个 service 可调用多个 query 方法

domainService <-> dao

domainService 接收 cmd 命令对象, 将 cmd 命令对象加工成 po对象, 调用 dao, 完成业务操作, 无返回或按需要返回主键

注意:

domainService 可调用多个 dao

domainService 可同级调用: 既可调用同一个domainService 类的方法, 也可以调用不同domainService 类的方法

dao

dao 接收 po 对象, 对 po 对象进行数据库的具体操作, 无返回或按需要返回主键

query

query 接收 dto 传输对象, 进行查询操作, 返回查询结果

基类

为方便程序识别各种 model, 为 dto / cond / vo / po 提供抽象基类 BaseDTO / BaseCond / BaseVO / BasePO

问题

DTO

DTO 爆炸

VO也有这个问题

接口通过 DTO 传输, DTO 没有通用性, 往往一个 service 方法就需要一个 DTO, 一个 VO, 造成 DTO 数量剧增的现象, 不但增加了很多的工作量, 包括定义, 转换成 CMD, 而且命名很麻烦, 又不好管理

DTO 转换成 CMD

每一个DTO 都需要转换成 CMD, 这一工作也非常枯燥无聊

若 DTO 嵌套的层次很深, 这个工作会变得更加的麻烦

DTO 嵌套

VO 也有这个问题

若 DTO 嵌套的层次很深, 定义会很麻烦, 转换成 CMD 也很复杂

造成 DTO 的种种问题的原因, 和 JAVA 的强数据类型特性分不开, 若能避开 强数据类型, 基本能解决

故一种解决方案是 将 查询的一层剥离出去给 node.js 来承接, 业务操作依然交给 JAVA 来做, node 这一层把 controller 完全承接过来, 把查询的service 也承接过来, 操作的service 依然交给 JAVA

<think>好的,我现在需要帮助用户了解鸿蒙系统项目从创建到实现三层架构的完整过程。首先,我得回忆一下HarmonyOS的基本概念和开发流程。鸿蒙系统是华为推出的分布式操作系统,支持多种设备类型。三层架构通常指的是表示层、业务逻辑层和数据访问层,这在软件开发中很常见,但在鸿蒙中可能需要结合其特有的Ability和FA/PA模型来实现。 用户的问题涉及项目创建到三层架构的实现步骤,所以我需要分步骤详细说明。首先,项目创建部分,我应该介绍如何使用DevEco Studio创建新项目,选择正确的模板,比如“Empty Ability”,这对应的是鸿蒙的Ability结构。然后配置项目的基本信息,比如项目名称、包名、保存路径和设备类型,这里可能需要说明不同设备类型的选择会影响后续的配置。 接下来是三层架构的具体实现。鸿蒙的UI部分对应表示层,通常使用XML布局和Java/JS来编写界面。需要举例说明如何创建一个简单的页面,比如登录界面,这可能涉及布局文件和交互逻辑。业务逻辑层需要处理数据验证、业务规则等,这里可能需要创建一个独立的Java类来处理,比如UserManager,这样可以将业务逻辑与界面分离,符合分层架构的思想。 数据访问层通常涉及数据库操作或网络请求。在鸿蒙中,可以使用轻量级数据库或其他存储方案。需要指导用户如何创建数据库帮助类,比如UserDataHelper,并实现数据的增删改查功能。同时,需要强调各层之间的调用关系,比如表示层调用业务逻辑层,业务逻辑层再调用数据访问层,确保层与层之间的解耦。 在整合三层架构时,需要展示如何将各层连接起来,例如在UI中调用UserManager的方法,而UserManager又调用UserDataHelper进行数据操作。同时,需要注意鸿蒙的Ability生命周期,确保数据加载和保存的时机正确。 另外,用户可能对鸿蒙的分布式能力感兴趣,但问题主要集中在三层架构,所以可能需要简要提及,但重点还是放在传统三层结构的实现上。需要检查是否有相关的引用资料可用,但根据提供的引用内容,似乎没有直接相关的信息,所以可能不需要添加引用标识。不过,需要确保回答结构清晰,步骤明确,代码示例正确,并且符合LaTeX格式的要求。 最后,生成相关问题时要围绕鸿蒙系统开发、架构设计、数据管理和分布式能力等方面,确保问题有针对性,能够帮助用户进一步深入学习。需要检查是否存在语法错误,尤其是LaTeX格式是否正确,比如行内公式使用$...$,独立公式用$$...$$,并正确分段。同时,确保使用中文回答,语言自然流畅,避免技术术语使用不当。</think>### 鸿蒙系统项目创建与三层架构实现步骤指南 #### 1. 项目创建与基础配置 1. **环境准备** 安装DevEco Studio(鸿蒙开发IDE)并配置SDK,确保选择支持目标设备(如手机、智能手表)的API版本。 2. **新建项目** - 选择模板:使用“**Empty Ability**”模板(对应UI交互能力) - 配置参数: ```plaintext 项目名称: MyHarmonyApp 设备类型: Phone 语言: Java/ArkTS(根据需求选择) ``` - 完成创建后,项目结构包含`entry > src > main`下的资源与代码目录。 #### 2. 三层架构实现 **a. 表示层(UI层)** 鸿蒙通过`Ability`和`Page`实现界面交互。 **示例代码(XML布局 + Java逻辑)** ```xml <!-- resources/base/layout/login_page.xml --> <DirectionalLayout xmlns:ohos="http://schemas.huawei.com/res/ohos" ohos:width="match_parent" ohos:height="match_parent"> <TextField ohos:id="$+id:username_input" ohos:hint="请输入用户名"/> <Button ohos:id="$+id:login_btn" ohos:text="登录"/> </DirectionalLayout> ``` ```java // entry/src/main/java/com.example.ui.LoginAbilitySlice.java public class LoginAbilitySlice extends AbilitySlice { @Override protected void onStart(Intent intent) { super.onStart(intent); super.setUIContent(ResourceTable.Layout_login_page); // 绑定按钮点击事件 findComponentById(ResourceTable.Id_login_btn).setClickedListener( component -> { String username = getTextInput(ResourceTable.Id_username_input); UserManager.validateUser(username); // 调用业务逻辑层 } ); } } ``` **b. 业务逻辑层(Service层)** 创建独立的Java类处理核心逻辑,如用户验证、数据加工。 ```java // 路径: entry/src/main/java/com.example.service.UserManager.java public class UserManager { public static boolean validateUser(String username) { if (TextUtils.isEmpty(username)) return false; return UserDataHelper.checkUserExists(username); // 调用数据层 } } ``` **c. 数据访问层(Data层)** 使用鸿蒙提供的轻量级数据库`LitePal`或`RDB`进行本地存储。 ```java // 路径: entry/src/main/java/com.example.data.UserDataHelper.java public class UserDataHelper { private static final OrmContext context = ...; // 初始化数据库连接 public static boolean checkUserExists(String username) { User user = context.where(User.class).equalTo("name", username).findOne(); return user != null; } } ``` #### 3. 层间调用关系 $$表示层 \xrightarrow{调用} 业务逻辑层 \xrightarrow{调用} 数据访问层$$ 通过依赖注入或接口隔离实现解耦,例如使用`@Inject`注解或接口定义数据操作。 #### 4. 分布式能力扩展(可选) 鸿蒙支持跨设备调用`Ability`,可通过`Distributed Scheduler`实现业务逻辑层和数据层的分布式协同。 ```java // 调用远程设备的Ability Intent remoteIntent = new Intent(); Operation operation = new Intent.OperationBuilder() .withDeviceId("目标设备ID") .withBundleName("com.example.remote") .withAbilityName("RemoteDataAbility") .build(); remoteIntent.setOperation(operation); startAbility(remoteIntent); ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值