С чего начать?

IncFramework-logo

disclamer: данная статья является пошаговым руководством, которое поможет ознакомиться с основными возможностями Incoding Framework. Результатом следования данному руководству будет приложение, реализующее работу с БД (CRUD + data filters) и полностью покрытое юнит-тестами.

Часть 0. Введение.

Для начала приведем краткое описание фреймворкаIncoding Framework состоит из трех пакетов: Incoding framework – back-end проекта, Incoding Meta Language – front-end проекта и Incoding tests helpers – юнит-тесты для back-end’а. Эти пакеты устанавливаются независимо друг от друга, что позволяет интегрировать фреймворк в проект частями: Вы можете подключить только клиентскую или только серверную часть (тесты очень сильно связаны с серверной частью, поэтому их можно позиционировать как дополнение).

В проектах, написанных на Incoding Framework, в качестве серверной архитектуры используется CQRS. В качестве основного инструмента построения клиентской части используется Incoding Meta Language. В целом Incoding Framework покрывает весь цикл разработки приложения.

Типичный solution, созданный с помощью Incoding Framework, имеет 3 проекта:

  1. Domain (class library) отвечает за бизнес-логику и работу с базой данных.
  2. UI (ASP.NET MVC project) – клиентская часть, основанная на ASP.NET MVC.
  3. UnitTests (class library– юнит-тесты для Domain.

Domain

После установки пакета Incoding framework через Nuget в проект помимо необходимых dll устанавливается файл Bootstrapper.cs. Основная задача этого файла – инициализация приложения: инициализация логирования, регистрация IoC, установка настроек Ajax-запросов и пр. В качестве IoC framework по умолчанию устанавливается StructureMap, однако есть провайдер для Ninject, а также есть возможность написания своих реализаций.

Далее в Domain дописываются команды (Command) и запросы (Query), которые выполняют операции с базой данных либо какие-то другие действия, связанные с бизнес-логикой приложения.

UI

Пакет Incoding Meta Language при установке добавляет в проект необходимые dll, а также файлы IncodingStart.cs и DispatcherController.cs (часть MVD) необходимые для работы с Domain.

После установки в UI дописывается клиентская логика с использованием IML.

UnitTests

При установке Incoding tests helpers в проект добавляется файл MSpecAssemblyContext.cs, в котором настраивается connection к тестовой базе данных.

Часть 1. Установка.

Итак, приступим к выполнению поставленной в disclamer задаче – начнем писать наше приложение. Первый этап создания приложения – создание структуры solution’а проекта и добавление projects в него. Solution проекта будет называться Example и, как уже было сказано во введении, будет иметь три projects. Начнем с project’а, который будет отвечать за бизнес-логику приложения – с Domain.

Создаем class library Domain.

Domain

Далее перейдем к клиентской части – создаем и устанавливаем как запускаемый пустой проект ASP.NET Web Application UI с сылками на MVC packages.

UI1

UI2

И наконец, добавим class library UnitTests, отвечающую за юнит-тестирование.

UnitTests

Внимание: хотя юнит-тесты и не являются обязательной частью приложения, мы рекомендуем Вам всегда покрывать код тестами, так как это позволит в будущем избежать множества проблем с ошибками в коде за счет автоматизации тестирования.

После выполнения всех вышеперечисленных действий должен получится следующий Solution:

Solution

После создания структуры Solution’а необходимо собственно установить пакеты Incoding Framework из Nuget в соответствующие projects.

Установка происходит через Nuget. Для всех projects алгоритм установки один:

  1. Кликните правой кнопкой по проекту и выберите в контекстном меню пункт Manage Nuget Packages…
  2. В поиске введите incoding
  3. Выберите нужный пакет и установите его

Сначала устанавливаем Incoding framework в Domain.

Incoding_framework_1

Далее добавляем в файл Domain -> Infrastructure -> Bootstrapper.cs ссылку на StructureMap.Graph.

StructureMap_ref

В UI нужно установить два пакета:

  1. Incoding Meta Language
  2. Incoding Meta Language Contrib

Incoding_Meta_Languge

MetaLanguageContrib_install

Внимание: убедитесь, что для References -> System.Web.Mvc.dll свойство “Copy Local” установлено в “true”

Теперь файл Example.UI -> Views -> Shared -> _Layout.cshtml измените таким образом, чтобы он выглядел так:

Осталось добавить ссылку на Bootstrapper.cs в файлы Example.UI -> App_Start -> IncodingStart.cs и Example.UI -> Controllers -> DispatcherController.cs.

IncodingStart_bootstrapper

DispatcherController_bootstrapper

Внимание: если вы используете MVC5, то для работы framework’а необходимо добавить следующий код в файл Web.config

Осталось установить Incoding tests helpers в UnitTests и добавить ссылку на Bootstrapper.cs в Example.UnitTests -> MSpecAssemblyContext.cs.

Incoding_tests_helpers

MSpecAssemblyContext_bootstrapper

Последний этап подготовки проектов к работе – создание структуры папок для projects.

Добавьте следующие папки в проект Example.Domain:

  1. Operations – command и query проекта
  2. Persistences – сущности для маппинга БД
  3. Specifications – where и order спецификации для фильтрации данных при запросах

Example.Domain_folders

В проекте Example.UnitTests создайте такую же структуру папок как и в Example.Domain.

UnitTests_folders

Часть 2. Настройка DB connection.

Для начала создадим БД, с которыми будем работать. Откройте SQL Managment Studio и создайте две базы данных: Example и Example_test.

add_DB

example_db

example_test_db

Для того чтобы работать с БД в необходимо настроить connection. Добавьте в файлы Example.UI -> Web.config и Example.UnitTests -> app.config connection string к базе данных:

В файле Example.Domain -> Infrastructure -> Bootstrapper.cs зарегистрируйте по ключу “Example” соответствующую строку подключения:

В файле Example.UnitTests -> MSpecAssemblyContext.cs  зарегистрируйте по ключу “Example_Test” строку подключения к базе данных для тестов:

Внимание: базы данных Example и Example_Test должны существовать.

Часть 3. CRUD.

После выполнения всех приведенных выше действий мы подошли к самой интересной части – написанию кода, реализующего CreateReadUpdateDelete-функционал приложения. Для начала необходимо создать класс сущности, которая будет маппиться на БД. В нашем случае это будет Human.cs, который добавим в папку Example.Domain -> Persistences.

Human.cs

Наш класс содержит несколько полей, в которые мы будем записывать данные, и вложенный класс маппинга (class Map).

Заметка: после создания класса Human Вам больше не нужно производить никаких действий (дописывание xml-маппинга) благодаря FluentNhibernate.

Теперь добавим команды (Command) и запросы (Query), которые будут отвечать за реализацию CRUD-операций. Первая комманда будет отвечать за добавление новой или изменение существующей записи типа Human. Комманда довольно простая: мы либо получаем из Repository сущность по ключу (Id), либо, если такой сущности нет, создаем новую. В обоих случаях сущность получает значения, которые указаны в свойствах класса AddOrEditHumanCommand. Добавим файл  Example.Domain -> Operations -> AddOrEditHumanCommand.cs в проект.

AddOrEditHumanCommand.cs

Следующая часть CRUD – Read – запрос на чтение сущностей из базы. Добавьте файл Example.Domain -> Operations -> GetPeopleQuery.cs.

GetPeopleQuery.cs

И оставшаяся часть функционала – это Delete – удаление записей из БД по ключу (Id).  Добавьте файл Example.Domain -> Operations -> DeleteHumanCommand.cs.

DeleteHumanCommand.cs

Для того чтобы наполнить БД начальными данными добавьте файл Example.Domain -> InitPeople.cs – этот файл наследуется от интерфейса ISetUp.

ISetup

Все экземпляры классов, унаследованных от ISetUp, регистрируются через IoC в Bootstrapper.cs (был приведен во введении). После регистрации они запускаются на исполнение (public void Execute() ) по порядку (public int GetOrder() ).

InitPeople.cs

Back-end реализация CRUD готова. Теперь надо добавить клиентский код. Также как и в случае с серверной частью, начнем реализацию с части создания/редактирования записи. Добавьте файл Example.UI -> Views -> Home -> AddOrEditHuman.cshtml. Представленный IML-код создает стандартную html-форму и работает с командой AddOrEditHumanCommand, отправляя на сервер соответствующий Ajax-запрос.

AddOrEditHuman.cshtml

Далее следует template, который является шаблоном для загрузки данных, полученных от GetPeopleQuery. Здесь описывается таблица, которая будет отвечать не только за вывод данных, но и за удаление и редактирование отдельных записей: добавьте файл Example.UI -> Views -> Home -> HumanTmpl.cshtml.

HumanTmpl.cshtml

Задача открытия диалогового окна достаточно распространена, поэтому код, отвечающий за это действие, можно вынести в extension.

Последняя часть – изменение стартовой страницы так, чтобы при ее загрузке выполнялся Ajax-запрос на сервер для получения данных от GetPeopleQuery и отображения их через HumanTmpl: измените файл Example.UI -> Views -> Home -> Index.cshtml так, чтобы он соответствовал представленному ниже коду.

Index.cshtml

В реальных приложениях валидация введенных данных форм – одна из самых частых задач. Поэтому добавим валидацию данных на форму добавления/редактирования сущности Human. Первая часть – добавление серверного кода. Добавьте следующий код в AddOrEditHumanCommand как nested class:

На форме AddOrEditHuman.cshtml мы использовали конструкции вида:

Поэтому нет необходимости дополнительно добавлять

для полей – ForGroup() сделает это за нас.

Таким образом, мы написали код приложения, которое реализует CRUD-функционал для одной сущности БД.

Часть 4. Specifications – фильтрация данных.

Еще одна из задач, которые часто встречаются в реальных проектах – фильтрация запрашиваемых данных. В Incoding Framework для удобства написания кода и соблюдения принципа инкапсуляции для фильтрации получаемых в Query данных используются WhereSpecifications. Добавим в написанный код возможность фильтрации получаемых в GetPeopleQuery данных по FirstName и LastName. В первую очередь добавим два файла спецификаций Example.Domain -> Specifications -> HumanByFirstNameWhereSpec.cs и Example.UI -> Specifications -> HumanByLastNameWhereSpec.cs

HumanByFirstNameWhereSpec.cs

HumanByLastNameWhereSpec.cs

Теперь используем написанные спецификации в запросе GetPeopleQuery. При помощи связок .Or()/.And() атомарные спецификации можно соединять в более сложные, что помогает использовать созданные спецификации многократно и при их помощи тонко настраивать необходимые фильтры данных (в нашем примере мы используем связку .Or() ).

GetPeopleQuery.cs

И наконец, осталось немного модифицировать Index.cshtml, чтобы добавить поисковую строку, задействующую при запросе поле Keyword для фильтрации данных.

Index.cshtml

Часть 5. Юнит-тесты.

Покроем написанный код тестами. Первый тест отвечает за проверку маппинга сущности Human. Файл When_save_Human.cs добавим в папку Persisteces проекта UnitTests.

When_save_Human.cs

Данный тест работает с тестовой базой данных (Example_test): создается экземпляр класса Human с автоматически заполненными полями, сохраняется в базу, а затем извлекается и сверяется с созданным экземпляром.

Теперь добавим тесты для WhereSpecifications в папку Specifications.

When_human_by_first_name.cs

When_human_by_last_name.cs

Теперь осталось добавить тесты для команды и запроса (папка Operations), причем для команды необходимо добавить два теста: один для проверки создания новой сущности и второй для проверки редактирования уже существующей сущности.

When_get_people_query.cs

When_add_human.cs

When_edit_human.cs

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

  1. IML, 5 причин использовать – клиентские сценарии
  2. CQRS расширенный курс – архитектура серверной части
  3. MVD – описание паттерна Model View Dispatcher
  4. IML-селекторы – описание использования селекторов в IML
  5. Расширения – помощь написания extensions для соблюдения принципа Don’tRepeatYourself
  6. Репозиторий – описание реализации репозитория и приемов работы с ним
  7. Ajax-сценарии – описание работы IML в связке с Ajax
  8. Шаблоны для вставки данных
  9. Тестирование и тестовые сценарии

Vlad Kopachinsky

I am a Senior developer at Incoding Software in Russian Federation. I created the Incoding Framework for rapid development

More Posts - Website - Twitter - Facebook - LinkedIn

Leave a Reply