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);
}
}
现在应该将所有逻辑放入域模型中的方法中,以便action方法看起来像这样吗?
[HttpPost]
public ActionResult Payout(PayoutViewModel model)
{
var model = GetModel(model);
return View(model);
}
或者你会怎么做呢?
答案 0 :(得分:11)
我们将应用程序和业务逻辑放在单独的层(csproj文件)中,用于业务逻辑的域层和应用程序逻辑的服务层。这将它们完全从MVC项目中抽象出来。这对我们有两大好处。首先,业务逻辑与可能发生变化的模式无关。几年前,我们当中没有人会想到MVC的受欢迎程度,并且在几年内我们不知道是否会出现一些新的东西并取代MVC以便让绝大多数代码成为如果你想要放弃MVC以获得别的东西,那么对MVC“没有束缚”会有所帮助。
第二个好处是它使得不同的表示层非常容易实现。因此,如果您希望将业务逻辑呈现为WCF服务,则可以通过创建新的WCF项目并使其成为服务和域层的外观来轻松实现。它使维护变得非常容易,因为您的MVC项目和WCF服务都将使用相同的业务逻辑类。
示例强> 下面是我要做的一些示例代码。这很快,很脏,如果用户没有保存回数据库等,应该更多地添加日志...
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;
}
}
您的控制器现在看起来像这样
[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);
}
如果你不得不在网络服务中公开支付(我在企业环境中工作,所以这对我来说发生了很多事情)你做了以下事情......
public class MyWCFService : IMyWCFService
{
private PayoutService service = new PayoutService();
public PayoutResult Payout(string username, PayoutModel model)
{
return this.service.Payout(username, model);
}
}
答案 1 :(得分:6)
对我来说,关注点的分离是这些决定最重要的指导原则。因此,这取决于您的域的复杂程度以及您从复杂代码中获得的好处。
无论如何,作为一般规则,我倾向于向控制者提出以下问题:
而且,我倾向于引用非特定应用领域知识的模型(或服务):
所以,这就是我分割代码的方式:
[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);
}
}
保存应用程序状态,我通常在拦截器中包含。这样,您可以在整个请求周围打包unit of work transaction。
答案 2 :(得分:2)
我会将所有逻辑放在域模型中,并对域进行两次调用,一次用于验证,一次用于执行用例。
所以实体看起来像这样:
public class User
{
public double Balance { get;set; }
public ValidationMessageCollection ValidatePayout(double withdrawAmount)
{
var messages = new ValidationMessageCollection();
if (withdrawAmount > Balance)
{
messages.AddError("Not enough funds on your account");
}
return messages;
}
public void Payout(double withdrawAmount)
{
balance -= withdrawAmount;
}
}
你的控制器看起来像这样:
[HttpPost]
public ActionResult Payout(PayoutViewModel model)
{
if (!ModelState.IsValid)
{
return View(model);
}
var user = PublicUtility.GetAccount(User.Identity.Name);
var validationMessages = user.ValidatePayout(model.WithdrawAmount)
if(validationMessages.Any())
{
ViewBag.Message = validationMessages.ToSummary();
return View(model);
}
ViewBag.Message = "Successfully withdrew " + model.WithdrawAmount;
model.Balance = user.Balance;
model.WithdrawAmount = 0;
return View(model);
}
我还会做其他事情,例如插入应用程序/服务层,使用viewModels并在ViewModelBuilder / Mapper或simmilar中重置ViewModel,但这显示了基本的想法。
答案 3 :(得分:0)
我们遵循的方法要求ViewModel中包含的业务案例(您的案例:PayoutViewModel)并通过方法公开,这些方法将在控制器操作中使用。此外,我们对模型和视图模型有明确的分离,其中viewmodel引用其中的模型。
答案 4 :(得分:-2)
建议你在控制器上有一个瘦代码,你最好在另一个层中处理业务逻辑,比如我之前使用过的serviceLayer,它会返回你想要返回到你的视图的视图模型/控制器。甚至在服务层类中定义你的ajax方法。这会降低代码复杂性和可维护性问题。即使它更具可读性..
在您的Controller中,您可以使用DI注入serviceLayer类或将其实例化为
ServiceLayer test = new ServiceLayer() ;
然后在你的控制器
test.registerCustomer(model); // or
test.registerCutomer(CustomerViewModel);