1. 仓储与持久化隔离的核心价值
在领域驱动设计(DDD)体系中,仓储(Repository)模式是实现领域层与基础设施层解耦的关键设计。最近在重构一个电商订单系统时,我深刻体会到这种隔离带来的好处——当需要将MySQL迁移到MongoDB时,领域层的业务逻辑完全不需要修改,只需调整仓储实现即可。这种架构弹性正是DDD提倡的"持久化无关性"原则的体现。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 仓储模式的实现原理
2.1 基础架构设计
典型的仓储模式包含三个核心部分:
- 领域层定义的仓储接口(如IOrderRepository)
- 基础设施层的具体实现(如OrderRepository)
- 领域对象与数据模型的转换逻辑
以用户模块为例,领域层会这样定义:
csharp复制public interface IUserRepository {
User FindById(UserId id);
void Save(User user);
// 其他领域相关查询...
}
2.2 转换策略对比
在实现数据转换时,通常有两种方案:
| 策略 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| 自动映射(如AutoMapper) | 开发效率高 | 隐藏业务语义 | 简单CRUD场景 |
| 显式转换 | 业务意图明确 | 代码量较大 | 复杂领域模型 |
在金融系统中,我坚持使用显式转换。比如处理账户余额时:
csharp复制public Account ToDomain(AccountPO po) {
return Account.Reconstruct(
new AccountId(po.Id),
new Money(po.Balance, Currency.FromCode(po.Currency)),
// 其他重建逻辑...
);
}
3. 数据库持久化实战
3.1 事务边界控制
仓储实现需要特别注意事务管理。在订单支付场景中,我采用以下模式:
csharp复制public class OrderRepository : IOrderRepository {
private readonly DbContext _context;
public void Save(Order order) {
using var transaction = _context.Database.BeginTransaction();
try {
// 1. 保存订单聚合根
var orderPO = ConvertToPO(order);
_context.Orders.Update(orderPO);
// 2. 保存领域事件
var events = order.DomainEvents;
_context.DomainEvents.AddRange(ConvertEvents(events));
_context.SaveChanges();
transaction.Commit();
} catch {
transaction.Rollback();
throw;
}
}
}
3.2 性能优化技巧
- 延迟加载陷阱:在仓储接口中明确标注是否需要关联数据
csharp复制public interface IProductRepository {
Product FindById(ProductId id, bool includeInventory = false);
}
- 批量操作:针对报表类查询实现特殊接口
csharp复制public IEnumerable<SalesReport> GetSalesReport(DateTimeRange range) {
return _context.Database.SqlQuery<SalesReport>(
@"SELECT ...",
range.Start,
range.End);
}
4. 常见问题解决方案
4.1 并发冲突处理
在库存扣减场景中,我采用乐观锁策略:
sql复制UPDATE inventories
SET quantity = quantity - @amount,
version = version + 1
WHERE product_id = @productId
AND version = @expectedVersion
配合仓储实现:
csharp复制public void UpdateInventory(Inventory inventory) {
var po = ConvertToPO(inventory);
int affected = _context.Database.ExecuteSqlRaw(
"...上述SQL...",
parameters);
if (affected == 0) {
throw new ConcurrencyConflictException();
}
}
4.2 复杂查询处理
对于跨聚合的复杂查询,我建议:
- 在应用层创建专门的QueryService
- 使用Dapper等轻量级ORM
- 返回DTO而非领域对象
例如商品搜索功能:
csharp复制public class ProductQueryService {
public PagedResult<ProductDto> SearchProducts(ProductSearchCriteria criteria) {
using var conn = new SqlConnection(_config.DbConnection);
var builder = new SqlBuilder();
// 动态构建查询条件...
var sql = @"SELECT p.*, s.stock_count
FROM products p
LEFT JOIN stocks s ON p.id = s.product_id
/**where**/";
return conn.QueryPaged<ProductDto>(sql, criteria);
}
}
5. 架构演进建议
当系统复杂度增加时,可以考虑:
- 引入CQRS模式分离读写模型
- 为关键聚合实现事件溯源(Event Sourcing)
- 使用Unit of Work模式管理事务范围
在最近的项目中,我们对订单模块做了如下改造:
plantuml复制@startuml
interface IOrderRepository {
+ FindById()
+ Save()
}
class EventSourcedOrderRepository {
+ AppendEvents()
+ Rehydrate()
}
IOrderRepository <|-- OrderRepository
IOrderRepository <|-- EventSourcedOrderRepository
@enduml
重要提示:仓储实现应该保持"笨拙"——它只是数据的保管者,不应该包含业务逻辑。所有业务规则都应该在领域模型中体现。
