2013-06-17 12 views
6

ASP.NET MVC4 - Zasadniczo miałem w swojej kontrolerce wszystkie moje logiki biznesowe (które staram się umieścić w modelach domen). Jednak nie wiem, czy WSZYSTKIE moje logiki biznesowe powinny zostać umieszczone w modelach domeny, czy też niektóre powinny pozostać w kontrolerach?Czy cała logika biznesowa powinna być w modelach domenowych czy?

Na przykład mam działania kontrolera, jak pokazano poniżej:

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

Teraz powinniśmy logika być wprowadzane do sposobu w modelu domeny więc metoda działania wygląda następująco?

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

Albo jak to robiłbyś?

+0

Zalecam umieszczenie całego kodu w modelu domeny. To sprawia, że ​​kontroler jest znacznie czystszy. –

+0

@DarrenDavies Więc nawet ModelState.IsValid należy umieścić w modelu domeny? –

+1

Nie, 'ModelState.IsValid' jest materiałem MVC, umieściłbym część warunkową' if' w domenie zwracającej model lub wyrzucając wyjątek. –

Odpowiedz

10

Umieszczamy naszą aplikację i logikę biznesową na oddzielnych warstwach (plik csproj), warstwę domeny dla logiki biznesowej i warstwę usług dla logiki aplikacji. To całkowicie usuwa je z projektu MVC. Ma to dla nas dwie duże korzyści. Po pierwsze, logika biznesowa nie jest powiązana z wzorcem, który mógłby się zmienić. Kilka lat temu nikt z nas nie wyobrażał sobie dziś popularności MVC, a za kilka lat nie wiemy, czy pojawią się nowe rzeczy, które pojawią się i zastąpią MVC, tak aby uzyskać znaczną większość kodu "niezwiązany" z MVC pomógłby, gdybyś kiedykolwiek chciał porzucić MVC na coś innego.

Drugą zaletą jest to, że bardzo różne warstwy prezentacji są łatwe do wdrożenia. Więc jeśli chcesz przedstawić swoją logikę biznesową jako usługę WCF, możesz to zrobić bardzo łatwo, tworząc nowy projekt WCF i tworząc tę ​​fasadę dla twoich Usług i warstw domen. Ułatwia to konserwację, ponieważ zarówno projekt MVC, jak i usługa WCF będą korzystać z tych samych klas Business Logic.

Przykład Poniżej znajduje się przykładowy kod tego, co zrobiłbym. Jest to szybki i brudny i nie powinno być więcej, jak dodawanie rejestrowanie jeśli użytkownik nie zapisać z powrotem do bazy danych itp ...

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

Twój kontroler będzie teraz wyglądać tak

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

A gdybyś musiał ujawnić zapłatę w usłudze sieciowej (pracuję w środowisku korporacyjnym, więc to dużo dla mnie) Wykonujesz następujące czynności ...

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

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

Ponownie, więcej mówić o umieszczaniu logiki biznesowej w warstwie usługi ??? Logika biznesowa należy do domeny? Czy masz na myśli z punktu widzenia MVC, że jest w warstwie serwisowej, ponieważ to wszystko widzi MVC? mając na uwadze, że w rzeczywistości jest on poniżej warstwy usług w domenie? –

-2

Zalecane jest stosowanie cienkiego kodu na kontrolerach, lepiej radzić sobie z logiką biznesową na innych warstwach, takich jak usługa ServiceLayer, z której korzystałem, a następnie zwraca model widoku, który chciałeś przywrócić do widoku. /kontroler. Nawet zdefiniować metody ajax wewnątrz warstwa usługa class.Which złożoności kodu spadek i konserwacji problems.Even jest ona bardziej czytelna ..

W ty Controller można użyć DI wstrzyknąć klasę serviceLayer lub instansiate go jako

ServiceLayer test = new ServiceLayer() ; 

potem ty kontrolera

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

Logika biznesowa powinna znajdować się w domenie. Proszę przestać mylić ludzi, mówiąc, że powinien znajdować się w warstwie serwisowej. –

+0

dlaczego jest w domenie, nie rozumiem, proszę, więc mówicie, że w ogóle nie potrzebujemy usługi ServiceLayer? – danielad

+1

Domena jest modelem firmy, jej klasy reprezentują podmioty w biznesie, a kod w nich reprezentuje logikę biznesową, w przeciwnym razie kończy się to anemicznym wzorcem domenowym. –

0

podejścia, które kierujemy wymagało przypadków biznesowych zamknięte w ViewModel (twoja sprawa: PayoutViewModel) i wystawiony przez metody i te metody będą zużywane w akcie kontrolera jony. Ponadto mamy wyraźny podział na model i model widoku, w którym model widoku viewmodel w nim.

2

chciałbym umieścić wszystkie logikę w modelu domeny, a nie dwa połączenia do domeny, po jednym dla walidacji, jeden dla realizacji przypadku użycia.

Więc podmiot wygląda następująco:

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

i kontroler będzie wyglądać następująco:

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

Są inne rzeczy, które robię, jak wstawić warstwę aplikacji/usług, wykorzystanie viewModels i wykonaj wszystkie resetowanie ViewModel w ViewModelBuilder/Mapper lub podobnym, ale to pokazuje podstawowy pomysł.

5

Dla mnie oddzielenie obaw jest najważniejszą zasadą przewodnią dla tych decyzji. Zależy to od stopnia złożoności domeny i korzyści z komplikowania kodu.

W każdym razie, zgodnie z ogólną zasadą, staram się dać kontrolerów następujące zastrzeżenia:

  1. instancji i mapowanie widzenia modeli (chyba, że ​​istnieje znaczna mapping)
  2. widzenia walidacji modelu

I, mam tendencję do odwoływania się do modelu (lub usługi) dla wiedzy o dziedzinie nieaplikacyjnej:

  1. można wypłacić pieniądze
  2. Dokonaj wycofanie

Tak, to jak bym podzielić kod:

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

Oszczędność stanu aplikacji, zwykle zawinąć w przechwytujących. W ten sposób możesz owinąć wokół siebie całe zapytanie unit of work transaction.

+0

Podobny do mojego podejścia, ale muszę przyznać, myślę, że lepiej jest mieć metodę sprawdzania poprawności, w przeciwnym razie, ponieważ inne restrykcje dotyczą dokonywania wypłat, możesz skończyć z mnóstwem instrukcji if w twoim kontrolerze. –

+0

@DavinTryon Masz więc takie metody, jak CanWithMoney itd. W modelu konta? Chciałbym oddzielić podmioty od rzeczywistej logiki biznesowej, więc może powinienem dołączyć do nich za pomocą częściowego? –

+0

@JohnMayer Tak w elemencie domeny konta (model). Dlaczego chcesz oddzielić jednostki i logikę biznesową? Jest to wzorzec anty-anemiczny zwany "anemiczną" domeną. Jednostki modelu domeny powinny zawierać zarówno dane, jak i zachowanie. –

Powiązane problemy