【问题标题】:MVC architectural question - Where should payment processing go?MVC 架构问题 - 支付处理应该去哪里?
【发布时间】:2011-01-30 20:18:27
【问题描述】:

这个问题与我的 ASP.NET MVC 2 开发有关,但它可以适用于任何 MVC 环境以及逻辑应该去哪里的问题。

假设我有一个控制器,它接受在线支付,例如购物车应用程序。我有接受客户信用卡信息的方法:

public class CartController : Controller
    CartRepository cartRepository = new CartRepository()

    [HttpPost]
    public ActionResult Payment(PaymentViewModel rec)
    {
        if(!ModelState.IsValid)
        {
            return View(rec);
        }

        // process payment here

        return RedirectToAction("Receipt");
    }

在评论process payment here应该处理付款:

  1. 在控制器中?
  2. 通过存储库?
  3. 其他地方?

【问题讨论】:

    标签: asp.net-mvc model-view-controller architecture


    【解决方案1】:

    我建议在业务对象中构建此逻辑并从您的控制器中调用它。

    例如创建一个 PaymentBO 类(静态或其他),以便您可以调用 PaymentBO.ProcessPayment(...)

    【讨论】:

      【解决方案2】:

      Model 应该处理这个问题,因为它是负责直接处理数据(并保持数据一致性)的组件。具体来说:

      模型是应用程序对其进行操作的数据的特定领域表示。

      【讨论】:

      • 模型只是传递给视图的对象。我认为商业逻辑不应该放在模型上。也就是说,我之前已经看过一百次了……
      • @Hunter 业务逻辑不应该在您的视图模型中,但如果您有,它应该在您的域模型中。
      • 我不同意,如果您认为大多数模型不过是域对象(ASP.NET MVC 有点模糊这两者,令人沮丧)。我尽量保持我的域对象(例如表示数据表的对象)和我的模型对象(可以是域对象或域对象的集合或其他)尽可能干净,并将 BO 用于此类事情......
      • 如果您的域对象没有行为,您将失去 mvc 设计的主要价值,因为它是本地化应用程序关注点的最简单方法。你最终会得到一个贫血的领域,要么是最重要的观点,要么是令人费解的行为。话虽如此,我更愿意提取一个用于与支付服务对话的接口,并让控制器为您正在使用的支付网关传递它的域或适配器对象的注入实例。
      • 我想这取决于个人喜好和模式。
      【解决方案3】:

      如果您使用的是存储库模式,那么我建议您创建一个服务对象来处理任何付款,并让您的控制器调用它。

      【讨论】:

        【解决方案4】:

        你想要 3. 其他地方。

        把它放在一个类库中。创建一个接口,其中包含支付处理所需的所有方法。使方法通用。将细节放在接口的实现中。然后从该接口派生您的支付处理服务。这为您提供了包括测试和多个支付处理器在内的选项。

        观看 http://www.asp.net/learn/mvc-videos/ 的 MVC Storefront 视频。可能是视频 #23(第 22 部分)。我已经有一段时间没有看到这些了。

        【讨论】:

        • @37 颗星。这些视频很好。谢谢你指点我那里。
        【解决方案5】:

        这是一个架构问题。对于整个系统,您应该决定哪种实现业务逻辑的方法最适合您的特定场景。 Martin Fowlers PoEAA 对这些方法进行了最好的描述(在我看来)。主要有以下三种模式:

        • 交易脚本 - 意味着有一个单独的对象来处理特定的交易
        • 活动记录 - 意味着将简单的逻辑直接放在 O/RM 映射的表表示对象上
        • 领域模型 - 意味着拥有丰富的纯代码模型,负责解决问题

        选择主要取决于您的系统复杂程度。我描述的模式是按照它们解决复杂问题的潜力排序的(DDD 最擅长处理真正复杂的事情,但它本身也会引入一些“意外”复杂性)。

        【讨论】:

          【解决方案6】:

          由于已经有关于存储库模式的参考,您可以详细说明域驱动设计(如 Szymon 所述)。

          如果您这样做,您将需要一个服务层,该服务层反过来与它下面的丰富模型进行对话。 这是伪代码

          在支付控制器上:

          var paymentDetails = mapFromViewModel(rec)
          PaymentService.Pay(paymentDetails)
          

          关于支付服务

          void Pay (PaymentDetails paymentDetails)
          {
            // leverage on rich model behavior here
            if (User.IsHaveEnoughMoney)
            {
              Cashier.Pay( ... )
            }
          
            // more ...
          
          }
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2012-10-09
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2011-04-20
            • 2012-06-05
            相关资源
            最近更新 更多