Привет, Хабр! Эта статья — часть проекта «20 в 20», в котором мы рассказываем о региональных ИТ-хабах Т-Банка и о том, как в них растут команды и инженерные практики.Я Дима Фирстов, инженер core-команды Т-Бизнеса из томского ИТ-хаба. Мы занимаемся разработкой общих библиотек и инструментов, которые помогают продуктовым командам работать эффективнее и не изобретать велосипеды. Наша миссия — позволить продуктовым командам сосредоточиться на бизнес-логике, а инфраструктурный код мы по максимуму возьмем на себя. Помогать продуктовым командам начинаем уже на старте нового сервиса — для этого мы разработали шаблон для генерации репозитория .NET-сервиса, который позволяет быстро получить предсказуемую и готовую к эксплуатации основу нового приложения.В статье расскажу о техническом устройстве нашего шаблона — базы для большинства backend-решений на .NET в компании. Читать далее

Привет, Хабр! Эта статья — часть проекта «20 в 20», в котором мы рассказываем о региональных ИТ‑хабах Т‑Банка и о том, как в них растут команды и инженерные практики.
Я Дима Фирстов, инженер core‑команды Т‑Бизнеса из томского ИТ‑хаба. Мы занимаемся разработкой общих библиотек и инструментов, которые помогают продуктовым командам работать эффективнее и не изобретать велосипеды.
Наша миссия — позволить продуктовым командам сосредоточиться на бизнес‑логике, а инфраструктурный код мы по максимуму возьмем на себя. Помогать продуктовым командам начинаем уже на старте нового сервиса — для этого мы разработали шаблон для генерации репозитория.NET‑сервиса, который позволяет быстро получить предсказуемую и готовую к эксплуатации основу нового приложения.
В статье расскажу о техническом устройстве нашего шаблона — базы для большинства backend‑решений на.NET в компании.
Автоматизация создания проектаАнализ показал, что без автоматизации процесса создания разработчик тратит 10–14 дней на настройку инфраструктуры:
создание структуры решения;
интеграцию с платформенными решениями: PostgreSQL, Kafka, Valkey и так далее;
написание docker‑файлов и helm‑чартов;
конфигурацию пайплайнов GitLab CI/CD.
настройку телеметрии: логов, метрик, трейсов.
Чтобы исключить дублирование усилий между командами и снизить порог входа, мы внедрили шаблон репозиториев на уровне платформы разработки — у нас она называется Spirit. Это позволяет уйти от практики «copy/paste из соседнего проекта», которая чревата распространением ошибок и непроверенных решений.
Шаблон проекта по своему UX очень похож на создание решения в IDE: нажимаешь красивую желтую кнопочку в UI платформы разработки, заполняешь базовые параметры — название и описание решения, желаемый пайплайн CI/CD, — и готово. Spirit генерирует готовый к работе репозиторий.
Механизм генерации репозитория. В отличие от стандартных шаблонов dotnet new наше решение работает на уровне платформы разработки. Это делает его независимым от стека: аналогичный подход используется для Java, Go, Python и других стеков. Кроме того, в результате генерируется не только решение, но и вся структура конечного репозитория — вплоть до шаблонного readme.md.
В основе генерации репозитория лежит старый, добрый copy/paste с небольшими доработками поверх. Мы подготовили эталонный репозиторий.NET‑проекта, и его содержимое по определенным правилам копируется в создаваемый репозиторий.
Не любой репозиторий может стать шаблоном. Чтобы платформа разработки поняла, что репозиторий можно использовать в качестве шаблона, необходимо положить в корень файл конфигурации с названием.template‑settings.yml, который описывает метаданные и параметры копирования. Вот пример минимального файла конфигурации:
apiVersion: devplatform.tcsbank.ru/v1
kind: RepositoryTemplate
spec:
name: Dotnet Core Template
status: ready
owner: "dotnet-template-devs"
description: "Базовый шаблон для .NET-приложений"
parameters:
- type: string
name: projectName
description: Пространство имен (например, TTech.MyService)
default: SomeProject
- type: boolean
name: useBusinessPipeline
description: Интеграция с CI/CD конкретной бизнес-линии
default: falseВ файле конфигурации есть следующее.
Стандартные заголовки, задающие версию формата конфигурации и конкретный тип конфигурации.
Метаданные шаблона:
name — название шаблона, которое будет отображаться в интерфейсе Spirit;
status — статус готовности шаблона: draft (шаблон в разработке) или ready (можно брать и пользоваться);
owner — группа ответственных за шаблон;
description — описание, которое вместе с названием будет отображаться в интерфейсе Spirit.
Параметры генерации — набор переменных, которые превратятся в текстовые поля, флажки и выпадающие списки в интерфейсе Spirit и позволят пользователю шаблона настроить свой будущий репозиторий под свои нужды. В примере конфигурации приведены два параметра:
projectName — название будущего сервиса, которое будет подставлено и в названия файлов, и в пространство имен, и вообще везде, где только можно;
useBusinessPipeline — галочка, которая позволяет включить использование пайплайна «Т‑Бизнес» (в разных бизнес‑линиях могут быть немного разные наборы задач, определяемых SRE).
Далее автор шаблона может использовать эти переменные для управления тем, как файлы шаблона будут скопированы в генерируемый репозиторий. Текстовые переменные можно использовать как в названии файла, так и в его содержимом.
Возьмем, к примеру, файл [=projectName].Api.csproj со следующим содержимым:
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<RootNamespace>[=projectName].Api</RootNamespace>
<AssemblyName>[=projectName].Api</AssemblyName>
</PropertyGroup>
</Project>Если пользователь шаблона укажет имя проекта TTech.MyService, в результате генерации получится файл TTech.MyService.Api.csproj с соответствующими RootNamespace и AssemblyName.
Булевские переменные можно использовать в тексте шаблонных файлов, гибко указывая, какие строчки необходимо сгенерировать:
#if useBusinessPipeline]
include:
- project: business-ci-cd/dotnet
file: pipelines/app/default-pipeline.gitlab-ci.yml
[#else]
include:
- local: '.gitlab-ci-base.yml'
[/#if]Так как шаблон — это обычный репозиторий, в нем могут быть различные файлы, которые не должны попасть в генерируемый репозиторий. Например, тесты самого шаблона. Такие исключения настраиваются в файле .template-exclude. Еще в этом файле допустимо использование переменных, что позволяет управлять тем, какие файлы должны копироваться в зависимости от выбранных пользователем параметров:
template-tests/**
[#if useBusinessPipeline]
.gitlab-ci-base.yml
[/#if]Содержимое шаблонаВ результате применения шаблона генерируется полностью готовый REST‑сервис с тестами, подключенными внутренними библиотеками, инфраструктурой для локального тестирования и CI/CD для сборки образа и развертывания в Kubernetes.
src — исходный код приложения:
TTech.MyService.sln — файл решения.
NuGet.Config — конфигурация источников NuGet-пакетов с внутренним репозиторием компании.
TTech.MyService.Domain — проект с доменной логикой.
TTech.MyService.Infrastructure.Interfaces.* — интерфейсы инфраструктурных интеграций:
TTech.MyService.Infrastructure.Interfaces.DataAccess — интерфейсы слоя доступа к данным.
TTech.MyService.Infrastructure.Interfaces.Kafka — интерфейсы для работы с Kafka.
TTech.MyService.Infrastructure.Interfaces.ExternalDependency — пример интерфейса для работы с внешней зависимостью — каким-нибудь HTTP-сервисом.
TTech.MyService.UseCases — проект с прикладной логикой.
TTech.MyService.Infrastructure.Implementation.* — реализация интерфейсов инфраструктурных интеграций:
TTech.MyService.Infrastructure.Implementation.DataAccess.
TTech.MyService.Infrastructure.Implementation.Kafka.
TTech.MyService.Infrastructure.Implementation.ExternalDependency.
TTech.MyService.Controllers — контроллеры API.
TTech.MyService.Api — точка входа, «грязная» сборка, которая связывает все воедино.
Tests — примеры интеграционных тестов для работы с БД, Kafka и моками внешних зависимостей.
devplatform — папка с файлами метаданных для платформы разработки Spirit:
service.yml — описание сервиса: название, ответственная команда, ссылки на документацию и так далее.
quality-gate.yml — настройки внутренней системы контроля качества проектов Quality Gate.
build — файлы для сборки Docker-образа:
TTech.MyService.Web.Dockerfile — образ самого приложения.
TTech.MyService.DbMigration.Dockerfile — образ для запуска миграций БД.
.gitlab-ci.yml — пайплайн GitLab CI/CD.
docker-compose.yml — инфраструктурные зависимости и моки внешних зависимостей для локального запуска.
readme.md.Мы в компании любим и ценим «Чистую архитектуру» дяди Боба. Опросы сообщества.NET‑разработчиков показывают, что большинство команд старается придерживаться этого подхода у себя в проектах. Конечно, есть и адепты Vertical Slice Architecture, и других подходов к организации кода, но большинство все‑таки за «Чистую архитектуру».
К сожалению, в оригинальной книге описаны лишь подходы в общем виде — конкретной реализации нет. Это приводит к тому, что разные проекты применяют этот подход немножко по‑разному. Чтобы выровнять новые проекты с точки зрения применяемой терминологии и структуры, мы приняли несколько решений по организации кода:
Ядро назвали Domain, следуя заветам DDD. Обычно это один проект, который содержит бизнес‑сущности, логику валидации, Value Objects и доменные события. Не имеет внешних зависимостей.
Проект с прикладной логикой назвали UseCases, как в «Чистой архитектуре». В шаблоне мы явно разделили код в этом проекте на Commands и Queries — для сценариев, которые изменяют и только возвращают данные соответственно. Для реализации обработчиков команд и запросов используем библиотеку Mediator.
Инфраструктурный слой назван Infrastructure, как в «Чистой архитектуре», и разбит на множество проектов сразу двумя способами.
Мы поделили все сборки на Interfaces и Implementation. Прикладная логика в UseCases ссылается только на Interfaces, реализация в Implementation — только на Interfaces. Так сборки получаются максимально чистыми, а их взаимоотношения — максимально явными.
Каждую интеграцию мы выносим в отдельную сборку. Так, есть отдельные проекты для Data Access, Kafka, по отдельному проекту для каждого клиента к системам банка. Так все зависимости становятся явными на уровне структуры решения.
В отдельную сборку Controllers решили вынести контроллеры API, чтобы они не терялись в файлах с кодом регистрации всех зависимостей и прочих настройках веб‑сервиса. Контроллеры разделяются на внутренние и внешние. Они привязываются к разным портам и имеют разное предназначение. Внешние нужны для использования внешними клиентами: Frontend, Mobile, другими сервисами. Внутренние предназначены для диагностических и административных функций.
Точку входа назвали Api, так как из шаблона генерируется REST‑сервис. Здесь уже нет никакой бизнес‑специфики — только регистрация всех зависимостей и логика всего сервиса: Middlewares, работа с настройками, Swagger и так далее.
Мы сразу генерируем сервис со всеми основными интеграциями: PostgreSQL, Kafka, Valkey и так далее. Не всем командам это может понадобится. Но мы осознанно не стали добавлять лишние настройки в интерфейс шаблона в Spirit: практика показала, что такие интеграции нужны практически всем, а тем, кому они не нужны, проще будет удалить лишнее.
Кроме того, пример написания интеграции можно использовать для реализации других интеграций. Так, в шаблоне показано, как при интеграции внешней зависимости настраивать политики отказоустойчивости, авторизацию, управлять настройками.
Помимо стандартных библиотек ASP.NET, Entity Framework, Confluent.Kafka и Mediator в шаблон интегрированы наши core‑библиотеки:
API — единый формат REST‑методов с унифицированной структурой успешных и ошибочных ответов.
Health Checks — автоматическое добавление проб для Kubernetes.
Graceful Shutdown — преднастроенный механизм корректного завершения работы сервиса.
Feature Management — управление функциональными флагами через централизованный механизм.
Observability — преднастроенный OpenTelemetry с экспортом в системы трейсинга и метрик.
Runtime Diagnostics — инструменты для диагностики и профилирования приложения в рантайме.
Созданный из шаблона проект сразу покрыт интеграционными тестами. Генерируются примеры тестов с использованием БД, Kafka и с моком внешнего HTTP‑сервиса. Сгенерированные тесты сразу запускаются в CI/CD. Результаты прогона тестов отливаются во внутреннюю систему контроля качества проектов Quality Gate.

Выбор шаблона
После создания репозитория сервис готов к развертыванию. Сразу настроена интеграция со стандартной системой мониторинга Sage — туда отливаются логи, метрики и трейсы. Для визуализации метрик есть готовые доски в Grafana:
RED‑метрики (rate, errors, duration).
Потребление ресурсов: CPU, память, работа GC, thread pool.
Метрики специфических библиотек (пулы соединений БД, размеры очередей Kafka).
Дополнительно в CI/CD встроены анализаторы, собирающие информацию о кодовой базе сервиса. В них попадают данные о версиях зависимостей, результатах статического анализа и уровне тестового покрытия. Это помогает контролировать техническое качество сервиса не только при генерации репозитория, но и при последующей его разработке и эксплуатации

Пример визуализации базовых метрик сервиса: потребления памяти и CPU, готовности подов и их перезапусков.
Результаты внедренияЗа прошедший год в компании было развернуто 359 новых сервисов на базе этого шаблона. Ключевой инженерный показатель — сокращение времени Time‑to‑Hello‑World (от создания репозитория до первого успешного деплоя в QA‑контур) с 14 до 2 дней.
Мы продолжим развивать шаблон и дальше — и с точки зрения технической базы, и с точки зрения удобства для команд.
Для меня в этом проекте особенно важно то, что шаблон не заканчивается на генерации файлов. Он помогает выстроить одинаковую основу для очень разных сервисов, уменьшить хаос на старте и сделать инфраструктурный путь более предсказуемым. А когда таких сервисов сотни, именно эта предсказуемость и дает самый заметный эффект.
Автоматизация рутины позволяет нашим инженерам фокусироваться на архитектурных задачах и реализации бизнес‑логики, не тратя время на склеивание инфраструктурных компонентов.
Томский хаб во всей этой историиПодобные шаблону инструменты не появляются в вакууме. У нас в томском хабе сильная инженерная среда, где регулярно пересекаются продуктовые команды, платформенные разработки, стажировки и университетское сообщество. Здесь проходят митапы, Т‑толки, junior‑встречи, экскурсии для студентов и разные образовательные активности. Это помогает не только растить специалистов, но и поддерживать культуру, в которой такие платформенные инициативы вообще становятся возможны.
В Томске работают команды с очень разным стеком. Когда офис только открылся, больше всего у нас развивалась профессия.NET, так как в Томске исторически большое.NET‑комьюнити и много сильных инженеров. Сейчас нас догнали Java‑разработчики, JS‑разработчики, мобильные разработчики, системные аналитики, QA, SRE и многие другие.
Но при этом офис остается местом, где инженерное сообщество ощущается как единое целое. Новички быстрее включаются в работу, опытные специалисты делятся практиками, а платформенные решения получают живую обратную связь от тех, кто ими реально пользуется.
В офисе сотрудники собираются в томские профессиональные сообщества: аналитиков, тимлидов, мобильных разработчиков,.NET‑разработчиков и другие профсообщества. Коллеги встречаются, обсуждают разные темы, делятся друг с другом опытом, готовят разные темы и выступают с докладами.
У нас обязательно проходят летние корпоративы и предновогодние вечеринки, бывают семейные дни в офисе, дни весны, айтишника и другие. А еще у нас много клубов по интересам: книжный клуб, спортивное комьюнити, туристический клуб, DnD, интеллектуальные игры и много всего другого.

Игра в кикер
У нас есть очень объединяющие кофе‑встречи, чтобы пообщаться с коллегами из разных направлений. Мы лучше узнаем друг друга и переносим этот опыт в рабочие задачи.

Если вам интересны региональные ИТ‑коммьюнити, можно подписаться на наш сибирский канал в Телеграме.
Для студентов и тех, кто хочет следить за жизнью томского хаба:
| # | Наименование новости | Тональность | Информативность | Дата публикации |
|---|---|---|---|---|
| 1 | Референсная архитектура платёжной платформы на.NET: схемы, границы транзакций, модули, кластер | 0 | 7.74 | 28-07-2026 |
| 2 | Платёжная платформа на.NET: во что она обходится и что из этого можно не писать | 0 | 7.74 | 28-07-2026 |
| 3 | Архитектура TMS на .NET: кластер, потоки координат и объектное хранилище | 0 | 8.14 | 30-07-2026 |
| 4 | Переезд с ESB на свой стек: что реально экономит бизнес | 0 | 8.04 | 29-07-2026 |
| 5 | [Перевод] Чему десять лет в разработке научили меня о технологиях | 0 | 12.49 | 06-08-2026 |
| 6 | Система растет там, где можно ошибаться. История из минского ИТ-хаба | 0 | 5 | 09-07-2026 |
| 7 | Продакшн‑разработка в одиночку с AI‑агентами: принуждай к правилам, а не объясняй их | 0 | 8.25 | 24-07-2026 |
| 8 | Технический долг — это не только legacy: как мы уменьшаем разброс решений между Go-сервисами | 0 | 7 | 07-07-2026 |
| 9 | [Перевод] Чему десять лет в разработке научили меня о технологиях | 0 | 12.49 | 06-08-2026 |
| 10 | Актуальность техстека как задача: наш путь от хаоса к регулярным SLA-апдейтам | 5 | 7 | 26-06-2026 |