Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.
ASP.NET MVC за
пределами Hello World
1
Дятлов Александр
О себе
Веб-разработка около 4х лет
ASP.NET MVC более 3х лет
Благодаря ASP.NET влюбился в платформу .NET
Люблю выпить и пог...
О чем доклад?
Архитектура MVC
Организация бизнес логики приложения
Повторное использование кода
Немного о модульном тестир...
История паттерна MVC
Трюгве Миккель Хейердал Реенскауг
1979
Smalltalk
Xerox
4
Пассивная модель
5
Controller
View Model
Активная модель
6
Controller
View Model
Первые шаги в изучении
Мануалы
Сайт ASP.NET
Habrahabr Tutorials
...
Учебники по ASP.NET MVC
Адам Фримен
Джеффри Палермо
.....
8
Проблемы понимания
Последствия
1. Бизнес-логика в контроллерах
9
public ActionResult GetByTagAndDate( string tagName, DateTime date,
string authorName)
{
var posts = _dbContext.Posts
.Whe...
public ActionResult GetByTagAndDate(string tagName, DateTime date, string authorName) {
...
bool troll = (bool)Session["Is...
12 public class PostsV1Controller : Controller {
13 public ActionResult GetByTagAndDate(string tagName,
DateTime date, str...
Последствия
1. Бизнес-логика в контроллерах
2. Использование динамических объектов для передачи данных
представлению
13
public ActionResult GetByTagAndDate( string tagName, DateTime date,
string authorName)
{
...
ViewBag.IsTroll = true;
retur...
Проверка на стороне представления
@{
var troll = false;
if (ViewBag.IsTroll != null)
troll = (bool)ViewBag.IsTroll;
}
@if ...
Последствия
1. Бизнес-логика в контроллерах
2. Использование динамической типизации для передачи данных
представлению
3. Н...
public class User_Entity {
[Required]
[StringLength(100)]
[Remote("CheckUserName", "Account")]
public string Name { get; s...
public class User_Entity {
[Required]
[StringLength(100)]
[Remote("CheckUserName", "Account")]
public string Name { get; s...
Последствия
1. Бизнес-логика в контроллерах
2. Использование динамической типизации для передачи данных
представлению
3. Н...
20
User_Entity
[Required]
[StringLength(100)]
[Remote("CheckUserName", "Account")]
public string Name { get; set; }
[Requi...
21
User_Entity
[Required]
[StringLength(100)]
[Remote("CheckUserName", "Account")]
public string Name { get; set; }
[Requi...
22
User_Entity
[Required]
[StringLength(100)]
[Remote("CheckUserName", "Account")]
public string Name { get; set; }
[Requi...
23
User_Entity
[Required]
[StringLength(100)]
[Remote("CheckUserName", "Account")]
public string Name { get; set; }
[Requi...
Взаимодействия компонентов
24
Контроллер Модель
Представление
Хранение
(обычно в
реляционной
базе данных)
Запрос HTTP
Отве...
Взаимодействия компонентов
25
Контроллер Модель
Представление
Хранение
(обычно в
реляционной
базе данных)
Запрос HTTP
Отве...
Обязанности контроллера
26
Cache
Session
Cookies
Controller
Обязанности контроллера
27
Сервисы
Запросы
...
Команды
User.Identity.Name
...
Controller
28
Тестирование контроллеров
public ActionResult GetByTagAndDate( string tagName, DateTime date,
string authorName)
{
...
bool troll = (bool)Session["I...
Если необходимость в тестировании
контроллеров осталась?
30
public ActionResult GetByTagAndDate(string tagName, DateTime date, string authorName,
Troll troll)
{
...
if (troll)
...
re...
public class TrollModelBinder : IModelBinder {
public object BindModel(ControllerContext controllerContext,
ModelBindingCo...
Регистрация
protected void Application_Start()
{
...
ModelBinders.Binders.Add( typeof(Troll), new TrollModelBinder());
Bun...
Взаимодействия компонентов
34
Контроллер Модель
Представление
Хранение
(обычно в
реляционной
базе данных)
Запрос HTTP
Отве...
Модель / Виды
35
АМ
~80%
БМ
~20%
Личного опыт
АМ / БМ
Анемичная модель (бедная модель)
Сущности представляют данные
Сущности это плоские классы
Богатая модель
Единый яз...
Анемичная модель
37
CRUD_Service<T>
+Create()
+Read()
+Update()
+Delete()
Post
Topic
Tag
...
38
Единый механизм
В реальном мире
Интернет-блог
Каталог товаров
Сайты галереи
Сайты портфолио
Сайты визитки
и т.д.
39
В реальном мире
Интернет-блог
Каталог товаров
Сайты галереи
Сайты портфолио
Сайты визитки
и т.д.
40
Богатая модель / Кредит
41
Сredit_Entity
int DaysCount
float InterestRate
decimal Sum
decimal Payment()
Богатая модель / Грузоперевозки
42
Shipping_Entity
float CargoTotal
float CargoCurrent
int OverflowRate
bool Add(float car...
В реальном мире
Автоматизация банковских систем
Автоматизация транспортной логистики
Экспертные системы
и т.д.
43
В реальном мире
Автоматизация банковских систем
Автоматизация транспортной логистики
Экспертные системы
и т.д.
44
Доступ к данным
45
DAL / Рекомендации
Persistence Ignorance
Сущности не должны зависеть от способа хранения данных
Использование паттерна Rep...
Repository
47
CRUD_Repository<T>
+Create()
+Read()
+Update()
+Delete()
Post
Topic
...
CRUD_Service<T>
Troll_Detector
+ Det...
DAL / Рекомендации
Persistence Ignorance
Сущности не должны зависеть от способа хранения данных
Использование паттерна Rep...
Взаимодействия компонентов
49
Контроллер Модель
Представление
Хранение
(обычно в
реляционной
базе данных)
Запрос HTTP
Отве...
public class PostsGetByTagAndDateViewModel {
public IEnumerable<Post> Posts { get; set; }
public bool IsTroll { get; set; ...
Заполнение модели
public ActionResult GetByTagAndDate(string tagName, DateTime date,
string authorName, TrollState trollSt...
public class RegistrationViewModel {
[Required]
[StringLength(100)]
[Remote("CheckUserName", "Account")]
public string Nam...
Классы помощники
public static class EmailHelpers {
public static IHtmlString Email(this HtmlHelper html, string address,
...
Классы помощники
@helper Email(string address, string name = null)
{
<a href="mailto:@(address)">@(name ?? address) </a>
}...
Классы помощники
@helper Email(string address, string name = null)
{
<a href="mailto:@(address)">@(name ?? address) </a>
}...
JavaScript код в представлениях
56
@model IEnumerable<Post>
@{
Layout = "~/Areas/Admin/Views/Shared/_Layout.cshtml";
var ajaxSave = new AjaxOptions {
Url = U...
58
Вынос JS кода из представлений
App.views.Posts.Troll = ( function () {
// private scope
return {
init: function () {
//...
59
Вызов в представлении
PostsTroll.js
App.views.Posts.Troll = (function () {
// private scope
return {
init: function () ...
Проблема HTTP 1.0 / 1.1
Дополнительные расходы на установку
соединения для каждого запроса
Конвейерная передача HTTP
Парал...
Проблема HTTP 1.0 / 1.1
Дополнительные расходы на установку
соединения для каждого запроса
Конвейерная передача HTTP
Парал...
Минификация
Объединение JS в один
файл при помощи bundle
62
public class BundleConfig {
public static void RegisterBundles...
Минификация
Объединение JS в один
файл при помощи bundle
Использование
сторонних сборщиков
gulp
grunt
...
63
public class ...
Собрать части в целое
64
Архитектура современного веб-приложения
65
Веб
User Interface
Технические сервисы
Technical
Services
Зависимости
Вспомогат...
Что делать если?
66
Веб
User Interface
Технические сервисы
Technical
Services
Зависимости
Болееспецифическаялогика
Вспомог...
Инверсия управления (IoC)
67
Веб
User Interface
Технические сервисы
Technical
Services
IPayService
Зависимости
Болееспециф...
Инверсия управления (IoC)
68
Веб
User Interface
Технические сервисы
Technical
Services
IPayService
PayService
IPayService
...
Регистрация зависимостей
public class DIConfig : Module {
protected override void Load(ContainerBuilder builder) {
builder...
Что делать если что-то пошло не так?
70
Иерархия исключений .NET
71
Exception
ApplicationExceptionSystemException
Но не бывает так легко
72
Решение
73
DemoException
ApplicationException
Решение
74
DemoException
LogicExceptionFatalException
ApplicationException
Обрабатываем конкретные типы
75
try {
// Логика подтверждения оплаты
}
catch (PaymentNotVerifiedLogicException ex)
{
Payme...
Полезные материалы
http://www.asp.net
http://sergeyteplyakov.blogspot.ru
http://metanit.com/sharp/mvc5/23.1.php
http://pro...
Спасибо за внимание!
77
dyatlovall@gmail.com
vk.com/dspro
Upcoming SlideShare
Loading in …5
×

ASP.NET MVC за пределами Hello World. Дятлов Александр D2D Just.NET

687 views

Published on

ASP.NET MVC простой и распространённый инструмент. Но строить на его основе большое веб-приложение не так просто. Туториалы не раскрывают проблем возникающих при росте проекта. Зачастую, изначально стройная архитектура размазывается с каждой следующей итерацией.

Я хочу поделиться своим опытом. Рассказать об основных проблемах и предложить выбранные мной решения.

Published in: Technology
  • Be the first to comment

  • Be the first to like this

ASP.NET MVC за пределами Hello World. Дятлов Александр D2D Just.NET

  1. 1. ASP.NET MVC за пределами Hello World 1 Дятлов Александр
  2. 2. О себе Веб-разработка около 4х лет ASP.NET MVC более 3х лет Благодаря ASP.NET влюбился в платформу .NET Люблю выпить и поговорить про архитектуру :) 2
  3. 3. О чем доклад? Архитектура MVC Организация бизнес логики приложения Повторное использование кода Немного о модульном тестировании Обработка исключительных ситуаций 3
  4. 4. История паттерна MVC Трюгве Миккель Хейердал Реенскауг 1979 Smalltalk Xerox 4
  5. 5. Пассивная модель 5 Controller View Model
  6. 6. Активная модель 6 Controller View Model
  7. 7. Первые шаги в изучении Мануалы Сайт ASP.NET Habrahabr Tutorials ... Учебники по ASP.NET MVC Адам Фримен Джеффри Палермо ... 7
  8. 8. 8 Проблемы понимания
  9. 9. Последствия 1. Бизнес-логика в контроллерах 9
  10. 10. public ActionResult GetByTagAndDate( string tagName, DateTime date, string authorName) { var posts = _dbContext.Posts .Where(p => p. Tags.Any(t => t.Name == tagName) && p.CreateOn < date && p.Author.Name == authorName); if (!posts.Any()) return HttpNotFound(); return View(posts); } 10
  11. 11. public ActionResult GetByTagAndDate(string tagName, DateTime date, string authorName) { ... bool troll = (bool)Session["IsTroll"]; if (troll || _dbContext.Posts.Where(p => p.Author.Name == User.Identity.Name) .SelectMany(p => p.Marks).Count(m => m.IsLike == true) > _dbContext.Marks.Count(m => m.Author.Name == User.Identity.Name && m.IsLike == false)) ViewBag.IsTroll = true; ... 11 И еще немножко бизнес-логики
  12. 12. 12 public class PostsV1Controller : Controller { 13 public ActionResult GetByTagAndDate(string tagName, DateTime date, string authorName) { … } 40 public ActionResult GetPostByTitle(string title) { … } 90 public ActionResult GetAll() { … } 1090 public ActionResult History() { … } } 12 И начинается АД
  13. 13. Последствия 1. Бизнес-логика в контроллерах 2. Использование динамических объектов для передачи данных представлению 13
  14. 14. public ActionResult GetByTagAndDate( string tagName, DateTime date, string authorName) { ... ViewBag.IsTroll = true; return View(posts); } 14 Потенциальное падение во время исполнения
  15. 15. Проверка на стороне представления @{ var troll = false; if (ViewBag.IsTroll != null) troll = (bool)ViewBag.IsTroll; } @if (troll) { <p>Ты мерзкий тролль!!!</p> } 15
  16. 16. Последствия 1. Бизнес-логика в контроллерах 2. Использование динамической типизации для передачи данных представлению 3. Навешивание на бизнес-сущность атрибутов валидации 16
  17. 17. public class User_Entity { [Required] [StringLength(100)] [Remote("CheckUserName", "Account")] public string Name { get; set; } [Required] [SecurePassword] public string Password { get; set; } } 17
  18. 18. public class User_Entity { [Required] [StringLength(100)] [Remote("CheckUserName", "Account")] public string Name { get; set; } [Required] [SecurePassword] public string Password { get; set; } } 18
  19. 19. Последствия 1. Бизнес-логика в контроллерах 2. Использование динамической типизации для передачи данных представлению 3. Навешивание на бизнес-сущность атрибутов валидации 4. Использование одной модели для отображения и получения данных 19
  20. 20. 20 User_Entity [Required] [StringLength(100)] [Remote("CheckUserName", "Account")] public string Name { get; set; } [Required] [SecurePassword] public string Password { get; set; } Список пользователей
  21. 21. 21 User_Entity [Required] [StringLength(100)] [Remote("CheckUserName", "Account")] public string Name { get; set; } [Required] [SecurePassword] public string Password { get; set; } Список пользователей Информация о пользователе
  22. 22. 22 User_Entity [Required] [StringLength(100)] [Remote("CheckUserName", "Account")] public string Name { get; set; } [Required] [SecurePassword] public string Password { get; set; } Список пользователей Информация о пользователе Сменить пароль
  23. 23. 23 User_Entity [Required] [StringLength(100)] [Remote("CheckUserName", "Account")] public string Name { get; set; } [Required] [SecurePassword] public string Password { get; set; } Список пользователей Информация о пользователе Сменить пароль Регистрация
  24. 24. Взаимодействия компонентов 24 Контроллер Модель Представление Хранение (обычно в реляционной базе данных) Запрос HTTP Ответ Модель представления
  25. 25. Взаимодействия компонентов 25 Контроллер Модель Представление Хранение (обычно в реляционной базе данных) Запрос HTTP Ответ Модель представления
  26. 26. Обязанности контроллера 26 Cache Session Cookies Controller
  27. 27. Обязанности контроллера 27 Сервисы Запросы ... Команды User.Identity.Name ... Controller
  28. 28. 28 Тестирование контроллеров
  29. 29. public ActionResult GetByTagAndDate( string tagName, DateTime date, string authorName) { ... bool troll = (bool)Session["IsTroll"]; ... return View(posts); } 29 Зависимость от сессии
  30. 30. Если необходимость в тестировании контроллеров осталась? 30
  31. 31. public ActionResult GetByTagAndDate(string tagName, DateTime date, string authorName, Troll troll) { ... if (troll) ... return View(posts); } 31 Получение через параметры
  32. 32. public class TrollModelBinder : IModelBinder { public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext) { var troll = (Troll)controllerContext.HttpContext.Session["IsTroll"]; if (troll == null) { troll = new Troll(); controllerContext.HttpContext.Session["IsTroll"] = troll; } return troll; } } 32 Кастомный биндинг
  33. 33. Регистрация protected void Application_Start() { ... ModelBinders.Binders.Add( typeof(Troll), new TrollModelBinder()); BundleTable.Bundles.EnableDefaultBundles(); ... } 33
  34. 34. Взаимодействия компонентов 34 Контроллер Модель Представление Хранение (обычно в реляционной базе данных) Запрос HTTP Ответ Модель представления
  35. 35. Модель / Виды 35 АМ ~80% БМ ~20% Личного опыт
  36. 36. АМ / БМ Анемичная модель (бедная модель) Сущности представляют данные Сущности это плоские классы Богатая модель Единый язык между разработчиком и специалистом Модель это дистиллированное знание 36
  37. 37. Анемичная модель 37 CRUD_Service<T> +Create() +Read() +Update() +Delete() Post Topic Tag ...
  38. 38. 38 Единый механизм
  39. 39. В реальном мире Интернет-блог Каталог товаров Сайты галереи Сайты портфолио Сайты визитки и т.д. 39
  40. 40. В реальном мире Интернет-блог Каталог товаров Сайты галереи Сайты портфолио Сайты визитки и т.д. 40
  41. 41. Богатая модель / Кредит 41 Сredit_Entity int DaysCount float InterestRate decimal Sum decimal Payment()
  42. 42. Богатая модель / Грузоперевозки 42 Shipping_Entity float CargoTotal float CargoCurrent int OverflowRate bool Add(float cargo) float FreePlace()
  43. 43. В реальном мире Автоматизация банковских систем Автоматизация транспортной логистики Экспертные системы и т.д. 43
  44. 44. В реальном мире Автоматизация банковских систем Автоматизация транспортной логистики Экспертные системы и т.д. 44
  45. 45. Доступ к данным 45
  46. 46. DAL / Рекомендации Persistence Ignorance Сущности не должны зависеть от способа хранения данных Использование паттерна Repository 46
  47. 47. Repository 47 CRUD_Repository<T> +Create() +Read() +Update() +Delete() Post Topic ... CRUD_Service<T> Troll_Detector + Detect()
  48. 48. DAL / Рекомендации Persistence Ignorance Сущности не должны зависеть от способа хранения данных Использование паттерна Repository Более гибкое решение: Использование CQRS 48
  49. 49. Взаимодействия компонентов 49 Контроллер Модель Представление Хранение (обычно в реляционной базе данных) Запрос HTTP Ответ Модель представления
  50. 50. public class PostsGetByTagAndDateViewModel { public IEnumerable<Post> Posts { get; set; } public bool IsTroll { get; set; } public PostsGetByTagAndDateViewModel (IEnumerable<Post> posts, bool isTroll) { Posts = posts; IsTroll = isTroll; } } 50 Модель представления
  51. 51. Заполнение модели public ActionResult GetByTagAndDate(string tagName, DateTime date, string authorName, TrollState trollState) { ... if (!posts.Any()) return HttpNotFound(); return View(new PostsGetByTagAndDateViewModel(posts, trollState.IsTroll)); } 51
  52. 52. public class RegistrationViewModel { [Required] [StringLength(100)] [Remote("CheckUserName", "Account")] public string Name { get; set; } [Required] [StringLength(100)] [SecurePassword] public string Password { get; set; } } 52
  53. 53. Классы помощники public static class EmailHelpers { public static IHtmlString Email(this HtmlHelper html, string address, string name = null) { return new HtmlString($"<a href="mailto: { address }"> { name ?? address } </a>"); } } <span>@Html.Email("google@gmail.com")</span> <span>@Html.Email("google@gmail.com", "Отправить письмо")</span> 53
  54. 54. Классы помощники @helper Email(string address, string name = null) { <a href="mailto:@(address)">@(name ?? address) </a> } <span>@Helpers.Email("google@gmail.com")</span> <span>@Helpers.Email("google@gmail.com", "Отправить письмо")</span> 54 App_CodeHelpers.cshtml
  55. 55. Классы помощники @helper Email(string address, string name = null) { <a href="mailto:@(address)">@(name ?? address) </a> } <span>@Helpers.Email("google@gmail.com")</span> <span>@Helpers.Email("google@gmail.com", "Отправить письмо")</span> 55 App_CodeHelpers.cshtml
  56. 56. JavaScript код в представлениях 56
  57. 57. @model IEnumerable<Post> @{ Layout = "~/Areas/Admin/Views/Shared/_Layout.cshtml"; var ajaxSave = new AjaxOptions { Url = Url.Action("Edit"), OnSuccess = "ajaxSuccess" }; } <table class="table-striped table-hover tablesorter table">...</table> <script type="text/javascript" src="@Url.Content("~/Scripts/table.js")"></script> <script type="text/javascript"> $(".tablesorter").tablesorter(); $("#TableId").change(function () { window.location.href = "/Posts/Edit/" + $("#PostId").val(); }); </script> 57
  58. 58. 58 Вынос JS кода из представлений App.views.Posts.Troll = ( function () { // private scope return { init: function () { // public API } } })(jQuery);
  59. 59. 59 Вызов в представлении PostsTroll.js App.views.Posts.Troll = (function () { // private scope return { init: function () { // public API } } })(jQuery); <div> </div> <script> App.views.Posts.Troll.Init(); </script>
  60. 60. Проблема HTTP 1.0 / 1.1 Дополнительные расходы на установку соединения для каждого запроса Конвейерная передача HTTP Параллельные HTTP соединения в современных браузерах от 4 до 8 60
  61. 61. Проблема HTTP 1.0 / 1.1 Дополнительные расходы на установку соединения для каждого запроса Конвейерная передача HTTP Параллельные HTTP соединения в современных браузерах от 4 до 8 61
  62. 62. Минификация Объединение JS в один файл при помощи bundle 62 public class BundleConfig { public static void RegisterBundles( BundleCollection bundles) { bundles.Add(new ScriptBundle("~/scripts/jquery") .Include("~/Scripts/jquery-{version}.js") .Include("~/Scripts/PostsTroll.js")); } }
  63. 63. Минификация Объединение JS в один файл при помощи bundle Использование сторонних сборщиков gulp grunt ... 63 public class BundleConfig { public static void RegisterBundles( BundleCollection bundles) { bundles.Add(new ScriptBundle("~/scripts/jquery") .Include("~/Scripts/jquery-{version}.js") .Include("~/Scripts/PostsTroll.js")); } }
  64. 64. Собрать части в целое 64
  65. 65. Архитектура современного веб-приложения 65 Веб User Interface Технические сервисы Technical Services Зависимости Вспомогательная логика Services Бизнес-логика (М) Models Domain
  66. 66. Что делать если? 66 Веб User Interface Технические сервисы Technical Services Зависимости Болееспецифическаялогика Вспомогательная логика Services Бизнес-логика (М) Models Domain
  67. 67. Инверсия управления (IoC) 67 Веб User Interface Технические сервисы Technical Services IPayService Зависимости Болееспецифическаялогика Вспомогательная логика Services Бизнес-логика (М) Models Domain
  68. 68. Инверсия управления (IoC) 68 Веб User Interface Технические сервисы Technical Services IPayService PayService IPayService Зависимости Болееспецифическаялогика Вспомогательная логика Services Бизнес-логика (М) Models Domain
  69. 69. Регистрация зависимостей public class DIConfig : Module { protected override void Load(ContainerBuilder builder) { builder.RegisterType< PayService>().As<IPayService>(); … base.Load(builder); } } 69
  70. 70. Что делать если что-то пошло не так? 70
  71. 71. Иерархия исключений .NET 71 Exception ApplicationExceptionSystemException
  72. 72. Но не бывает так легко 72
  73. 73. Решение 73 DemoException ApplicationException
  74. 74. Решение 74 DemoException LogicExceptionFatalException ApplicationException
  75. 75. Обрабатываем конкретные типы 75 try { // Логика подтверждения оплаты } catch (PaymentNotVerifiedLogicException ex) { PaymentVerified = false; _logger.Warning($"При оплате возникли проблемы { ex }"); }
  76. 76. Полезные материалы http://www.asp.net http://sergeyteplyakov.blogspot.ru http://metanit.com/sharp/mvc5/23.1.php http://professorweb.ru/my/ASP_NET/mvc/level1/1_7.php https://habrahabr.ru/post/73692/ http://andrey.moveax.ru/page/aspnet-mvc-3-in-depth 76
  77. 77. Спасибо за внимание! 77 dyatlovall@gmail.com vk.com/dspro

×