I understand MVC is all about putting things in the correct place and the logic where it should be. My controller actions are getting filled up with business logic (not related to data storage) and I feel that I should start moving some of the logic to a different place.
Is there a convention for where I should place this logic? For example I have the following controller that's located in the controllers file:
adminPowerController
public ActionResult Create(string test1)
// business logic
// business logic
// business logic
return View();
}
public ActionResult Index(string test1)
// business logic
// business logic
// business logic
return View();
}
Business logic should live in the data model. And, what's more, it should live in the graph data model because that's the right abstraction for the next twenty years. If you've been paying attention to this blog or to Stardog generally, then you must have known this is where we were going to end up.
Let me tell you this, controllers shouldn't do anything remotely related to business logic, and directly access data stores. The controller's only purpose is to receive a request and return a response. Everything that goes in between is not its responsibility.
You can start with Use of Service Layer stack overflow or Implementing Business Logic from docs.oracle.com. Now, if we look at both ways mentioned in your question, then it represents that in the first case "write business logic in Controller" is not the way you should go.
A controller is responsible for controlling the way that a user interacts with an MVC application. A controller contains the flow control logic for an ASP.NET MVC application. A controller determines what response to send back to a user when a user makes a browser request.
The recommended place to put business logic is into a service layer. So you could define an interface which will represent the business operation:
public interface IMyService
{
DomainModel SomeOperation(string input);
}
and then have an implementation of this service. Finally the controller will use it:
public class MyController: Controller
{
private readonly IMyService _service;
public class MyController(IMyService service)
{
_service = service;
}
public ActionResult Create(string input)
{
var model = _service.SomeOperation(input);
var viewModel = Mapper.Map<DomainModel, ViewModel>(model);
return View(viewModel);
}
}
and configure your DI framework to pass the proper implementation of the service into the controller.
Remark: In the example I provided I used AutoMapper to convert between a domain model into a view model which is passed to the view.
What i tend to do in my MVC projects is to keep as much of the business logic as possible outside of my actions so that I can test them
In some cases I create a service layer and then use that
public class QuizRunner : IQuizRunner
{
private readonly IServiceProxyclient _quizServiceProxy;
public QuizRunner(IServiceProxyclient quizServiceProxy)
{
_quizServiceProxy = quizServiceProxy;
}
public GameCategory GetPrizeGameCategory(int prizeId)
{
return _quizServiceProxy.GetGameCategoryForPrizeId(prizeId);
}
}
public interface IQuizRunner
{
GameCategory GetPrizeGameCategory(int prizeId);
}
private IQuizRunner_serviceClass;
public AdminPowercontroller(IQuizRunner serviceClass)
{
_serviceClass = serviceClass;
}
public ActionResult Create(string test1)
var itemsFromLogic = _serviceClass.Method1();
return View();
}
public ActionResult Index(string test1)
var gameCategory = _serviceClass.GetPrizeGameCategory(test1);
var viewModel = Mapper.Map<GameCategory, GameCategoryViewModel>(gameCategory);
return View(viewModel);
}
this allows my actions to be tested separately from my service layer and without dependency
Hope this helps
Paul
Business logic should live in your domain model separated from MVC framework and other stuff.
Real world example...
Application (one of my domain entities) controller:
[HttpPost]
public ActionResult Withdraw(int applicationId){
//find it from repository or whatever
var app=FindApplication(applicationId);
//force it do do stuff
a.Withdraw();
//send back some response
return RedirectToAction("Application",new{applicationId});
}
Application entity itself:
public class Application{
public void Withdraw(){
//check if current user is authorized to withdraw applications
Authorize<CanWithdrawApplications>();
//check if application itself can be withdrawn
ThrowIf(!CanBeWithdrawn(),"Application can't be withdrawn.");
//apply state changes
IsWithdrawn=true;
//raise domain event
Raise(new Withdrawn(this));
}
public bool CanBeWithdrawn(){
return !IsWithdrawn && !Project.Contract.IsSigned;
}
}
For more about this You might want to check out what domain driven design is about.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With