Монорепозиторий с C#: организация кода в .NET проектах

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

Монорепозиторий с C#: как организовать код в .NET проектах

Современная разработка на C# и .NET всё чаще сталкивается с выбором: хранить все связанные проекты в одном репозитории (монорепозиторий) или разделить их по отдельным репозиториям (мультирепозиторий). Монорепозиторий — это подход, при котором код нескольких приложений, библиотек, тестов и конфигураций хранится в одном Git-репозитории. Для больших корпоративных решений, микросервисов или библиотек NuGet этот подход может значительно упростить управление зависимостями, рефакторинг и развёртывание.

В этой статье мы разберём, как правильно организовать монорепозиторий с C# в экосистеме .NET, рассмотрим структуру папок, настройку проектов, CI/CD и лучшие практики.

1. Преимущества и недостатки монорепозитория в .NET

Прежде чем углубляться в технические детали, важно понять, когда монорепозиторий действительно полезен, а когда он может стать проблемой.

Преимущества:

  • Единая версия зависимостей. Все проекты внутри репозитория используют одни и те же версии пакетов, что исключает конфликты версий.
  • Упрощённый рефакторинг. Изменение общего интерфейса или API библиотеки сразу видно во всех проектах, которые его используют.
  • Целостность коммитов. Атомарные коммиты, которые изменяют и библиотеку, и приложение, работают без временных разрывов.
  • Общие настройки. Один файл Directory.Build.props задаёт настройки компиляции, анализа кода и версии для всех проектов.

Недостатки:

  • Рост размера репозитория. Со временем клонирование и история могут стать очень большими.
  • Сложность CI/CD. Требуется умная система сборки, чтобы не пересобирать всё при каждом коммите.
  • Права доступа. В одном репозитории сложнее разграничить доступ для разных команд.

Для .NET-проектов монорепозиторий особенно удобен, когда у вас есть общая библиотека классов (например, Domain или Common), несколько микросервисов или веб-API, которые зависят от неё.

2. Структура монорепозитория для C# (.NET)

Правильная структура папок — основа успешного монорепозитория. Рассмотрим типовую организацию для решения, включающего несколько микросервисов, библиотеку и тесты.

MyApp/
├── .github/
│   └── workflows/
│       └── ci.yml
├── src/
│   ├── MyApp.Common/          # общая библиотека
│   │   ├── MyApp.Common.csproj
│   │   └── ...
│   ├── MyApp.Api.Orders/      # микросервис для заказов
│   │   ├── MyApp.Api.Orders.csproj
│   │   └── ...
│   └── MyApp.Api.Users/       # микросервис для пользователей
│       ├── MyApp.Api.Users.csproj
│       └── ...
├── tests/
│   ├── MyApp.Common.Tests/
│   │   ├── MyApp.Common.Tests.csproj
│   │   └── ...
│   └── MyApp.Api.Orders.Tests/
│       ├── MyApp.Api.Orders.Tests.csproj
│       └── ...
├── Directory.Build.props       # общие настройки для всех проектов
├── MyApp.sln                   # решение для Visual Studio
└── README.md

Ключевые моменты:

  • Все проекты находятся в папке src/, тесты — в tests/. Это стандарт, принятый в .NET сообществе.
  • Файл Directory.Build.props задаёт общие свойства: TargetFramework, Nullable, ImplicitUsings, версии пакетов и т.д.
  • Один SLN-файл упрощает работу в IDE, но при большом количестве проектов можно разбить на несколько решений.

Пример Directory.Build.props:

<Project>
  <PropertyGroup>
    <TargetFramework>net8.0</TargetFramework>
    <Nullable>enable</Nullable>
    <ImplicitUsings>enable</ImplicitUsings>
    <TreatWarningsAsErrors>true</TreatWarningsAsErrors>
  </PropertyGroup>
</Project>

Этот файл автоматически применяется ко всем .csproj в папке и подпапках репозитория.

3. Настройка CI/CD для монорепозитория C#

Одна из главных сложностей — настроить сборку так, чтобы она пересобирала только изменённые проекты и их зависимости. Для этого можно использовать dotnet test с фильтрацией или инструменты вроде Nuke Build или Microsoft.Build.Traversal.

Пример GitHub Actions workflow (упрощённый):

name: .NET CI

on:
  push:
    branches: [ main ]

jobs:
  build:
    runs-on: ubuntu-latest

    steps:
    - uses: actions/checkout@v4

    - name: Setup .NET
      uses: actions/setup-dotnet@v4
      with:
        dotnet-version: '8.0.x'

    - name: Restore
      run: dotnet restore MyApp.sln

    - name: Build
      run: dotnet build MyApp.sln --configuration Release

    - name: Test
      run: dotnet test MyApp.sln --configuration Release --verbosity normal

Для более продвинутого сценария (сборка только изменённых проектов) можно использовать dotnet test --filter или скрипты, которые анализируют изменённые файлы через git diff. Например, если изменилась только библиотека MyApp.Common, то тесты для неё запускаются, а для остальных — нет.

Совет: используйте пакет Microsoft.Build.Traversal

Этот пакет позволяет создать единый проект, который ссылается на все остальные проекты в решении. Тогда команда dotnet build будет собирать их в правильном порядке.

Блоги

Рекомендаций по книгам