Przejdź do treści

DevStyle - Strona Główna
Trochę inna organizacja kodu w ASP.NET MVC

Trochę inna organizacja kodu w ASP.NET MVC

Maciej Aniserowicz

30 czerwca 2011

Backend

ASP MVC 3 jest w dużej części spoko – znajdą się elementy bardzo irytujące, ale ogólnie mogę powiedzieć że jestem z pracy z tym frameworkiem raczej zadowolony. Denerwuje mnie jednak to, że pracując nad jedną daną akcją w jakimś kontrolerze muszę śmigać po kilku plikach:

  • plik kontrolera
  • plik z routingiem
  • plik z modelem parametru akcji
  • plik z modelem zwracanym przez akcję
  • plik z mapowaniami AutoMappera
  • plik widoku .cshtml
  • plik skryptów .js
  • … o czymś zapomniałem?

Jakiś czas temu postanowiłem wypróbować alternatywne podejście do organizacji kodu w swoim projekcie webowym…

[Przyszło mi ono do głowy zimą podczas przeglądania materiałów dotyczących frameworka

FubuMVC

, ale właściwie momentalnie zarzuciłem zapoznawanie się z tym rozwiązaniem, więc nie jestem w stanie na dzień dzisiejszy powiedzieć, jak poniższe podejście i praktyki Fubu “

ze sobą korespondują

“;)… co z kolei teraz staram się nadrabiać, eksperymentując ponownie z Fubu]

Najlepiej zobrazuje to przykład, żeby było wiadomo o co mi CHO. Poniżej przedstawiam kawałek kodu odpowiedzialny za akcję “administrator dodaje notatkę do firmy”:

  1:  public partial class CompaniesManagementController
  2:  {
  3:      [HttpPost]
  4:      public virtual ActionResult SaveNote(SaveNoteModel model)
  5:      {
  6:          var company = _session.Load<Company>(model.CompanyId);
  7:  
  8:          Note newNote = model.Map<Note>();
  9:  
 10:          company.AddNote(newNote);
 11:  
 12:          return Json(new SaveNoteResponse()
 13:                          {
 14:                              SavedSuccessfully = true,
 15:                              Message = MessagesContent.CompanyNote_SaveSuccess
 16:                          });
 17:      }
 18:  
 19:      public class SaveNoteModel
 20:      {
 21:          public int CompanyId { get; set; }
 22:  
 23:          [Required]
 24:          [DisplayName("Notatka")]
 25:          public string Content { get; set; }
 26:      }
 27:  
 28:      public class SaveNoteResponse
 29:      {
 30:          public bool SavedSuccessfully { get; set; }
 31:          public string Message { get; set; }
 32:      }
 33:  
 34:      public class Routes : IRouteProvider
 35:      {
 36:          public void RegisterRoutes(RouteCollection routes)
 37:          {
 38:              routes.MapRoute(
 39:                  "Admin_SaveNote",
 40:                  "Admin/ZapiszNotatke",
 41:                  MVC.Administration.CompaniesManagement.SaveNote()
 42:              );
 43:          }
 44:      }
 45:  
 46:      public class Mappings : IMappingProvider
 47:      {
 48:          public void RegisterMap()
 49:          {
 50:              AutoMapper.Mapper.CreateMap<SaveNoteModel, Note>();
 51:          }
 52:      }
 53:  }

Cóż tu widzimy? Jeden plik zawierający prawie wszystko, co jest potrzebne do oprogramowania jednej akcji. Kontroler jest partial – bo w innych plikach (które nazywam CompaniesManagement_SaveNote.cs, CompaniesManagement_DeleteCompany.cs etc…) piszę inne akcje, będące częściami tego samego kontrolera. Modele są zagnieżdżone – więc nigdy nie nastąpi żaden konflikt nazw z innymi mikroskopijnymi klaskami rozsianymi po projekcie. Tak samo definicje routingu oraz mapowań – każda akcja sama sobie definiuje pod jakim URLem chce się znajdować i w jaki sposób skorzystać z AutoMappera.

Dostrzegłem bowiem, że tak naprawdę NIC mi nie daje trzymanie modeli w jednym katalogu – bo nigdy nie potrzebuję dostępu do więcej niż jednego naraz. Tylko powoduje bałagan i zamieszanie podczas nawigacji. To samo z mapowaniami – i tak każda akcja ma własny model (lub dwa), więc i mapowania są wyłącznie dla niej charakterystyczne. A routing… No tutaj wiele już zależy od specyfiki danego systemu, ale generalnie podoba mi się swoboda, jaką daje przedstawione rozwiązanie: hierarchia URLi nie musi wcale pokrywać się z organizacją kontrolerów w “aree” oraz grupowanie akcji w kontrolery.

Wszystkie zależności wymagane przez wszystkie akcje kontrolera znajdują się w jednym konstruktorze, zdefiniowanym w głównym pliku CompaniesManagement.cs, który nie zawiera żadnych akcji. Zadaniem tego pliku jest przyjęcie zależności, dziedziczenie z odpowiedniej klasy bazowej oraz nałożenie atrybutów wspólnych dla wszystkich akcji (jak [Authorize]).

Okazało się, że taka organizacja kodu w projekcie webowym znacznie ułatwia mi pracę. Początkowo podchodziłem do tego bardzo sceptycznie, ale… po jakimś czasie wszystkie nowe funkcjonalności powstawały właśnie w ten sposób.

Zastanawiałem się czy nie pójść o krok dalej i nie “scustomizować” zachowania MVC tak, aby jeszcze bardziej wyeksponować akcję jako główny element rozwiązania webowego. Chodzi o to, aby każda akcja otrzymała osobny katalog, a w nim zarówno przedstawiony wyżej kod C#, jak i widok cshtml. Ale tego jeszcze nie wypróbowałem.

Co o tym myślicie? Tak jak napisałem – w moim przypadku takie coś naprawdę się sprawdza. Pliczki z akcjami są niewielkie i łatwo się po nich poruszać, a mają wszystko co potrzeba.

 

P.S.: Proszę po powyższym kodzie zbytnio nie jechać – ma on służyć jedynie demonstracji mojej koncepcji i nie jest żywcem wzięty z żadnego systemu.

Zobacz również