【问题标题】:DDD and implementing contract in Application Layer from base class in Infrastructure LayerDDD 和从基础设施层中的基类在应用层中实现合同
【发布时间】:2021-11-22 23:57:56
【问题描述】:

所以我和同事讨论了从基类到接口的合同实现。

我们的 DDD 结构如下,Api -> Application -> Domain -> Infrastructure。 在基础架构中,我们使用 EF Core。

以下代码示例

应用程序

public interface IOrderRepository
{
    IUnitOfWork UnitOfWork { get; }
    Task AddOrderAsync(Order order);
}

基础设施

public class OrderRepository : BaseRepository<Order, DbContext>, IOrderRepository
{
    public OrderRepository(DbContext ctx) : base(ctx) { }

    public async Task AddOrderAsync(Order order)
    {
        try
        {
            await AddAsync(order);
        }
        catch (Exception ex)
        {
            Log.Error($"Exception: {ex}");
            throw ex;
        }
    }

    /*
     * 
     * Some other db methods
     * 
     */
}

public abstract class BaseRepository<T, U> where T : class where U : BaseDbContext, IUnitOfWork
{
    protected readonly U _context;

    public IUnitOfWork UnitOfWork 
    { 
        get 
        { 
            return _context; 
        } 
    }

    public BaseRepository(U context)
    {
        _context = context;
    }

    protected virtual async Task AddAsync(T entity)
    {
        await _context.Set<T>().AddAsync(entity);
    }
}

所以我主张,而不是在每个存储库中实现 AddNAMEAsync 方法,以在基类和相应的接口中使 AddAsync public virtual 并利用基类实现。这样,我们仍然可以在需要时订购 AddAsync,并最大限度地减少存储库中不必要的“重复代码”。

另一方面,我的同事认为这会使名称过于笼统,并且在调用存储库时,您仅通过阅读代码就不会知道要添加到上下文中的实体。并且还争论说我们不应该在接口中公开基类方法,而应该只公开 Parent -> Child。

我们使用 SOLID 原则,每个处理程序只处理一个实体/域聚合,我们对变量/对象也有非常清晰的命名,因此您可以轻松地在存储库名称以及在上下文中看到您要添加的内容领域模型

【问题讨论】:

    标签: c# architecture entity-framework-core domain-driven-design


    【解决方案1】:

    在命名方面,从我的角度来看,您可以安全地使用“AddAsync()”。更重要的是使用存储库的客户端使用 IOrderRepository 类型。在这种情况下,当查看 AddOrderAsync() 时,Order 部分在命名方面可能被视为多余,因为首先,您在已经表明一切都与订单有关的类型上调用它,其次,您正在传递 Order 类型的对象 这再次表明您要添加的内容。

    您仍然可以做的是明确指出泛型方法也是 IOrderRepository 可以扩展的泛型接口(例如 IRepository)的一部分。如果您想从基类派生您的基础架构 OrderRepository,或者如果您想将其传递到其他对象中以支持组合而不是继承(例如 DbContext),这是一个实现细节。

    您可以在 Microsoft 的微服务参考应用程序 (eshopOnContainers) 中看到我所指的 here 的说明。

    【讨论】:

      【解决方案2】:

      您可以在BaseRepository 中使用AddAsync(T entity) 方法,这将为您的所有实体提供可重用代码,但这已由Entity Framework Core 实现。你真的需要再次实现这个结构吗?

      EF Core 已经提供DbSet 作为基本存储库DbSet&lt;Order&gt; 作为您的订单存储库。还提供DbContext 作为工作单元实现。

      如果您不使用任何其他 ORM 工具或 EF Core 持久性方法,则无需创建通用基础存储库、存储库和工作单元类。 EF Core 已经封装了这些模式。

      用这种方式封装 EF Core 提供的类和方法会解决什么问题? EF Core 的 DbSet 和你的 BaseRepository 有什么区别?

      查看此示例project

      【讨论】:

      • 在需要存储库模式的代码中还需要其他一些限制,所以使用它是有原因的,一个是限制其他开发人员无限制地查询任何内容
      【解决方案3】:
      • 我认为您在基类中实现 AddAsync() 方法并从中继承其他类是正确的。因为,当您要使用AddAsync() 时,您将首先注入相关的类,然后再使用它。 (它将为您提供可重用的代码库)
      public class MyClass 
      {
         private readonly OrderService _orderService;
         
         //..
         await _orderService.AddAsync(...);
      }
      
      
      • 这类使用在命名方面是没有问题的,因为相关的服务和它会做什么都很清楚。

      顺便说一句,CreateAsyncInsertAsync 在我看来可能是一个更好的命名来演示数据库操作。

      【讨论】:

        猜你喜欢
        • 2017-06-10
        • 2020-10-12
        • 2019-01-27
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2010-11-25
        • 2013-09-19
        相关资源
        最近更新 更多