You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Clean Architecture中通用实体的数据库适配实现方案问询

Clean Architecture下多数据库实体适配的正确方案

核心问题分析

你当前的矛盾在于:数据库特定的IMongoEntity接口放在Infrastructure层,但Domain实体需要实现它才能被MongoRepository调用,导致Domain层反向依赖外层的Infrastructure层,直接违反了Clean Architecture依赖向内的核心原则(内层Domain不应依赖外层基础设施)。

正确实现方式

1. 重构Domain层的核心实体接口

在Domain层定义完全脱离数据库依赖的通用实体标识接口,让所有Domain实体仅实现这个纯业务导向的接口:

// Domain层
public interface IEntity<out TId>
{
    TId Id { get; }
}

// 示例Domain实体(仅包含业务属性)
public class User : IEntity<Guid>
{
    public Guid Id { get; init; }
    public string Username { get; set; }
    // 其他业务属性...
}

这里的泛型TId支持不同数据库的ID类型(SQL用int/long、Mongo用ObjectId/Guid、键值库用string等),彻底实现业务与数据库解耦。

2. 移除数据库特定的实体接口(IMongoEntity)

不需要让Domain实体继承任何数据库相关的接口,所有MongoDB的映射规则都放在Infrastructure层,通过类映射配置完成实体到Mongo文档的转换:

// Infrastructure层 - MongoDB模块
public static class MongoDbMappingConfigurator
{
    public static void RegisterMappings()
    {
        // 为User实体配置Mongo序列化规则
        BsonClassMap.RegisterClassMap<User>(map =>
        {
            map.AutoMap();
            // 配置Id字段的Mongo序列化逻辑
            map.MapIdProperty(u => u.Id)
               .SetIdGenerator(StringObjectIdGenerator.Instance) // 自动生成字符串格式的ObjectId
               .SetSerializer(new GuidSerializer(BsonType.String)); // 将Guid序列化为Mongo字符串类型
        });

        // 其他Domain实体的映射配置...
    }
}

在应用启动阶段(比如Program.cs)调用该配置方法,MongoDB驱动会自动按照规则处理实体序列化/反序列化。

3. 调整MongoRepository的泛型约束

修改MongoRepository,让它依赖Domain层的IEntity<TId>,而非数据库特定接口:

// Infrastructure层
public class MongoRepository<TEntity, TId> : ICrudRepository<TEntity, TId> 
    where TEntity : class, IEntity<TId>
{
    private readonly IMongoCollection<TEntity> _collection;

    public MongoRepository(IMongoCollection<TEntity> collection)
    {
        _collection = collection;
    }

    public void InsertOne(TEntity entity) => _collection.InsertOne(entity);

    public async Task InsertOneAsync(TEntity entity) => await _collection.InsertOneAsync(entity);

    // 直接使用Domain层的TId类型实现DeleteById
    public void DeleteById(TId id)
    {
        _collection.FindOneAndDelete(Builders<TEntity>.Filter.Eq(e => e.Id, id));
    }

    public async Task DeleteByIdAsync(TId id)
    {
        await _collection.FindOneAndDeleteAsync(Builders<TEntity>.Filter.Eq(e => e.Id, id));
    }

    // 其他CRUD方法同理调整...
}

4. 多数据库扩展逻辑

如果需要支持SQL、键值库等其他数据库,仅需在Infrastructure层为对应数据库实现ICrudRepository<TEntity, TId>即可,Domain层完全不需要修改——完美符合Clean Architecture的架构隔离要求。

方案合理性说明

  • 依赖方向合规:所有外层(Infrastructure)依赖内层(Domain),Domain层完全不感知任何数据库实现细节。
  • 实体纯度高:Domain实体仅包含业务属性和行为,没有任何数据库相关注解或接口,保持业务核心的独立性。
  • 扩展性强:新增数据库类型时,仅需在Infrastructure层添加对应的Repository实现和映射配置,不影响Domain和Application层的代码。

内容的提问来源于stack exchange,提问作者Memordei

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.23 04:18:16