2013-06-17 3 views
6

ASP.NET MVC4 - Grundsätzlich hatte ich alle meine Geschäftslogik in meinen Controllern (die ich versuche, in die Domain-Modelle zu setzen). Allerdings weiß ich nicht ganz, ob ALLE meine Geschäftslogik in die Domänenmodelle eingefügt werden soll oder ob einige in den Controllern bleiben sollen.Sollte sich die gesamte Geschäftslogik in Domänenmodellen befinden oder?

Zum Beispiel habe ich eine Controller-Aktion wie folgt:

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

Nun sollte die gesamte Logik in ein Verfahren in einem Domänenmodell gesetzt werden, so dass die Aktion-Methode wie folgt aussieht?

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

Oder wie würdest du das machen?

+0

Ich würde empfehlen, all Ihren Code in ein Domänenmodell zu setzen. Es macht den Controller viel sauberer. –

+0

@DarrenDavies Also selbst ModelState.IsValid sollte in das Domänenmodell gelegt werden? –

+1

Nein, 'ModelState.IsValid' ist MVC-Zeugs, ich würde den' if'-Bedingungsteil in die Domäne setzen, um ein Modell zurückzugeben oder eine Ausnahme auszulösen. –

Antwort

10

Wir fügen unsere Anwendungs- und Geschäftslogik in separate Schichten (csproj-Datei), eine Domänenebene für Geschäftslogik und eine Serviceschicht für Anwendungslogik ein. Dadurch werden sie vollständig vom MVC-Projekt abstrahiert. Das hat zwei große Vorteile für uns. Die erste ist, dass die Geschäftslogik nicht an ein Muster gebunden ist, das sich ändern könnte. Vor ein paar Jahren hätte sich keiner von uns vorstellen können, wie beliebt MVC heute ist, und in ein paar Jahren wissen wir nicht, ob es etwas Neues geben wird, das MVC ersetzen wird und den Großteil Ihres Codes bekommt "Ungebunden" zu MVC würde helfen, sollten Sie jemals MVC für etwas anderes verlassen wollen.

Der zweite Vorteil ist, dass verschiedene Präsentationsebenen sehr einfach zu implementieren sind. Wenn Sie also Ihre Geschäftslogik als WCF-Dienst darstellen möchten, können Sie dies ganz einfach tun, indem Sie ein neues WCF-Projekt erstellen und eine Fassade für Ihre Service- und Domain-Layer erstellen. Das macht die Wartung sehr einfach, da sowohl Ihr MVC-Projekt als auch Ihr WCF-Dienst dieselben Business Logic-Klassen verwenden.

Beispiel Unten ist ein Beispielcode, was ich tun würde. Dies ist schnell und schmutzig, und es sollte wie das Hinzufügen von Protokollierung es mehr sein, wenn der Benutzer nicht zurück in die Datenbank usw. nicht speichern ...

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

Ihr Controller nun wie folgt

[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); 
} 
aussehen würde

Und wenn Sie die Auszahlung in einem Web-Service aussetzen mussten (ich arbeite in einer Unternehmensumgebung, so dass dies für mich viel passiert), tun Sie Folgendes ...

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

    public PayoutResult Payout(string username, PayoutModel model) 
    { 
     return this.service.Payout(username, model); 
    } 
} 
+0

Nochmals mehr über die Platzierung von Geschäftslogik in der Service-Schicht sprechen ??? Geschäftslogik gehört in die Domäne? Meinst du aus der Sicht von MVC ist es in der Service-Schicht, weil das alles MVC sieht? während es in der Realität unterhalb der Service-Schicht in der Domain ist? –

-2

Es wird empfohlen, dass Sie einen dünnen Code auf Controllern haben, es ist besser, Geschäftslogik in anderen Schichten wie serviceLayer zu handhaben, die ich verwendet habe, bevor Sie ein Ansichtsmodell von dem zurückgeben, was Sie zu Ihrer Ansicht zurückkehren wollten /Regler. Auch Ihre Ajax-Methoden innerhalb einer Service-Schicht class.Which Abnahme Codekomplexität und Wartbarkeit definieren problems.Even es besser lesbar ist ..

In Ihrem Controller, den Sie DI können Sie die serviceLayer Klasse injizieren oder instansiate es als

ServiceLayer test = new ServiceLayer() ; 

dann in Ihnen

test.registerCustomer(model); // or 
    test.registerCutomer(CustomerViewModel); 
+1

Business-Logik sollte in der Domäne sein. Bitte hören Sie auf, Leute zu verwirren, indem Sie sagen, dass sie sich in der Service-Schicht befinden sollten. –

+0

warum es in domain ist, verstehe ich bitte nicht, also sagst du, wir brauchen gar nicht serviceLayer? – danielad

+1

Die Domäne ist das Modell des Unternehmens, ihre Klassen stellen Entitäten innerhalb des Unternehmens dar, und der darin enthaltene Code steht für die Geschäftslogik, andernfalls endet die Anemic Domain mit einem Anti-Pattern. –

0

Der Ansatz der Steuerung, die wir folgen hat im Ansichtsmodell (Ihr Fall: PayoutViewModel) eingeschlossen Geschäftsfällen erforderlich und ausgesetzt durch Verfahren und diese Methode in der Steuerung handeln verbraucht wird Ionen. Darüber hinaus haben wir eine klare Trennung von Modell und Ansichtsmodell, in der das Ansichtsmodell das Modell innerhalb dieses Modells referenziert.

2

würde ich die ganze Logik in dem Domänenmodell setzen und tun zwei Anrufe auf die Domäne, eine für die Validierung, eine für den Anwendungsfall ausgeführt wird.

So sieht das Unternehmen wie folgt aus:

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

Und Ihre Controller würde wie folgt aussehen:

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

Es gibt andere Dinge, die ich tun würde, wie eine Anwendung/Dienstschicht einsetzen, verwenden viewModels und das Zurücksetzen des ViewModel in einem ViewModelBuilder/Mapper oder ähnlich, aber dies zeigt die Grundidee.

5

Für mich ist die Trennung von Anliegen das wichtigste Leitprinzip für diese Entscheidungen. Es hängt also davon ab, wie komplex Ihre Domain ist und welchen Nutzen Sie daraus ziehen, den Code zu komplizieren.

Wie auch immer, als allgemeine Regel, neige ich Controller folgende Anliegen und Probleme geben:

  1. Instanziierung und Kartierung der Ansicht Modelle (es sei denn, es gibt erhebliche mapping)
  2. Ansicht Modellvalidierung

und ich neige dazu, auf ein Modell (oder Service) für nicht anwendungsspezifische Domänenwissen verweisen:

  1. Kann Geld
  2. Make Rückzug

So zurückziehen, das ist, wie ich den Code spalten würde:

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

Die Speicherung des Anwendungszustandes, ich in der Regel in einem Abfangjäger einpacken. Auf diese Weise können Sie eine unit of work transaction um die gesamte Anfrage wickeln.

+0

Ähnlich wie mein Ansatz, aber ich muss sagen, ich denke, es ist besser, eine Validierungsmethode zu haben, da es sonst zu Einschränkungen bei der Auszahlung kommen kann. –

+0

@DavinTryon Sie haben also die Methoden wie CanWithdrawMoney usw. im Account-Modell? Ich möchte die Entitäten von der eigentlichen Geschäftslogik trennen, also vielleicht sollte ich sie mit partiellen verbinden? –

+0

@JohnMayer Ja in der Konto-Domain-Einheit (Modell). Warum möchten Sie die Entitäten und Geschäftslogik trennen? Dies ist ein Antimuster, das "anämische" Domäneneinheit genannt wird. Domänenmodell-Entitäten sollten sowohl Daten als auch Verhalten aufweisen. –

Verwandte Themen