DDD领域事件的最佳实践
DDD 实战:关于领域事件(Domain Events)的设计抉择与工程落地思考在领域驱动设计(DDD)的战术落地过程中,领域事件(Domain Event) 是连接各个子域、聚合以及限界上下文的“胶水”。它不仅解耦了复杂的业务逻辑,更是实现“最终一致性”架构的关键手段。 本文基于实际开发经验,总结领域事件的建模、生成、发布及可靠性投递的最佳实践,并探讨不同方案背后的权衡。 一、 什么是领域事件?简单来说,领域事件是聚合内已发生的业务事实。 业务事实:意味着它是过去式。比如“用户已注册”、“订单已支付”。 命名规范:推荐使用 动词过去式(如 OrderPaid, AccountActivated)。 价值: 解耦:核心业务逻辑不需要知道谁在关注它。 副作用处理:触发通知、大数据分析、报表生成等非核心逻辑。 数据一致性:跨聚合、跨服务的状态同步。 二、 建模:胖消息 vs 瘦消息领域事件通常被建模为不可变的值对象(Value Object)。但在设计消息体(Payload)时,我们面临一个经典抉择: 1. 瘦消息(Id-Only)消息体仅包含最基础的元数据:123456...
算法总结(三) 链表
链表相关的题目这里用来记录链表相关的题目以及思路 相交链表输入两个链表,找出它们的第一个公共结点。12345678910111213141516public class Solution { public ListNode getIntersectionNode(ListNode headA, ListNode headB) { /** 定义两个指针, 第一轮让两个到达末尾的节点指向另一个链表的头部, 最后如果相遇则为交点(在第一轮移动中恰好抹除了长度差) 两个指针等于移动了相同的距离, 有交点就返回, 无交点就是各走了两条指针的长度 **/ if(headA == null || headB == null) return null; ListNode pA = headA, pB = headB; // 在这里第一轮体现在pA和pB第一次到达尾部会移向另一链表的表头, 而第二轮体现在如果pA或pB相交就返回交点, 不相交最后就是null==null ...
分布式相关的总结
分布式事务TC 如何知道哪些RM属于同一个全局事务?以Seate框架为例,在TM向TC发送全局事务开始时,TC会生成对应的全局事务ID(XID),并发送给TM。TM 将 XID 放入当前线程的 ThreadLocal 中,后续的 RM (资源管理器) 分支事务通过解析该 XID 来注册分支。如果涉及RPC调用,每次调用时会在请求头部传递这个XID,让RM知道这个XID,RM向TC发送分支事务开始时,就会携带这个XID,从而告诉TC这个RM属于哪一个全局事务。 在多线程中,由于每个线程的XID是线程私有的,所以无法确定哪些RM属于同一个全局事务。这时全局事务会失效。解决方案可以是在开启新线程时,将XID传递给新线程,新线程的XID会继承父线程的XID。这样子线程的XID就会和父线程的XID一致,从而可以确定哪些RM属于同一个全局事务。 流程图片
DDD与MVC的统一
MVC到DDD:从架构分层到业务建模的思考在后端开发学习和实践过程中,我们总会被各种架构模式和设计思想围绕——MVC、Service+DAO分层、DDD四层架构(DDD并非与四层架构绑定 DDD是思想,四层架构是代码落地的一个架构)……这些概念之间到底是替代关系还是互补关系?为什么简单业务用Service+DAO就够,复杂业务却要引入DDD?结合近期的学习思考,我想梳理清楚这些架构之间的核心关联,帮自己(也希望帮到同样困惑的同学)建立清晰的认知。 一、起点:MVC的核心价值——解决“代码放哪”的基础问题最早接触后端开发时,MVC是绕不开的入门架构。它的核心思想非常简单:将软件分为三个核心层级,实现“展示与逻辑”的初步解耦。 Controller(控制器):接收用户请求、协调后续处理、返回响应,相当于系统的“入口/出口”,不承载核心业务逻辑; Model(模型):封装数据和核心业务逻辑,是系统的“大脑”,负责数据的增删改查和业务规则校验; View(视图):展示数据,与用户直接交互(前后端分离场景下,前端页面即View层,后端不再关注)。 MVC的价值在于给出了“代码该放...
计算机网络问题总结
学习过程中的疑惑通过ARP协议我们可以知道目的ip的mac地址 那要是这个ip不在我的局域网里面 那我应该得到谁的mac地址呢这是一个非常经典且关键的网络问题。 简单直接的回答是:你会得到你所在局域网的“网关”(Gateway)的 MAC 地址。 详细原理解析当你的电脑(主机 A)想要发送数据包给一个 IP 地址(主机 B)时,它会经历以下逻辑判断流程: 判断目标 IP 是否在同一个局域网(子网)内: 电脑会查看自己的子网掩码(Subnet Mask)。 如果 目标IP 和 本机IP 在同一个网段内,电脑认为它可以直接通信。 如果不在同一个网段内,电脑认为“我够不着它”,必须找一个“中介”来帮忙转发。 寻找“中介”(网关): 这个“中介”就是你配置的默认网关(Default Gateway)。通常这是你路由器(Router)的内网 IP 地址(例如 192.168.1.1 或 192.168.0.1)。 电脑会把数据包发给网关,由网关负责把数据包投递到外网。 ARP 解析的对象发生变化: 既然数据包要发给网关,那么在数据链路层(二层),目标 MAC 地址就必须是...
算法总结(二)
算法总结(二)异或运算的骚操作异或解释 异或运算,相同的值异或为0,不同的值异或为1 也可以理解为无进位的加法 异或运算满足交换律,结合律 一个数组中所有数的异或和 跟 某些数异或的结果 相当于 减去某些数的异或和 使用异或进行交换两个数 a = a^b b = a^b (此时相当于a^b^b = a , 就让b等于a) a = a^b (此时相当于a^b^a = b , 就让a等于b) 局限性:注意这个交换不能对同一个地址的两个变量进行操作。因为第一步就会把他们都变为0 1234567 int a = -2323;int b = 10;a = a ^ b;b = a ^ b;a = a ^ b;System.out.println(a);System.out.println(b); 使用异或不用判断语句去判断两个数的大小大体思路就是根据a-b的符号,来判断a和b的大小。但是这样有溢出的风险(a-b会溢出)优化思路: 获取a和b的符号 获取c=a-b的符号 综合判断 如果a,b符号相同,则不可能溢出,直接根据c的符号来判断大小 如果a,b符号不同,则a-b可能会溢...
算法总结(一)
算法总结(一)归并分治使用情况 左边结果+右边结果+跨越两边的结果是否等于最终结果 对于两边排序之后对于统计跨越两边的结果是否有帮助 注意与总结 主要思想跟归并排序一样 只不过是在merge的时候进行一些额外统计操作(统计跨越两边的结果) 如果只是单纯的比大小(比如 小和,逆序对)可以直接在merge的时候进行比较12345678910//小和 直接在合并的时候进行比较统计 while(a <=m && b<=r){ if(nums[a]<nums[b]){ help[i++] = nums[a]; sum+= (nums[a]*(r-b+1)); a++; }else { help[i++] = nums[b++]; } } 如果不只是比大小 而是比较乘以二倍的结果,那么就...
feign的三种异常处理关系
Feign异常处理三重奏:ErrorDecoder、Fallback与全局异常捕获器的优先级探秘在微服务架构中,Feign作为声明式HTTP客户端,是服务间调用的核心组件。而异常处理是保障微服务稳定性的关键环节——当Feign调用下游服务抛出异常时,我们常通过 ErrorDecoder、容错组件的 Fallback(如Sentinel/Hystrix)、Spring全局异常捕获器(@RestControllerAdvice)三种方式处理异常。但三者并存时,执行顺序如何?优先级背后的原理是什么?特殊场景下又会出现哪些变化?本文将结合实践拆解这一核心问题。 一、核心结论:三者的优先执行顺序当Feign调用发生异常(非2xx HTTP状态码、超时、网络异常等)时,三者的执行优先级从高到低依次为: ErrorDecoder(Feign原生) → Fallback(容错组件) → 全局异常捕获器(Spring MVC) 完整执行链路可概括为: Feign调用触发异常 → ResponseInterceptor(OpenFeign 12.0+ 可选预处理) → ErrorDecoder(异...
微服务之间的全局异常捕获
在单体架构中,我们通过全局异常捕获器(如@ControllerAdvice+@ExceptionHandler)就能优雅地处理各类异常,自定义响应状态码和错误信息。但迁移到Spring Cloud微服务架构后,却遇到了一个棘手的问题:无论发生何种异常,接口返回的状态码始终是500,错误信息固定为INTERNAL_SERVER_ERROR,全局异常捕获器形同虚设,尤其在微服务间通过OpenFeign调用API时,该问题更为突出。本文将围绕这一问题,从原因分析、解决方案到特殊场景适配,逐步拆解实战过程中的思考与踩坑。 一、问题现象:单体架构异常捕获在微服务中失效在单体应用中,我们通常会编写如下全局异常捕获器,针对不同业务异常返回自定义状态码和信息: 123456789101112131415@ControllerAdvicepublic class GlobalExceptionHandler { @ExceptionHandler(BusinessException.class) public ResponseEntity<ErrorResponse&g...
MyBatis-Plus的分页查询原理
MyBatis-Plus分页查询底层原理一、分页查询的核心痛点与 MP 的解决方案在传统 MyBatis 开发中,分页实现往往需要手动拼接LIMIT语句(MySQL)或ROWNUM(Oracle),不仅繁琐且易出错,还存在两个核心问题: SQL 侵入性强:业务 SQL 与分页语法耦合,切换数据库时需批量修改; 总条数统计冗余:需手动编写 count 查询,且需处理复杂查询(如多表关联、分组统计)的 count 适配。 MyBatis-Plus(以下简称 MP)的分页插件通过拦截器机制+数据库方言适配,实现了 “无侵入式分页”,其核心设计思路是:在 SQL 执行前动态改写 SQL,自动添加分页语法和 count 查询,同时封装分页结果。 二、MP 分页的核心组件与依赖关系MP 分页功能的实现依赖 3 个核心组件,它们的协作流程决定了分页的底层逻辑: 组件名称 作用说明 PaginationInnerInterceptor 核心拦截器,负责拦截 SQL 执行、改写 SQL、统计总条数(MP 3.4.0 + 推荐使用) Page 分页参数载体,封装页码、每页...


