Опыт разработки сложных клиент-серверных приложений...

Post on 27-Jul-2015

357 views 3 download

Transcript of Опыт разработки сложных клиент-серверных приложений...

1

На примере текстового редактора ASPxRichEdit

Опыт разработки сложных

клиент-серверных приложений на

TypeScript и ASP.NET

Developer Express Inc.

Роман Решетников

Ведущий разработчик команды ASP.NET

2Go

# C

on

fere

nce

s

Продукты DevExpress - для множества платформ

3Go

# C

on

fere

nce

s

Продукты DevExpress - для всех устройств

4Go

# C

on

fere

nce

s

Продукты команды ASP.NET

5Go

# C

on

fere

nce

s

Офисные контролы ASP.NET - ASPxSpreadSheet

6Go

# C

on

fere

nce

s

ASPxRichEdit – процессор rich-text документов

Работа со всеми популярными rich-text форматами

(DOCX, DOC, RTF, TXT, MHT, ODT, EPUB и т.д.)

Нативная работа с rich-text документами без потери

содержимого

Поддержка стилей, настроек секций, форматирования

текста и параграфов

Печать, экспорт в PDF

Знакомый UI вдохновленный продуктами MS Office

7Go

# C

on

fere

nce

s

ASPxRichEdit – не очередной HTML редактор

Rich-Text Editor HTML Editor

Постраничная разбивка

Настраиваемые поля

Поддержка колонок

Header и Footer

Поддержка содержания

Стили документа

Форматирование текста

Поддержка параграфов

Принцип работы Полный контроль над

документом

Нативная браузерная

функциональность

Формат документа RTF, DOCX, DOC, ODT… HTML

8Go

# C

on

fere

nce

s

RichEdit для десктопных платформ: WinForms, WPF

30+MB исходного кода (базового для всех платформ)

9000+ тестов базового кода

Более 8 лет разработки

Язык разработки C#

9Go

# C

on

fere

nce

s

Выбираем инструменты TypeScript

Jasmine + Chutzpah

Перенос логики на

клиент

Синхронизация с

сервером

Integration тесты

Генерация кода

Асинхронность в JS

Сервис/хандлер вместо привычных

ASP.NET коллбеков

Синхронизируем реквесты, версионность

Тестируем синхронизацию

Как перенести код с сервера на клиент?

10

Инструменты

TypeScript – язык, позволяющий работать со сложной типизированной моделью

Тестирование клиентского кода

11Go

# C

on

fere

nce

s

TypeScript для .NET разработчика

Строгая типизация – необходимость для приложений со

сложными моделями

700+ классов в TS проекте

5000+ классов в .NET библиотеке

Компиляция в «чистый» .JS

Размер скомпиленного .js кода на 15% больше размера .ts.

Простая отладка js кода даже без .map файлов

Хорошая поддержка Visual Studio

Простой синтаксис подобный C#

public class CharacterFormattingInfo {

public string FontName { get; set; }

public int Size { get; set; }

public bool Bold { get; set; }

public bool Italic { get; set; }

public StrikeoutType StrikeoutType { get; set; }

public UnderlineType UnderlineType { get; set; }

public bool AllCaps { get; set; }

export class CharacterFormattingInfo {

fontName: string;

size: number;

bold: boolean;

italic: boolean;

strikeoutType: StrikeoutType;

underlineType: UnderlineType;

allCaps: boolean;

C# код

TypeScript код

12Go

# C

on

fere

nce

s

Но не все идеально в TypeScript

Отсутствует автоматическая линковка .ts файлов.

Компилятору надо подсказывать в каком порядке их

склеивать

Сложно выделить .ts файлы с тестами за пределы

основного TypeScript кода

TS классы не линкуются между проектами Visual Studio

Медленный и не всегда корректный анализ ошибок

компиляции в VS (на больших проектах)

Как вынести тесты в отдельный проект и не потерять связь

между типами:

<TypeScriptCompile Include="{Путь-до-базового-проекта}\**\*.ts"><Link>_referencesTS\%(RecursiveDir)%(FileName)</Link>

</TypeScriptCompile>

13Go

# C

on

fere

nce

s

Покрываем клиентский код тестами

Jasmine – удобный framework для BDD unit тестирования

Встроенный framework для быстрого создание mock функций

Простое тестирование timeout функций

«Читабельный» синтаксис

Совместим с TypeScript

Chutzpah – runner тестов в Visual Studio

Простой запуск тестов прямо в VS

Сам создает необходимое окружение для запуска теста в

браузере

Поддерживает тесты написанные на TypeScript и Jasmine

14

Перенос логики на клиент

Перенос серверных типов на клиент

Очереди процессов вместо асинхронности

15Go

# C

on

fere

nce

s

Быстрый отклик – когда все на клиенте

Клиент работает независимо от сервера. Сервер только

уведомляется о новом состоянии модели

Для продолжения работы с документом клиенту не нужен

ответ сервера

UI не блокируется на время request/response

Модель отправляется на клиент частями

Вся модель разбивается на небольшие части – chunks

Вместе с рендером страницы отправляется первый chunk

Остальные chunks отсылаются на клиент асинхронно

16Go

# C

on

fere

nce

s

Передача модели между сервером и клиентом

Сервер

Model→JSON

Response

JSON→Model

Клиент

Model→JSON

Request

JSON→Model

17Go

# C

on

fere

nce

s

TextTemplates для генерации JSON конвертеров

RichEdit.Core DLL

TextTemplate Engine

• Type FindType(string

typeName)

• PropertyInfo[]

GetProperties()

Converters

• .NET type to JSON

• TS type to .NET

public class ParagraphFormattingInfo {[JSONEnum(JSONParagraphFormattingProperty.Alignment)]public ParagraphAlignment Alignment { get; set; }

[JSONEnum(JSONParagraphFormattingProperty.BackColor)]public Color BackColor { get; set; }

[JSONEnum(JSONParagraphFormattingProperty.LeftIndent)]public int LeftIndent { get; set; }

public class ParagraphFormattingInfoExporter : IExporter<ParagraphFormattingInfo> { public void FillHashtable(Hashtable result, ParagraphFormattingInfo info) {

result.Add(0, (int)info.Alignment); result.Add(1, info.BackColor.ToArgb());result.Add(2, info.LeftIndent);

…public void RestoreInfo(Hashtable source, ParagraphFormattingInfo info) {

info.Alignment = (ParagraphAlignment)source[0];info.BackColor = Color.FromArgb(source[1]);info.LeftIndent = Convert.ToInt32(source[2]);

18Go

# C

on

fere

nce

s

Асинхронное выполнение длительных функций

Длительные операции выполняются «асинхронно»

Цикличные алгоритмы упаковываются в вызовы множества

«быстрых» примитивных функций

Быстрые функции вызываются поочередно из setTimeout

функции с нулевым интерваломrunFormating() {

if(this.timerID)return;

var asyncCalculating = () => {if(this.formatter.formatNext())

this.timerID = setTimeout(asyncCalculating, 0);else

this.timerID = null;};this.timerID = setTimeout(asyncCalculating, 0);

}

runFormatting() {while(this.formatter.formatNext()) { }

}

19

Синхронизация с сервером

Модель стейтов и реквестов

Синхронизация изменений от нескольких пользователей

20Go

# C

on

fere

nce

s

Сессионная модель сервера

Реквесты делаются к HttpHandler, но не к странице

Инстанс пользовательской сессии хранится в статичном

Dictionary. Ключ отсылается на клиент

Сессия идентифицирует конкретную модель (документ), а

не пользователя

Пользователя идентифицирует его ClientGuid (создается

на открытии страницы)

Несколько человек могут открыть один и тот же документ

и работать с одной сессией

Клиент

• Тело реквеста

• ID рабочей сессии

• ID клиента

HttpHandler• Поиск рабочей сессии по ID

• Передача ей ID клиента и

тела реквеста

Сессия

• Обработка реквеста

• Формирование

ответа

21Go

# C

on

fere

nce

s

Очередь реквестов

Реквесты на сервер не блокируют UI контрола

Формирование очереди реквестов

Сформированные реквесты добавляются очередь

Очередь накапливается в течение N секунд

Защита от коллизий

Один и тот же реквест может быть отправлен несколько раз,

пока сервер не подтвердит его принятие

Каждый реквест помечен своим идентификатором (версией

документа)

Сервер не выполнит реквест с одним и тем же ID дважды

22Go

# C

on

fere

nce

s

Путь к collaboration

Две роли клиентов – Editor и Viewer

Роли клиентов распределяет сервер

Editor – клиент, последний изменивший актуальную

модель

Viewer может стать редактором, если у него была

последняя версия модели и он ее поменял

Viewer с устаревшей моделью – менять ее не может

Рассинхронизация возможна только на коротком

интервале времени или при потере связи

23

Integration тестирование

Как протестировать корректную инициализацию клиента

Тестирование синхронизации клиента и сервера

PhantomJS

24Go

# C

on

fere

nce

s

Клиентская часть повторяет серверную

Server-side

Client-side

25Go

# C

on

fere

nce

s

Тестируем клиент и сервер одновременно

Инициализация стартового состояния

Тестирование применения изменений клиента на

сервере

Тестирование применения изменений состояния

серверной модели к клиентской модели

Тестирование возможных коллизий при синхронизации

состояний

26Go

# C

on

fere

nce

s

Цикл интеграционного тестирования

Серверный код теста

Assert полученного состояния

клиентской модели

Применение клиентского

JSON к серверной модели

Assert нового состояния

серверной модели

PhantomJS с клиентским инстансом

Чтение инициализационного

JSON

Выполнение клиентских

операций

Запись в console клиентского

состояния модели

Серверный код теста

Создание серверного инстанса Заполнение модели

27Go

# C

on

fere

nce

s

Пример интеграционного теста

[Test]

public void TestParagraphProperties() {

ChangeDocumentModel(); // предварительная настройка серверной модели

string[] clientResults = RunClientSide(

GetClientModelStateAction(), // записать Model в Output

ExecuteClientCommandAction() // выполнить клиентскую команду, записать реквест в Output

);

// clientResults[0] – JSON с клиентским состоянием модели (инициализация)

// clientResults[1] – JSON с реквестом на изменение серверной модели

AssertClientModelState(clientResults[0]);

ApplyClientRequestToServerModel(clientResults[1]);

AssertServerModelState(DocumentModel);

}

28Go

# C

on

fere

nce

s

Выбор инструментовСинхронизация и

интеграционное тестированиеМиграция кода на клиент

TypeScript – отличный

инструмент для сложных

клиентских проектов

Jasmine+Chutzpah –

покрывают все задачи по

тестированию клиентского

кода

PhantomJS – помогает

написать интеграционные

тесты

Для переноса серверных

типов на клиент можно

использовать TextTemplates

Сложные вычисления можно

разделить на атомарные

операции и выполнять

очередью из таймаутов

Строгая типизация TypeScript

позволяет довольно просто

портировать логику на клиент

Реквесты на сервер не

должны блокировать UI

Контрол не должен зависеть

от ответов сервера

Интеграционные тесты

позволяют протестировать

стартовую инициализацию

Применение клиентских

изменений на сервере также

покрывается тестами

ASPxRichEdit – «толстые» клиент и сервер

29Go

# C

on

fere

nce

s

Синхронизация моделей без коллизий

30Go

# C

on

fere

nce

s

Спасибо за внимание!

Продукты DevExpress можно попробовать на нашем

стенде и на http://devexpress.com

Ведущий разработчик команды ASP.NET

Решетников Роман

Developer Express Inc.

Roman.Reshetnikov@devexpress.com