所有业务逻辑都应该在域模型中吗?

Joh*_*yer 6 .net c# asp.net-mvc asp.net-mvc-4

ASP.NET MVC4 - 基本上我曾经在我的控制器中使用我的所有业务逻辑(我试图将其放入域模型中).但是,我不太清楚我的所有业务逻辑是否应该放入域模型中,或者是否应该保留在控制器中?

例如,我得到了一个控制器动作,如下所示:

[HttpPost]
    public ActionResult Payout(PayoutViewModel model)
    {
        if (ModelState.IsValid)
        {
            UserProfile user = PublicUtility.GetAccount(User.Identity.Name);
            if (model.WithdrawAmount <= user.Balance)
            {
                user.Balance -= model.WithdrawAmount;
                db.Entry(user).State = EntityState.Modified;
                db.SaveChanges();

                ViewBag.Message = "Successfully withdrew " + model.WithdrawAmount;
                model.Balance = user.Balance;
                model.WithdrawAmount = 0;
                return View(model);
            }
            else
            {
                ViewBag.Message = "Not enough funds on your account";
                return View(model);
            }
        }
        else
        {
            return View(model);
        }
    }
Run Code Online (Sandbox Code Playgroud)

现在应该将所有逻辑放入域模型中的方法中,以便action方法如下所示?

[HttpPost]
    public ActionResult Payout(PayoutViewModel model)
    {
        var model = GetModel(model);
        return View(model);
    }
Run Code Online (Sandbox Code Playgroud)

或者你会怎么做呢?

ner*_*rdo 11

我们将应用程序和业务逻辑放在单独的层(csproj文件)中,用于业务逻辑的域层和用于应用程序逻辑的服务层.这将它们完全从MVC项目中抽象出来.这对我们有两大好处.首先,业务逻辑与可能发生变化的模式无关.几年前,我们当中没有人会想到MVC的受欢迎程度,并且在几年内我们不知道是否会出现一些新的东西并取代MVC以便让绝大多数代码成为如果你想要放弃MVC以获得其他东西,那么对MVC"不绑定"会有所帮助.

第二个好处是它使得不同的表示层非常容易实现.因此,如果您希望将业务逻辑呈现为WCF服务,则可以通过创建新的WCF项目并使其成为服务和域层的外观来轻松实现.它使维护变得非常容易,因为您的MVC项目和WCF服务都将使用相同的Business Logic类.

下面是我要做的一些示例代码.这是快速和脏的,如果用户不保存回数据库等,应该更多地添加日志记录...

public enum PayoutResult
{
    UserNotFound,
    Success,
    FundsUnavailable,
    DBError
}

public class UserProfile
{
    public float Balance { get; set; }

    public string Username { get; set; }

    // other properties and domain logic you may have

    public bool Withdraw(PayoutModel model)
    {
        if (this.Balance >= model.Amount)
        {
            this.Balance -= model.Amount;
            return true;
        }

        return false;
    }
}


public class PayoutService
{
    IUserRepository userRepository;

    public PayoutService()
    {
        this.userRepository = new UserRepository();
    }

    public PayoutResult Payout(string userName, PayoutModel model)
    {
        var user = this.userRepository.GetAll().SingleOrDefault(u => u.Username == userName);
        if (user == null)
        {
            return PayoutResult.UserNotFound;
        }

        // don't set the model properties until we're ok on the db
        bool hasWithdrawn = user.Withdraw(model);
        if (hasWithdrawn && this.userRepository.SaveUser(user))
        {
            model.Balance = user.Balance;
            model.Amount = 0;

            return PayoutResult.Success;
        }
        else if (hasWithdrawn)
        {
            return PayoutResult.DBError;
        }

        return PayoutResult.FundsUnavailable;
    }
}
Run Code Online (Sandbox Code Playgroud)

你的控制器现在看起来像这样

[HttpPost]
public ActionResult Payout(PayoutModel model)
{
    if (ModelState.IsValid)
    {
        var result = service.Payout(User.Identity.Name, model);
        // This part should only be in the MVC project since it deals with 
        // how things should be presented to the user
        switch (result)
        {
            case PayoutResult.UserNotFound:
                ViewBag.Message = "User not found";
                break;
            case PayoutResult.Success:
                ViewBag.Message = string.Format("Successfully withdraw {0:c}", model.Balance);
                break;
            case PayoutResult.FundsUnavailable:
                ViewBag.Message = "Insufficient funds";
                break;
            default:
                break;
        }               
    }

    return View(model);
}
Run Code Online (Sandbox Code Playgroud)

如果你不得不在Web服务中公开支付(我在企业环境中工作,所以这对我来说发生了很多)你做了以下......

public class MyWCFService : IMyWCFService
{
    private PayoutService service = new PayoutService();

    public PayoutResult Payout(string username, PayoutModel model)
    {
        return this.service.Payout(username, model);
    }
}
Run Code Online (Sandbox Code Playgroud)


Dav*_*yon 6

对我来说,关注点的分离是这些决策最重要的指导原则.因此,这取决于您的域的复杂程度以及您从复杂代码中获得的好处.

无论如何,作为一般规则,我倾向于给控制器以下问题:

  1. 视图模型的实例化和映射(除非有相当大的映射)
  2. 查看模型验证

并且,我倾向于引用非应用程序特定领域知识的模型(或服务):

  1. 可以提款
  2. 退出

所以,这是我分割代码的方式:

    [HttpPost]
    public ActionResult Payout(PayoutViewModel model)
    {
        if (ModelState.IsValid)
        {
            var account = accountRepository.FindAccountFor(User.Identity.Name);

            if (account.CanWithdrawMoney(model.WithdrawAmount))
            {
                account.MakeWithdrawal(model.WithdrawAmount);

                ViewBag.Message = "Successfully withdrew " + model.WithdrawAmount;
                model.Balance = account.Balance;
                model.WithdrawAmount = 0;
                return View(model);
            }

            ViewBag.Message = "Not enough funds on your account";
            return View(model); 
        }
        else
        {
            return View(model);
        }
    }
Run Code Online (Sandbox Code Playgroud)

保存应用程序状态,我通常在拦截器中结束.这样,您可以围绕整个请求包含一个工作单元事务.