Files
StoreWeb/.agents/skills/storeweb-module/references/transactions.md
T
2026-10-29 11:34:35 +08:00

4.1 KiB

事务与命令管道约定

仅在涉及多步写入一致性、并发控制、幂等或命令管道时读取。适用于本项目所有业务模块,锁定资源、去重范围和权限条件由各模块实际需求决定。路径相对于仓库根目录。

Service 中的事务

  1. DTO 非空验证和不依赖数据库的业务参数校验先于事务;依赖数据库状态的校验在获取必要状态后完成,写入前必须满足业务条件。
  2. 用 orm.CreateUnitOfWork()、GetOrBeginTransaction() 开启工作单元。事务内相关读写统一使用 work.Orm,不能混用根 ORM 使部分操作脱离事务。
  3. 全部写入成功后 Commit(),未提交由工作单元释放回滚。不 catch 所有异常后继续提交,不封装同义事务工具。
  4. 异步数据库调用传递 CancellationToken,提交前检查取消。数据库事务不覆盖外部通知、文件或第三方服务等副作用,按现有业务机制安排执行与失败处理,不默认增加额外基础设施。
  5. 只更新允许修改的字段。需要避免丢更新的操作使用既有锁或条件更新,并检查受影响行数;不要把“必须影响一行”套到允许无变化的幂等更新上。

并发与幂等

  • 涉及金额时沿用 decimal;允许值、精度和上限由业务入口判断,可能溢出的累计运算按需要使用 checked。
  • 同一操作锁定多条资源时采用稳定顺序;在锁定或并发条件保护的有效状态上判断是否允许修改。进程内锁不能替代跨进程数据库一致性。
  • 业务变更与必须同步保存的流水、审计记录在同一事务提交或回滚,记录实际操作者与业务目标。
  • 幂等操作明确请求号的作用范围和重复请求结果,保留现有请求号、唯一约束及处理机制;不能把某模块的去重键当作全项目统一规则。
  • 去重检查和写入使用能阻止竞态的数据库约束或锁机制,不能仅无保护地先查后写。重试按接口约定复用请求号。
  • MySQL 表引擎、约束、隔离及并发行为需要对应环境验证,SQLite 只承担部分持久化和回滚验证。

MediatR 管道

复用以下框架实现,按当前文件确认行为:

  • Core/MAX.Framework/Mediation/ValidationBehavior.cs
  • Core/MAX.Framework/Mediation/TransactionBehavior.cs
  • Core/MAX.Framework/Mediation/UnitOfWorkExecutionContext.cs
  • Core/MAX.Framework/DependencyInjection/MediatRBehaviorServiceCollectionExtensions.cs

命令携带业务输入 DTO,可信操作者由服务端上下文提供。普通 Service 方法不必建立同义命令。

  • 通过 [MediatRBehavior(typeof(...))] 选择必要步骤,顺序由实际依赖决定。基础输入校验先于事务;需要事务内状态的业务步骤位于事务内部。
  • IValidatableRequest.Validate() 调用 RequestValidator.Validate(Input) 完成 DTO 非空校验;长度、格式、范围及组合规则仍放在 Service 或已有业务处理入口,不迁入 DTO。后续 Handler 不重复基础校验。
  • 不依赖数据库的业务参数校验在事务前执行,优先使用已有业务处理入口或适用的业务管道,不为单次判断机械新增一层。
  • 事务内管道和 Handler 统一使用 UnitOfWorkExecutionContext.Orm。
  • TransactionBehavior 统一提交、取消检查和回滚;当前实现拒绝嵌套事务命令。组合操作在同一事务用例中完成,不递归发送另一个事务命令。
  • 已授权上下文是内部执行结果,不是客户端输入。保留必要上下文清理、执行顺序和重入约束,不把这些当作重复 DTO 校验删除。
  • 在 IStartup.Register(IServiceRegistrar) 注册 Handler,宿主通过 AddMediatRAttributedBehaviors 扫描特性。确认模块程序集被现有机制发现,不另写扫描器或重复注册。

验证

按本次变化验证成功提交、任一步骤失败时整体回滚、权限或状态拒绝时无写入、重复请求处理,以及实际操作者的审计记录。涉及管道时使用真实管道入口验证顺序和注册;并发锁验证使用对应数据库,不以单线程 SQLite 测试替代。