当前位置: 首页 > news >正文

从Void到Task<PublishAggregateResult>:一次服务方法返回类型重构的纠结与决策

今天原本想美美地完成UI层与Core层通过消息总线实现博客发布的功能。一切都很顺利,直到我重构到 PublishBlog 方法,准备为ApplicationService 写事件处理逻辑时,不然发现不对劲——“不兑!等等,我该怎么把发布结果信息传回给ApplicationService 呢?” 就是这么一个看似简单的问题,让我一脸懵逼。。。

一、 项目背景与问题起源

先简单介绍下我的项目:这是一个.NET Winform博客一键发布工具。架构上做了清晰的分层(自己突然想出来写着玩的):

  • UI层:负责界面交互,它不关心核心逻辑,只通过消息总线向Core层发送指令(比如“发布博客”)。

  • Core层:负责所有核心业务。其中有一个关键的 ApplicationService,它就像是Core层的“总指挥部”,所有消息的接收和发布都由它一手包办。当它收到UI层的指令后,会去调用像 BlogPublishService 这样的具体“业务部门”来干活。

这种设计的好处是解耦:UI层和Core层互相不认识,只通过消息沟通,非常清爽。在后续开发我甚至打算使用更加精美与符合现代审美WPF项目来作为UI层,所以如此设计打有好处。(得意叉腰)

问题就出在这里:我的 BlogPublishService.PublishBlog 方法,以前干完活是自己直接“大喊一声”(发布事件)通知UI层。但现在规矩变了,所有“喊话”的权力都收归 ApplicationService 了。这下好了,PublishBlog 方法变成了一个“哑巴”,它活干得怎么样,ApplicationService 完全不知道,自然也就没法向上汇报了。

所以,核心问题变成了:如何让“哑巴”开口,把它工作的结果告诉“总指挥部”?

二、 思路博弈:返回值 vs 输出参数

首先得确定沟通方式。脑子里蹦出两个选项:

  1. 返回值 (Return Value):最直接,像函数式编程,输入 -> 处理 -> 输出,符合直觉。

  2. 输出参数 (out Parameters):感觉更“高级”一点,但用在这里有点别扭。

我琢磨了一下,PublishBlog 这个方法非常纯粹:给它配置和内容,它就去发布,最后给你一个结果。这简直就是为返回值量身定做的场景。用 out 参数的话,代码会显得很拧巴,所以果断选择了返回值方案。


// 返回值方式:清晰,像在问一个问题并得到一个答案var result = await blogPublishService.PublishBlog(configs, blogInfo);// 输出参数方式:有点绕,像在操作一个机器blogPublishService.PublishBlog(configs, blogInfo, out var result);

三、 命名的纠结:与AI的“头脑风暴”

确定了用返回值,下一个头疼的事来了:返回什么类型?方法内部主要有两种信息要传递:一个是所有平台的发布详情 (List<PublishResult>),另一个是可能出现的业务错误 (List<string>)。

直接返回元组 (List<PublishResult>, List<string>) 太丑了,过于冗长,不够优雅,Item1Item2 过几天自己都看不懂。返回事件类型又会造成层与层之间的耦合。所以,定义一个新类型是唯一优雅的选择

但取什么名字呢?我犯了难,于是求助了AI。AI给了我几个方案:

  • PublishOperationResult (发布操作结果)

  • PublishSummaryResult (发布摘要结果)

  • PublishAggregateResult (发布聚合结果)

我盯着这几个名字看了好久。OperationResult 太泛了,什么操作都能用;SummaryResult 强调摘要,感觉有点轻量。最后我选择了 PublishAggregateResult

为什么? 因为这个词精准地描述了这个类的本质:它不是一个简单的结果,而是由多个PublishResult聚合而成的。Aggregate(聚合)这个词在DDD(领域驱动设计)里很常见,它暗示了这是一个整体,能很好地表达“总和”的概念。就这么定了!


// 最终,方法签名变得非常清晰public async Task<PublishAggregateResult> PublishBlog(...){// ... 内部逻辑return new PublishAggregateResult {PublishResults = res,FailReasons = failedReasons};}

四、 总结与心得

这次重构让我深刻体会到,软件设计就是在不断地做权衡。从最初的架构选择(消息总线),到具体的技术方案(返回值),再到最后的命名细节,每一步都是一个决策点。

  1. 分层与解耦是基础:清晰的分层让问题暴露得更早,解决起来方向也更明确。

  2. 返回值是直觉之选:对于纯计算或纯业务逻辑,返回值通常是最直接、最清晰的通信方式。

  3. 命名是设计的延伸:一个好名字能准确传达类的职责,PublishAggregateResult 无疑比 Item1Item2 要好懂得多。

http://www.hskmm.com/?act=detail&tid=15741

相关文章:

  • LVGL移植到STM32F4出现无法运行的问题
  • 题目记录(Before NOIP2025 ver)
  • 专业修复sqlserver master 数据库损坏。
  • jenkins job的configure中配置git时 选择的credential为什么不能选择secret认证方式的数据
  • Day21继承
  • C# Avalonia 15- Animation- ImageWipe
  • 题解:P8067 [BalkanOI 2012] balls
  • 题解:P8300 [COCI 2012/2013 #2] INSPEKTOR
  • SuperHarness-3D低压柜机电协同设计方案!
  • 详细介绍:.NET驾驭Word之力:打造专业文档 - 页面设置与打印控制完全指南
  • 使用.NET标准库实现多任务并行处理的详细过程 - 实践
  • 模型训练中 平均损失值和平均准确率的深入理解
  • torch.max函数在分类问题中的使用 学习
  • godot3.6字典遍历
  • 国产DevOps工具链崛起:Gitee领衔的本土化技术生态全景解读
  • 安装 elasticsearch-9.1.4的 IK分词器
  • react性能优化
  • 从研发效能到知识中枢:Gitee Wiki如何重塑企业知识管理范式
  • Gitee DevSecOps平台:军工软件研发的智能化革命
  • 杆状病毒表达系统为何成为蛋白表达首选
  • 日记3
  • Gitee如何重塑中国开发者的代码托管体验
  • 模块化面向对象 2章
  • css `isolation: isolate` - 详解
  • Debezium + Kafka + Flink/Doris Stream Load 实时数仓
  • Gitee DevOps平台:中国企业数字化转型的代码管理新范式
  • Ansible + Docker 部署 Zookeeper 集群
  • 幂运算与航班中转的奇妙旅行:探索算法世界的两极 - 实践
  • Gemini CLI 配置问题
  • 本土化与全球化博弈下的项目管理工具选型:Gitee如何为中国企业破局?