5

I am learning Onion Architecture by Jeffrey Palermo for more than 2 weeks now. I have created a test project by following this tutorial. While studying I came across this question on SO. According to accepted answer, one person nwang suggests that Methods like GetProductsByCategoryId should not be in Repository and one the other hand Dennis Traub suggests that it is the responsibility of the Repository. What I am doing is :

I have a General Repository in Domain.Interface in which I have a method Find :

public interface IRepository<TEntity> where TEntity : class
{
     IEnumerable<TEntity> Find(Expression<Func<TEntity, bool>> filter = null);
     .......
     .......
     .......
}

Then I created a BaseRepository in Infrastucture.Data:

public class RepositoryBase<TEntity> : IRepository<TEntity> where TEntity : class
{
     internal readonly DbSet<TEntity> dbSet;
     public virtual IEnumerable<TEntity> Find(
            Expression<Func<TEntity, bool>> filter = null)
     {
            IQueryable<TEntity> query = dbSet;

            if (filter != null)
            {
                query = query.Where(filter);
            }
            return query.ToList();
     }
}

And I have a concrete repository in Infrastructure.Data

public class ProductRepository : RepositoryBase<Product>, IProductRepository
{
      public ProductRepository(MyDBContext context)
           : base(context)
       {         

       }
}

Now what I am doing in my Service Layer is Injecting Repository into Service and calling Repository.Find for methods like GetProductsByCategoryId. Like :

public class ProductService : IProductService
{
     private readonly IUnitOfWork _unitOfWork;
     private readonly IProductRepository _productRepository;

     public ProductService(IUnitOfWork unitOfWork, IProductRepository productRepository)
     {
          _unitOfWork = unitOfWork;
          _productRepository = productRepository;
     }

     public IList<Product> GetProductsByCategoryId(int CategoryId)
     {
          // At the moment, My code is like this:
          return _productRepository.Find(e => e.CategoryId == CategoryId).ToList();

          // My confusion is here. Am I doing it right or I need to take this code to 
          // ProductRepository and call _productRepositoy.GetProductsByCategoryId(CategoryId) here instead.
          // If I do this, then Service Layer will become more of a wrapper around repository. Isn't it?
          // My question is : What exactly will be the responsibility of the Service Layer in Onion Architecture?
         }
    }
4

3 回答 3

6

您设计应用程序的方式还可以……但前提是您的服务将处理其他事情,而不仅仅是包装存储库方法!

始终牢记YAGNI 原则

总是在你真正需要的时候实施,而不是在你预见到你需要它们的时候

假设您有一个用户故事,它说每当在您的数据库中找不到产品描述时,您应该从其他地方检索它(调用外部服务或其他东西)。那么很明显你的 ProductService 必须有一个

private readonly IProductRepository _productRepository;

但也是一个

private readonly IProductDescriptionService _productDescriptionService;

在这种情况下,在存储库之上添加一个服务层真的很有意义。

于 2013-07-03T09:33:53.243 回答
5

我发现有时事情可能会为了它而变得抽象,并没有提供真正的价值。我会说您示例中的结构很好并且正确地遵循了模式。您的服务层正确地服务于客户端 UI 的需求,它与数据层松散耦合,并包含操作数据所需的任何业务逻辑。

我一直认为从简单开始并建立在你的结构上比过度抽象、过度复杂和过度膨胀项目更有效率。业务或技术案例通常会推动项目,并决定是否需要它。

于 2013-07-03T08:30:26.507 回答
4

尽管在这种情况下,稍后的服务似乎只是一个包装器,但有时您可能需要添加一些业务逻辑或调用两个存储库。假设您有一个名为 CartService 的服务,并且您有一个名为 AddToCart 的方法,您需要首先获取产品,进行一些计算,然后调用 insert 到另一个存储库,如下所示。

public class CartService : ICartService 
{
   private readonly IUnitOfWork _unitOfWork;

   public CartService(IUnitOfWork unitOfWork)
   {
      _unitOfWork = unitOfWork;
   }

   public void AddToCart (int productId, int quantity)
   {
     var product = _unitOfWork.ProductRepository
                              .Find(p => p.ProductId == productId).Single();

     var cartItem = new CartItem { 
                    ProductId = productId, 
                    Desc = product.Desc, 
                    Quantity = quantiry 
                  };

     _unitOfWork.CartRepository.Add(cartItem);  
  }
}

更复杂的场景包括调用第三方网络服务等。

于 2013-07-03T22:03:34.800 回答