月度记录-2025-8月

43 阅读9分钟

1. centos swapoff -a命令

swapoff -a 是一个 禁用所有 swap 分区/文件 的命令。

swap 是 Linux 的虚拟内存机制:当物理内存不够用时,系统会把一部分内存数据写到磁盘的 swap 分区(或 swap 文件)里,相当于“硬盘模拟内存”。 和Windows 的“虚拟内存(Pagefile)”原理上是类似。但是:

  • swap 会 降低性能(因为磁盘比内存慢得多)。
  • 在容器化环境、K8s、数据库等场景中,swap 经常被禁用,以防止不可控的性能抖动。

2. ddd模式开发中,为什么基础设施层的 DO 不建议用 @Data?

在 DDD(领域驱动设计)模式中,基础设施层的数据库对象(DO),主要职责是数据的存取与转换,不承载任何业务逻辑,优先使用 @Getter @Setter,而不是@Data。

@Data 的含义:Lombok 的 @Data 注解 =@Getter + @Setter + @RequiredArgsConstructor + @ToString + @EqualsAndHashCode

DDD场景下语义有点重,能减轻一点就减轻一点。

3. idea生成的serialVersionUID 是怎么生成的,是随机数吗

在 Java 中,serialVersionUID 是用于 Java 对象序列化版本控制 的一个唯一标识符。它的默认值是由 JDK 的 serialver 工具 或 JVM 在运行时根据类的结构(字段、方法、修饰符等)计算出来的。IDEA 的生成方式与 serialver 工具一致,遵循以下逻辑:将类的结构信息做签名哈希,生成一个固定的 long 值。

建议直接用 1L, 而不是 IDEA 生成的那串 hash。

  1. 手动维护序列化版本更清晰

  2. 避免因自动生成导致版本飘变

  3. 大多数企业级项目/框架都这么做

4. self-explaining-自解释性

类名、方法名、字段名,都要语义明确。

好代码应该像一本读得通顺的小说,而不是一本需要翻字典的谜语集。

self-explaining 是代码质量的第一层,也是团队协作效率的保障。

5. DDD项目中 使用 Optional 的位置

层级

是否推荐使用 Optional

说明

Repository 层

推荐返回 Optional

明确表达:查不到对象可能为 null,Optional 是语义清晰的替代方案。

Domain 层

尽量避免传递 Optional

领域模型不关心 Optional,应处理为 Null Object、异常或业务决策。

ApplicationService 层之间调用

不推荐返回 Optional

应明确是存在还是不存在,内部做好处理,不要把“不确定性”传递出去。

Controller 层

不建议返回 Optional

接口层应返回标准响应体结构,如 ApiResult,对“无结果”有统一处理。

6. @Nonnull加在接口上还是实现方法上?

都加。那为什么不只加在实现类?

  • 接口是对外的能力声明(即契约),调用方只看到接口,不知道实现,要让调用方看到 。
  • @Nonnull加在接口上,IDE/静态分析工具(如 IntelliJ、Sonar)才会高亮提示“永远不为 null”;

7. 题外话

你知道隆达新桥吗?一座把监狱建在桥中央、把城市分裂在峡谷两岸的西班牙奇迹。

站在桥上,你看见的不只是建筑,而是历史、权力和地理共同撕开的地表。

8. DDD中,参数的校验应该在哪做?在Application service中可以做,在Domain-Factory构建对象的时候也可以做

一句话核心原则:

“是否和领域业务语义相关”,决定校验逻辑是否进入 Domain 层。与业务说话的进 Domain,和接口打交道的留在外头。

问题的核心不是“在哪里都可以做”,而是“做这件事的语义归谁”

类型

示例

应放位置

原因

领域规则校验

用户是否有发布权限、时间是否合理

Domain(Entity/Factory)

这是业务本身的一部分,决定行为是否合法

构造合法聚合根的条件

类别是否存在、金额是否超限

Domain(Factory)

聚合根必须是合法的业务对象

上下文一致性校验

当前用户是否归属某商户

ApplicationService

和调用者相关,不属于领域本身

防御性参数校验

判空、长度、格式、手机号是否合法

DTO或 AppService

跟业务语义无关,只是接口层的输入检查

Bonus:怎么判断一个规则是不是“业务规则”?

  • 是否跟具体业务场景(发布职位、投递简历)强绑定?
  • 是否会影响领域行为(发布、编辑、撤回等)的成立?
  • 是否可作为领域模型约束的一部分?

如果答案是“是”,那么它就是业务规则,应当进到 Domain。

9. domain service 能否注入其他 Bean?

可以注入,但要分清楚依赖的“性质”:

注入的 Bean 类型

是否允许

原因/建议

Domain Object / Repository

合法领域依赖,例如查用户套餐

Domain Service(协同)

多个领域服务组合逻辑

QueryApi(只读服务)

⚠️ 可用,但需封装抽象

读模型是外部依赖,应通过接口/适配层屏蔽

CommandApi(带副作用)

❌ 避免

domain service 应尽量纯粹,不直接引入副作用调用

第三方服务 / 客户端

❌ 绝不

属于 infrastructure 层依赖,破坏领域封装性

话是这么说,真做起来QueryApi哪有时间做适配。都是直接注入。

10. 给第三方出接口,一定要加业务唯一id 和 请求唯一id

对接的某个公司就只有一个请求唯一id,用来做防重放,简直是灾难级别的接口设计。

目的

字段

作用

幂等

业务唯一ID

保证“相同业务”不会被重复处理,比如重复提现

防重放

请求唯一ID

防止同一个请求被拦截/篡改后重复投递(重放攻击)

11. 策略模式的核心价值——在运行时可替换的行为抽象

  • 如果一种业务逻辑在不同情况下可以切换实现(比如支付渠道、排序算法、折扣计算方式),那么策略模式就能帮你把变化点独立出来,降低耦合。

  • 如果业务逻辑是硬编码规则(例如佣金结算单只能众腾开票,其他都必须百旺),那就没有动态替换的需求,抽策略反而是自找麻烦。这种固定映射更适合用 if/else 或 switch + Factory/Service 定向调用,而不是策略模式。因为未来要改成“可替换”概率很低,它只是一个路由决策,不是一个可变行为点。

12. 领域行为

Domain Behavior,领域对象自己提供的、与它业务职责相关的动作(方法),并且会修改自己状态或者返回一个新对象。

领域行为的几个特征

  1. 发生在领域对象内部

  2. 围绕业务概念

  3. 隐藏实现细节

  4. 保证领域不变量

总结

  • 领域行为 = 领域对象自己提供的、能代表业务语义的动作方法
  • 发生在领域对象内部
  • 负责维护业务规则和不变量
  • 能避免应用层直接改内部状态

13. public AuthWrappedRequestDTO 前后两个T各自的作用

1. 方法前面的

2. 方法返回类型的 AuthWrappedRequestDTO

所以,前后两个 T 其实都是同一个泛型类型,前面是声明,后面是使用。

14. List的话不返回Optional,单个查询返回optional

单对象查询 → Optional。避免了返回 null,减少了 NullPointerException 的风险。

多对象查询 → List,不包Optional,返回Collections.emptyList() 或 new ArrayList<>(),空列表()天然表示“没有结果”,无需额外包装。

15. MyBatis-Plus list返回结果直接stream

MyBatis-Plus 的 list() 返回的永远是 非 null 的 List(底层已经做了 Collections.emptyList() 处理)。

所以你不需要担心 list() 返回 null,直接 .stream() 是安全的。

MyBatis 自身的 ResultHandler 机制保证了 List 一定是非 null。

16. 查询方法命名

Controller / Service 层:更常用 getXxx 或 queryXxx,因为对外暴露接口,语义要直观。

Repository/Dao 层:多用 findBy...,贴近数据库语义。

17. 什么是应用编排,什么是领域逻辑?

if 判断的结果是否改变业务合法性?

if 判断依赖的是领域模型还是外部上下文?

18. Spring Modulith 注解 @NamedInterface

Spring Modulith 是 Spring 团队在近几年推出的一个框架,用来支持 模块化单体应用(Modular Monolith) 的开发。它的目标是让一个大的 Spring Boot 应用可以被拆分成内部模块(类似“bounded context”),并且在代码层面 enforce(约束)模块之间的依赖关系。

在 Spring Modulith 里,每个 package 都可以看作一个模块。而模块对外暴露哪些 API(哪些包能被外部依赖),就需要通过 @NamedInterface 来标注。

@NamedInterface 的作用:如果别的模块想要依赖你这个模块,只能依赖你 @NamedInterface("xxx") 标记的包。没有标注的包(比如 domain、internal)在概念上属于模块的“实现细节”,外部模块不应该直接引用。

19. factory 不依赖外部系统

Factory 可以做校验,但前提是 只涉及构建对象所需的内部规则。

如果校验需要依赖外部系统(API、Repository),那它就超出了 Factory 的职责,应该在 Application Service里做。

换句话说,Factory 接收的参数必须已经是“干净”的,外部数据准备、跨边界的校验,应该在应用层解决。

20. DDD Infrastructure 层职责

  1. 技术资源适配

  2. 外部系统调用适配

这些在 infra 层里通常以 Client/Adapter 的形式存在,例如:

public interface OrderQueryPort {
    OrderInfoDTO findByOrderNum(String orderNum);
}

@Component
@RequiredArgsConstructor
public class OrderQueryApiAdapter implements OrderQueryPort {
    private final OrderQueryApi orderQueryApi;
    @Override
    public OrderInfoDTO findByOrderNum(String orderNum) {
        return orderQueryApi.orderInfoByOrderNum(orderNum);
    }
}

然后 domain/application 依赖的是 OrderQueryPort 接口,不直接依赖 Feign 或 Dubbo API。

  • 解耦:domain/application 不知道你到底是调用 Feign、Dubbo 还是 mock,本地单测时可以直接替换。

  • 隔离变化:外部 API 签名、DTO 改了,只动 infra 层,不动 domain/application。

  • 统一规范:所有外部交互(无论数据库还是远程 API)都在 infra,代码更清晰。

21. 订单和业务收入类型挂钩,业务收入类型和发票挂钩

业界主流做法就是 “订单 → 收入类型 → 可开票种类” 这条链路。订单不直接决定开什么票,而是通过业务收入类型来决定。

为什么这样挂钩?

订单本身是交易事实,但税务系统不关心“你卖了个套餐还是抽了佣金”,它关心的是:这笔收入属于哪个税收分类编码。

所以要通过 业务收入类型(Revenue Type) 来做中间层。

这样你不用在每个订单里直接硬编码“发票内容”,而是通过配置来映射。