Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Should all business logic be in domain models or?

ASP.NET MVC4 - Basically I used to have all my business logic in my controllers (which I'm trying to put into the domain models instead). However I don't quite know if ALL my business logic should be put into the domain models or if some should remain in the controllers?

For instance I got a controller action as shown below:

[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);
        }
    }

Now should all the logic be put into a method in a domain model so the action method looks like this?

[HttpPost]
    public ActionResult Payout(PayoutViewModel model)
    {
        var model = GetModel(model);
        return View(model);
    }

Or how would you go around doing it?

like image 923
John Mayer Avatar asked Jun 17 '13 08:06

John Mayer


People also ask

Should business logic go in models?

For non trivial applications, business logic/business rules/data access should not be placed directly into Models, Views, or Controllers. To do so would be placing business logic in your presentation layer and thus reducing reuse and maintainability of your code.

Where should business logic be?

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.

Can models have business logic?

The model in MVC should also be able to implement business logic on the data it represents. For example, common business logic for web applications includes validation rules. Validation rules enforce that data coming into the application meets the constraint of the design of the system required.

Where does business logic go in DDD?

All invariant to use-cases logic (business entities, business workflow components, e.g. Domain model, Domain services) goes to the Domain layer (Domain logic). This layer is responsible for concepts of the business domain and business rules.


1 Answers

We put our application and business logic into separate layers (csproj file) a domain layer for business logic and a service layer for application logic. This abstracts them out from the MVC project completely. This has two big benefits for us. The first is that the business logic isn't tied to a pattern that could change. A few years ago none of us would have imagined the popularity of MVC today, and in a a few years we don't know if there will be some new thing that will come along and replace MVC so getting the vast majority of your code to be "un-tied" to MVC would help should you ever want to abandon MVC for something else.

The second benefit is it makes having different presentation layers very easy to implement. So if you wanted to present your business logic as a WCF service you could do that very easily by creating a new WCF project and making that a façade for your Service and domain layers. It makes maintenance very easy since both your MVC project and your WCF service would be using the same Business Logic classes.

Example Below is some example code of what I would do. This is quick and dirty and there should be more to it like adding logging if the user doesn't save back to the database etc...

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;
    }
}

Your controller would now look like this

[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);
}

And if you had to expose the payout in a web service (I work in an enterprise environment so this happens a lot for me) You do the following...

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

    public PayoutResult Payout(string username, PayoutModel model)
    {
        return this.service.Payout(username, model);
    }
}
like image 191
nerdybeardo Avatar answered Nov 12 '22 00:11

nerdybeardo