ݺߣ

ݺߣShare a Scribd company logo
Референсная архитектура
приложения на ASP.NET MVC

        Андрей Майоров
            BYTE-force
        twitter.com/xorets
MVC
Model-View-Controller
• Разделяет данные и представление
• Модель: активная или пассивная


                  Controller




         View                  Model
Holy war
• Где держать бизнес-логику?
• Можно брать доменные объекты в модель?
Модификации: MVP
• Model-View-Presenter


                  Presenter




           View               Model
Модификации: MVVM
• Model-View-ViewModel



      View        ViewModel   Model
ASP.NET
Олдскульный .ASPX
• <% … %>
• Code behind
• Web controls

• Раздутый HTML на выходе
Олдскульный .ASPX

// Hello World in C#
class HelloWorld
{
       static void Main()
       {
              System.Console.WriteLine("Hello, World!");
       }
}
ASP.NET MVC
Контроллер

public class HomeController : Controller
{
       public ActionResult Index()
       {
              var model = …
              return View(model); // of type ViewResult
       }
}
Маршруты

routes.MapRoute(
      name: "Default",
      url: "{controller}/{action}/{id}",
      defaults: new {
              controller = "Home",
              action = "Index",
              id = UrlParameter.Optional
      } );


/home/index
Передача параметров

public ActionResult Index( int id, string x )
{ …}



routes.MapRoute(
      url: "{controller}/{action}/{id}",
      …



/home/index/123?x=red
View
• «Представление», «вьюха»
• Модель → HTML
• Отделено от контроллера
Реализация вьюхи
• Файл /view/home/index.*
• Расширения файлов:
  • ASPX → Web Forms Engine
  • CHTML → Razor
  • ... → Что душа пожелает
Demo
Шаблонный проект
Проблема: 1 метод на 1 URL
•   Огромные контроллеры
•   Нечеткие критерии группировки методов
•   Мало базовых классов
•   Сервисные методы раздувают
    контроллеры
Решение:
Только один action в
   контроллере
Один action на контроллер
• Execute()
• Исключение из правил – NextPage ()
Single responsibility principle


Kаждый объект должен иметь одну
обязанность и эта обязанность должна
быть полностью инкапсулирована в класс.




SOLID
Сложности с именами view

/View
        /Home
              /Execute.chtml
        /News
              /Execute.chtml
        /Products
              /Execute.chtml
              /NextPage.chtml
        /Product
              /Execute.chtml
Свой ViewEngine

/View
        /Home.xslt
        /News.xslt
        /Products.xslt
        /Products_NextPage.xslt
        /Product.xslt
Преимущества
•   Один контроллер – одно действие
•   Контроллеры маленькие
•   Общий функционал → в базовый класс
•   Создаем DSL*
•   Дружит с Dependency Injection*
ControllerBase
                  +Model
                  +FillCommonData()




IndexController                       ListControllerBase

+Execute()




                    NewsController                     ProductsController

                    +Execute()                         +Execute()
Правила
1. Один контроллер – одно действие
Модель
Модель
• Данные для View
• НЕ интерфейс получения/записи данных*




* — холивар варнинг
ViewModel
      vs.
Domain Objects
Типизированная модель
         vs.
    Динамическая
Typed vs. Dynamic

public ActionResult Execute()
{
       var model = new Model();
       …
       return View(model);
}



      dynamic model = new ExpandoObject();
Модель – член класса
Правила
1. Один контроллер – одно действие
2. Модель – член класса
Декларация маршрутов
Явная декларация маршрута

routes.MapRoute( name: "Home", url: "home",
      new { controller = "Home", action = "Execute" });

routes.MapRoute( name: "News", url: "news",
      new { controller = "News", action = "Execute" });




Url.RouteUrl( home ) // Returns “/home”
Правила
1. Один контроллер – одно действие
2. Модель – член класса
3. Задавайте маршруты явно
Demo
Single action controllers
Domain Specific Language
Domain Specific Language

public ActionResult Execute()
{
       CheckAccessRights( “index” );
       Model.news = SelectObjects()
             .OfType( “article” )
             .FilterPublished()
             .Execute();

      return View(Model);
}
ControllerBase
                  +Model
                  +FillCommonData()
                  +CheckAccessRights()
                  +SelectObjects()




IndexController                          ListControllerBase

+Execute()                               +PreparePagingData()




                     NewsController                        ProductsController

                     +Execute()                            +Execute()
Сервисные классы
                                  ControllerBase
                                +Model
                                +FillCommonData()
                                +CheckAccessRights()
                                +SelectObjects()




              IndexController                          ListControllerBase

              +Execute()                               +PreparePagingData()




NewsService

+GetNews()                         NewsController                        ProductsController

                                   +Execute()                            +Execute()
Demo
DSL and helpers
Правила
1.   Один контроллер – одно действие
2.   Модель – член класса
3.   Задавайте маршруты явно
4.   Делайте собственные DSL
Dependency Injection
Dependency inversion principle
Модули верхних уровней не должны
зависеть от модулей нижних уровней.
Модули должны зависеть от абстракций.

Абстракции не должны зависеть от
деталей. Детали должны зависеть от
абстракций.


SOLID
Dependency Inversion

 <<interface>>
 INewsService

 +GetNews()




 NewsServiceImpl   IndexController

 +GetNews()        +Execute()
Dependency Injection
<<interface>>
INewsService

+GetNews()




 NewsServiceImpl               IndexController

 +GetNews()                    +Execute()




                   Assembler

                +Resolve()
Указание зависимостей
• Параметры конструктора
• Свойства с атрибутами
• Правила именования полей и аргументов
Указание зависимостей

public class HomeController : Controller
{
       [Dependency]
       public INewsService NewsService { get; set; }

      public HomeController( IDatabase database ) { … }
      …
}
Настройка DI-контейнера
 • Связь между интерфейсом и реализацией*
 • Контроль за жизнью объекта

<register type="INewsService"
           mapTo="NewsService">
       <lifetime type="singleton"/>
</register>

 * Не обязательно
Demo
Dependency Injection
DI delivers
• Уменьшает связность приложения
• Помогает писать модульные тесты
• Заставляет правильно мыслить
Правила
1.   Один контроллер – одно действие
2.   Модель – член класса
3.   Задавайте маршруты явно
4.   Делайте собственные DSL
5.   Используйте DI
Редактирование
    данных
Обычный процесс
1.   Браузер запрашивает URL
2.   Сервер формирует HTML, шлет на клиент
3.   Браузер показывает HTML
4.   Пользователь редактирует данные. Submit
5.   Браузер шлет данные в POST-запросе
6.   Сервер обновляет базу, goto 2
Проблемы обычного подхода
• Сериализация-десериализация состояния
• Постоянные перезагрузки
• Трафик
Референсная архитектура приложения на ASP.NET MVC
Ajax-редактирование
1.   Браузер запрашивает URL
2.   Сервер формирует HTML, шлет на клиент
3.   Браузер показывает HTML
4.   Пользователь редактирует данные. Submit
5.   Браузер шлет данные в POST-запросе
6.   Сервер обновляет базу
7.   goto 4
JSON

// Браузер шлет серверу
{
      id: 245,
      price: 19.95
}



// Сервер отвечает
{ success: true }
Ajax delivers
• Не надо сериализовать всю страницу
• При ошибке можно повторить
• Трафик меньше на порядки
Правила
1.   Один контроллер – одно действие
2.   Модель – член класса
3.   Задавайте маршруты явно
4.   Делайте собственные DSL
5.   Используйте DI
6.   Используйте Ajax для редактирования
Референсная архитектура приложения на ASP.NET MVC
AngularJS - MVVM

// Model
$scope.id = 245;
$scope.title = “Red button”;
$scope.price = 19.95;



<!-- View -->
Price for {}: <input type=“text” ng-model=“price” />
Спасибо за внимание


   Андрей Майоров
       BYTE-force
  xor@byte-force.com
   twitter.com/xorets

More Related Content

Референсная архитектура приложения на ASP.NET MVC