Шаблоны микросервисов 5

Nimble Framework | создано: 5/4/2022 | опубликовано: 5/4/2022 | обновлено: 10/2/2022 | просмотров: 749

В статье речь пойдет про версию Nimble Framework 5.0. Шаблоны позволяют ускорить процессы, потому что исключают рутину. Не за чем делать одно и тоже много раз, можно просто использовать наработки и опыт других людей. Причем сфера применения шаблонов абсолютно не важна. Как часто вы создаете микросервисы?

Описание и предназначение

Внимание: данная статья описывает шаблон Nimble Framework версии 5.0.x

Шаблон Nimble Framework позволяет максимально быстро развернуть новое решение (solution), в котором будут настроены проекты (projects) и подключено (установлено) всё то, что требуется для старта процесса разработки, чтобы приступить непосредственно к решению поставленной задачи. Другими словами, позволяет упростить создание микросервиса (backend-сервера для SPA) на базе ASP.NET Core, которые готовы к дальнейшему расширению или какому-либо другому преобразованию, например, установка дополнительных nuget-пакетов. Когда микросервисы объединены в одну систему (МСА), они не могут быть не связаны между собой. Для облегчения этой задачи "введение нового сервиса в систему" также и предназначен шаблон, в который заложены базовые инфраструктурные функции и возможности.

В проектах, созданные из шаблона используется Coding Convention описанный компанией Microsoft, потому как определяет основные правила именования методов, переменных, полей, функций. А также Identifier names | Microsoft Docs для именование идентификаторов от той же самой компании Microsoft. Все проекты шаблонов построены на платформе NET 5.0

Структура проектов шаблона

Шаблон представляет собой решение (Solution) для .NET, который состоит из двух трех проектов (Project) трансформированных определенным образом, чтобы реализовать возможность сгенерировать готовый к работе проект для Visual Studio (Visual Studio Code, Rider). Проекты имеют следующие названия:

  • {CompanyName}.{ModuleName}.Entities
  • {CompanyName}.{ModuleName}.Data
  • {CompanyName}.{ModuleName}.Web

Разберемся, что из себя представляет каждых из проектов.

Проект Entities (Class library)

Проект содержит модели данных. Те самые данные, которые "складываются" в базу данных. Модели представляют "чистую" реализацию объектов бизнес логики (Anemic Model Model). Все "обслуживающие" классы (Service, Repository, Manager, Provider и т.д.) находятся в проекте .Web.

Зависимости (nuget-пакеты):

<ItemGroup>
   <PackageReference Include="Calabonga.EntityFrameworkCore.Entities.Base" Version="0.0.0" />
</ItemGroup>
  • Сборка Calabonga.EntityFrameworkCore.Entities.Base содержит базовые классы, которые позволяют использовать generic операции в сборках по работе с DAL.
  • Других зависимостей у этого проекта нет, он первый в иерархии зависимостей в шаблоне.

Проект Data (Class library)

Описание

В шаблоне реализован подход CodeFirst, который дает максимально возможность для реализации Clean ArchitectureDomain-Driven Design и прочите возможности, которые дает Domain Model паттерн. Другими словами, в этом проекте находится всё, что требуется для обслуживания DAL. ApplicationDbContext (DbContext от EntityFrameworkCore с некоторыми предопределенными параметрами), а также папка Migrations, в которой хранятся все изменения схемы данных в формате cs-классов.

Зависимости (nuget-пакеты):

<ItemGroup>
    <PackageReference Include="Calabonga.EntityFrameworkCore.Entities.Base" Version="0.0.0" />
    <PackageReference Include="Calabonga.UnitOfWork" Version="0.0.0" />
    <PackageReference Include="Microsoft.EntityFrameworkCore" Version="5.0.6" />
    <PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="5.0.6">
        <PrivateAssets>all</PrivateAssets>
        <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
    </PackageReference>
    <PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="5.0.6" />
    <PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="5.0.6">
        <PrivateAssets>all</PrivateAssets>
        <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
    </PackageReference>
    <PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="5.0.0" />
    <PackageReference Include="Microsoft.Extensions.Configuration.FileExtensions" Version="5.0.0" />
    <PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="5.0.0" />
</ItemGroup>

Проект Web (Web API)

Описание

Основным проектом шаблона (стартовым) является проект на базе Web API с поддержкой OpenAPI, который реализован на базе Swagger. Для вывод логов на консоль используется Serilog, как расширенная реализация ILogger. После генерации, сразу можно запустить решение.

OpenAPI

На картинке выше показан интерфейс OpenAPI на базе Swagger.

Зависимости (nuget-пакеты):

<ItemGroup>
    <PackageReference Include="AutoMapper" Version="10.1.1" />
    <PackageReference Include="AutoMapper.Extensions.Microsoft.DependencyInjection" Version="8.1.1" />
    <PackageReference Include="Calabonga.AspNetCore.Controllers" Version="3.0.1" />
    <PackageReference Include="Calabonga.EntityFrameworkCore.UnitOfWork" Version="2.0.2" />
    <PackageReference Include="Calabonga.Microservices.Core" Version="3.0.2" />
    <PackageReference Include="Calabonga.UnitOfWork" Version="0.0.0" />
    <PackageReference Include="Calabonga.UnitOfWork.Controllers" Version="3.0.3" />
    <PackageReference Include="FluentValidation" Version="10.2.3" />
    <PackageReference Include="FluentValidation.DependencyInjectionExtensions" Version="10.2.3" />
    <PackageReference Include="GitInfo" Version="2.1.2">
        <PrivateAssets>all</PrivateAssets>
        <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
    </PackageReference>
    <PackageReference Include="IdentityServer4.AccessTokenValidation" Version="3.0.1" />
    <PackageReference Include="Microsoft.EntityFrameworkCore" Version="5.0.6" />
    <PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="5.0.6">
        <PrivateAssets>all</PrivateAssets>
        <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
    </PackageReference>
    <PackageReference Include="Microsoft.EntityFrameworkCore.InMemory" Version="5.0.6" />
    <PackageReference Include="OperationResultCore" Version="3.0.0" />
    <PackageReference Include="PagedListLiteCore" Version="1.0.4" />
    <PackageReference Include="Serilog.AspNetCore" Version="4.1.0" />
    <PackageReference Include="Swashbuckle.AspNetCore" Version="6.1.4" />
    <PackageReference Include="System.Text.Json" Version="5.0.2" />
</ItemGroup>
  • Microsoft.EntityFrameworkCore.InMemory - по умолчанию, шаблон настроен на использование режима InMemory для EntityFrameworkCore. При необходимости возможна реализация подключения к любому провайдеру (например, Microsoft SQL Server, PostgreSQL Server и т.д.)
  • Automapper - используется как для очень сложных и частых проекций, та и для простых но очень частых mappings. Для увеличения производительности, может использоваться "ручной" режим.
  • Calabonga.Microservices.Core - базовые определения, контракты.
  • Calabonga.AspNetCore.Controllers - реализация на базе Mediatr (Vertical Slice Architecture)
  • Сборка Calabonga.UnitOfWork обеспечивает полный набор механизмов по управлению EntityFrameworkCore потому что поддерживает NET 5 и основан на инструменте UnitOfWork рекомендованных Microsoft списке расширений для EntityFrameworkCore.
  • GitInfo - "вытаскивает" версию приложения из GIT. А также название бранча и хэш коммита
  • IdentityServer4.AccessTokenValidation - интеграция сервиса с OAuth2.0 на базе IdentityServer4.
  • Swashbuckle.AspNetCore - Реализация OpenAPI
  • PagedListLiteCore - Сборка реализует разбивку данных на страницы (paging) для ответов, которые не используют IUnitOfWork.
  • OperationResultCore - обертка ответов от сервера (вариант реализация спецификации RFC 7807)

Что в шаблоне?

  1. Шаблон содержит предустановленные nuget-пакеты (далее пакеты или сборки) общего пользования, которые реализуют возможности разного рода. Например, mappings, настройки подключения, базовый функционал для работы с данными. Установка новых пакетов (для решения конкретной задачи) или удаление ненужных пакетов остается на ответственности разработчика. 
  2. В шаблоне заложен подход "Code First" на базе EntityFrameworkCore со всеми вытекающими отсюда последствиями: migrations, lazy loading, change tracker и прочие аспекты при работе с ORM. Эффективная работа с EntityFrameworkCore позволяет существенно ускорить процесс разработки.
  3. В шаблоне реализованы два подхода разработки прикладных задач: Vertical Slice Architecture на базе Mediatr (способ "правильный"), а также простой подход на базе MVC (способ "быстрый").

Подходы к написанию кода

Стоит отметить, что основной функционал заложенный в шаблон (способы описанные ниже) легко вернуть к исходному (чистый ASP.NET Core MVC) из сгенерированного проекта простым удалением двух сборок из списка nuget-пакектов и пару папок. Таким образом, превратив проект в "голый", но при этом содержащий все заложенные в него инфраструктурные механизмы: логирование, мониторинг, подключение к системе, регистрация в AuthorizationService и т.д.  

Способ "Быстрый"

Следуя из названия способа напрашивается вывод, что можно разрабатывать API сервиса очень быстро, и, на самом деле, так оно и сесть. Разработка методов CRUD получается очень быстрой. Такой подход используется для простых (или относительно простых сущностей).  Пример использования "скоростного" подхода с детальным описанием и подробными комментариями.

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

Требуется быстро "поднять" сервис, который будет иметь одну сущность, например, "DocumentType" для использования как справочник (то есть, только для чтения по идентификатору и получение постраничного списка). Вторая сущность "Message", для которой требуется элементарный CRUD без каких-либо излишество в бизнес-логики. Создание контроллеров DocumentTypeController и MessagesController на базе уже реализованных ReadonlyControllerBase и WritableControllerBase займет не более получаса при использовании данного способа.

Способ "Правильный" 

Способ, который должен браться за основу. Реализация функционала построена на базе Mediatr (близко к паттерну CQRS), что позволяет реализовать Vertical Slice Architecture (VSA). Использование VSA позволяет гибко и эффективно использовать подход разделяющий такие понятия как Query и Command, тем самым дает возможность масштабировать возможности "вертикально".

Преимущества большие, потому что вы сможете использовать обработчики Query и Command (паттерн CQRS) обрабатывать удобным (правильным, подходящим, эффективным, выбранным на основании условия, в конце-концов) способом для достижения максимального результата. Более того, такое понятие pipeline, заложенное в VSA, дает очень гибкой настройки процесса обработки запросов. Следовательно, вы можете "вклеить" специализированные обработчики, например, логирование, кэширование, обработку транзакций и прочите необходимые "полезности" на уровне базовых реализаций - на уровне шаблона. 

На рисунке ниже, все методы работают по одному принципу, но сами обработчики разные, причем в некоторых вариантах диаметрально противоположные. От ORM до StoredProcedure при необходимости. Даже не смотря на то, что StoredProcedure - это шаг к двухзвенной архитектуре (а это 100 шагов назад в истории разработки).

Подсказка

Заметьте, что всегда можно удалить одну сборку, которая реализует способ "Быстрый" и установить другую, которая реализует способ "Правильный", чтобы расширить возможности реализации бизнес-логики. Более того, можно использовать обе сборки параллельно.

Для чего шаблон?

Основа для старта

Шаблон создается как отправная точка для разработки нового микросервиса (backend или API), в котором заложены основы некоторых механизмов и подходов с примерами их использования в том числе. Использование шаблона универсализирует эти самые механизмы и подходы при использовании в разных микросервисах, созданных на базе одного и того же шаблона. Разработчику проще ориентироваться в "знакомом" проекте, например, использовать один и тот же способ делать запросы к базе данных, в также дорабатывать расширения для шаблона, делиться ими с другими разработчиками в других проектах (в контексте командной разработки).

Универсальность

Попробуем выделить основные аспекты, которые позволяет решить или существенно упростить использование шаблона:

  1. Шаблон включает нужные для всех микросервисов сборки и пакеты. Нужные новые - поставь, не нужные какие-то - удали. Часто добавляешь? Добавь в шаблон и т.д.;
  2. Шаблон включает (или может включать) общие (базовые) настройки на стандартных механизмов:
    1. Настройки подключения к MassTransit (через RabbitMQ);
    2. Настройки подключения к ELK для логирования;
    3. Настройки .editorconfig для среды разработки;
    4. Настройки CI/DC;
    5. Настройка подключения к Database;
  3. Шаблон включает общий (базовый) функционал:
    1. Расширения (extensions) для примитивных типов в C# (например, IsEmpty());
    2. Демонстрационные примеры использования тех или иных механизмов (изучив которые, можно просто удалить);
    3. Мониторинг на основе принятых в компании договоренностей;
    4. Работу с сервером аутентификации и авторизации OAuth2.0;
    5. Обеспечение защиты методов на базе встроенного в ASP.NET Core механизма авторизации и аутентификации (например, атрибут Authorize) настроенного на сервер авторизации;
  4. Универсальную структуру проектов в компании, которая поможет проще "входить" разработчикам в проект, и более осознано ориентироваться в проекте, построенных на базе одного или нескольких шаблонов.

Развитие

Существует два варианта дальнейшего развития использования шаблонов для разного типа проектов.

  • Расширять возможности одного шаблона через сборки (nuget-пакеты).

Принцип 1

Cоздал новый микросервис из одного общего шаблона, установил nuget-пакеты те, которые тебе нужны, чтобы реализовать бизнес-логику. Например, потребовался доступ к шине сообщений - уставил какой надо nuget-пакет.

  • Увеличивать количество шаблонов с реализацией конкретных возможностей.

Принцип 2

Cоздал проект из конкретного типа шаблона. Например, потребовался доступ к шине сообщений - взял за основу TemplateWithRabbitMQ.

Общая инфрастуктура

Каждый из микросервисов является частью системы, а система должна уметь разворачивать сервисы, собирать логи, мониторить и прочие обязательные в таких случаях манипуляции. Если данный функционал обязателен для каждого сервиса, то не глупо его писать (реализовывать) каждый раз сначала? Если говорить в контексте микросервисной архитектуры, то все микросервисы, так или иначе "похоже" друг на друга, с точки зрения инфраструктуры использования. Следовательно, заложив основу можно из одного общего создать другой шаблон более специализированный, который предназначен для более узкого круга задач: прокси сервис, gateway-сервис и т.д. Другое дело, если существует потребность создать шаблоны для разных платформ: ASP.NET Core, WPF, WinForm и т.д.

Заключение

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

Ссылки