【问题标题】:How and where should domain object methods be consumed?应该如何以及在哪里使用域对象方法?
【发布时间】:2023-03-23 06:09:01
【问题描述】:

我正在为带有 DI 和 ORM 的 ASP.NET MVC 应用程序开发模型。

最近,我一直在研究在服务层中编写所有业务逻辑与将特定于实体的逻辑放在实体类本身中的优缺点。实体类中声明的方法显然是在实体的特定实例上调用的,因此只能在该实例已从查询实例化到 ORM 时调用。

假设我有一个Product 实体,并在其上声明了一个ApplyDiscount 方法。给定从控制器的操作方法传入的产品的ID,我必须首先使用此ID 查询产品的实例,然后调用ApplyDiscount 方法。但是查询代码应该在哪里发生呢?在我的服务层中声明一个方法是否有效,该方法采用ID,查询Product 实例,然后在该实例上调用ApplyDiscount?或者该代码应该去其他地方吗?

最终,我想知道在尝试避免胖服务层和贫血域时,在服务层中查询代码并在实体类本身中修改结果实体的代码是否是常见/正确的实现型号。

在服务层中有查询代码是否完全违背了目的?

【问题讨论】:

  • 如果有人能提供讨论这个问题的参考资料就好了。

标签: c# asp.net-mvc oop design-patterns


【解决方案1】:

它并没有违背目的,但您最终会将功能(特别是验证)迁移到它不应该存在的服务层中。

通常,您希望尽早进行验证;也就是说,如果您获得了一个无效的 ID,您需要确保在启动执行链之前通过您的逻辑全部捕获该无效条件。通常,这意味着在控制器中执行查询并将结果对象传递给您的服务层;这将在执行链中向上隔离查询的功能,并防止您必须在服务层中实现高级排除逻辑(例如,如果整个 ID 集无效,您可以执行该验证在您的服务层之外,从而隔离相关的逻辑并使您无需在服务层内执行此操作。

一般而言,请考虑以下方法:在最早的参考点执行对象级验证和反序列化,将您的逻辑尽可能地迁移到您的服务层之外,从而“精简”您的服务层。当然,这里涉及到一定程度的灵活性,但作为一般规则,这是一个很好的规则。

【讨论】:

  • "这意味着在控制器中执行查询并将结果对象传递给您的服务层。"那么服务层方法会简单地将产品作为参数并调用ApplyDiscount 吗?那么单行 myproduct.ApplyDiscount() 服务层方法肯定很薄。控制器需要引用各种存储库来查询和执行验证。
  • @Sanjeev:是的,假设这就是您的服务层所做的一切,它肯定很薄。当你的服务层开始积累逻辑时,这真的开始变得有价值(因为它几乎肯定会随着时间的推移);您可以避免在服务层中使用的任何逻辑都会使其更简单。作为旁注,ApplyDiscount 不是您的服务层的功能吗?你考虑过要不要?
  • 好吧,ApplyDiscount 只是一个例子,但在这个主题上,我当然可以使用指导方针来确定哪些功能决定了这些方法的放置位置。 ApplyDiscount 不会影响任何其他实体,因此我最初假设它属于 Product 方法。
  • @Sanjeev:不让 Discount 成为一个引用产品的实体的理由是什么,只有一个 Apply 方法?我并不是说应该这样做,但看起来这可能是一种不同但有效的看待事物的方式,可能会带来一些清晰。不同的观点,等等。
  • 我认为您需要仔细查看域。您是对product 应用折扣(如所有客户的销售价格),还是对invoice line item 应用折扣?
【解决方案2】:

我真的不认为这里需要一个服务层。如果逻辑只影响一个实体,我会将逻辑放在实体本身中。 (显然您不应该相信用户的输入,我只是将其显示为假设代码。)

public ViewResult ApplyDiscount(int productId, decimal percent)
{
    var product = Database.Get<Product>(productId);
    product.ApplyDiscount(percent);
    Database.Save(product);
    return View(product);
}

产品:

public void ApplyDiscount(decimal percent)
{
    this.Price = this.Price * (1 - discount);
}

【讨论】:

  • 你的意思是this.Price = this.Price * (1 - (percent/100)); 对吧?无论如何,您放置在控制器操作方法中的代码是否应该放置在专用的服务层方法中似乎是一个争论的问题。哪个应该是优先事项:在我的服务层中使用最少的代码或在我的控制器中使用最少的代码?据我所知,瘦控制器和瘦服务层都很重要。
  • @Sanjeev 好吧,我实际上会选择一个比percent 更好的名字,因为不清楚这意味着什么。我曾经把所有东西都放在一个服务中,并且有一个非常薄的控制器,但我意识到我只是添加了一个不必要的层,因为我只将 MVC 用于 UI。我仍然在实体中保留所有逻辑。
【解决方案3】:

目前我在实体上使用静态Find 方法,例如:

public ActionResult ApplyDiscount(int id, decimal percent) {

   Product product = Product.Find(id);

   if (product == null)
      return HttpNotFound();

   var result = product.ApplyDiscount(percent);

   if (result.IsError)
      return ViewWithErrors(result);

   return RedirectToAction("DiscountApplied");
}

ApplyDiscount 返回一个带有验证错误的对象,可以映射到 ModelState 错误。

【讨论】:

    猜你喜欢
    • 2019-06-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-04-02
    • 1970-01-01
    • 2023-03-03
    • 2011-01-27
    • 2012-07-18
    相关资源
    最近更新 更多