Монорепозиторий с C#: организация кода и лучшие практики
В современной разработке на C# (.NET) всё чаще используется подход монорепозитория (monorepo). В отличие от мультирепозиториев, где каждый проект живёт в своём Git-репозитории, монорепозиторий хранит весь код компании или продукта в одном центральном хранилище. Для .NET-разработчиков это означает единое решение (solution), общие библиотеки и упрощённую настройку CI/CD. В этой статье мы разберём, как правильно организовать монорепозиторий с C#, приведём примеры кода и обсудим ключевые преимущества.
1. Преимущества монорепозитория для .NET
Монорепозиторий особенно полезен для команд, работающих над несколькими связанными .NET-проектами: микросервисами, веб-приложениями, библиотеками классов. Основные плюсы:
- Единая версия зависимостей — все проекты используют одинаковые версии NuGet-пакетов, что исключает конфликты.
- Упрощённый рефакторинг — изменение общего интерфейса или модели данных сразу применяется ко всем потребителям.
- Общая история Git — атомарные коммиты, которые затрагивают несколько проектов, видны в одном месте.
- Централизованная CI/CD — один pipeline для сборки, тестирования и деплоя всех компонентов.
Для C# монорепозиторий строится вокруг единого .sln-файла (solution), который ссылается на все проекты. Это позволяет использовать встроенные средства Visual Studio и .NET CLI для управления зависимостями.
2. Структура монорепозитория для C#
Рассмотрим типовую структуру монорепозитория для .NET-решения. Она может выглядеть так:
MyCompany/
├── src/
│ ├── MyCompany.Common/ # Общая библиотека
│ │ ├── MyCompany.Common.csproj
│ │ └── ...
│ ├── MyCompany.WebApi/ # Веб-API
│ │ ├── MyCompany.WebApi.csproj
│ │ └── ...
│ └── MyCompany.BackgroundJob/ # Фоновый сервис
│ ├── MyCompany.BackgroundJob.csproj
│ └── ...
├── tests/
│ ├── MyCompany.Common.Tests/
│ ├── MyCompany.WebApi.Tests/
│ └── MyCompany.BackgroundJob.Tests/
├── docs/ # Документация
├── scripts/ # Скрипты для CI/CD
├── .gitignore
└── MyCompany.sln # Единое решение
Ключевые элементы этой структуры:
- src/ — папка с исходным кодом. Каждый проект — отдельная папка со своим .csproj-файлом.
- tests/ — папка с тестами. Названия тестовых проектов повторяют названия основных с суффиксом .Tests.
- docs/ — документация (README, архитектурные решения).
- scripts/ — скрипты для автоматизации сборки или деплоя.
- MyCompany.sln — единый файл решения, который включает все проекты.
Такая структура упрощает навигацию и позволяет легко добавлять новые проекты без изменения корневой конфигурации.
3. Настройка единого решения и общих зависимостей
Для создания монорепозитория в .NET используйте команду dotnet new sln, а затем добавляйте проекты:
# Создаём корневую папку и переходим в неё
mkdir MyCompany && cd MyCompany
# Создаём пустой файл решения
dotnet new sln -n MyCompany
# Создаём структуру папок
mkdir src tests docs scripts
# Создаём проекты
dotnet new classlib -n MyCompany.Common -o src/MyCompany.Common
dotnet new webapi -n MyCompany.WebApi -o src/MyCompany.WebApi
dotnet new console -n MyCompany.BackgroundJob -o src/MyCompany.BackgroundJob
# Добавляем проекты в решение
dotnet sln add src/MyCompany.Common/MyCompany.Common.csproj
dotnet sln add src/MyCompany.WebApi/MyCompany.WebApi.csproj
dotnet sln add src/MyCompany.BackgroundJob/MyCompany.BackgroundJob.csproj
Теперь добавим ссылку на общую библиотеку из веб-API:
cd src/MyCompany.WebApi
dotnet add reference ../MyCompany.Common/MyCompany.Common.csproj
Пример кода общей библиотеки (MyCompany.Common):
// src/MyCompany.Common/Models/User.cs
namespace MyCompany.Common.Models;
public class User
{
public int Id { get; set; }
public string Name { get; set; } = string.Empty;
public string Email { get; set; } = string.Empty;
}
Пример использования в веб-API (MyCompany.WebApi):
// src/MyCompany.WebApi/Controllers/UsersController.cs
using Microsoft.AspNetCore.Mvc;
using MyCompany.Common.Models;
namespace MyCompany.WebApi.Controllers;
[ApiController]
[Route("[controller]")]
public class UsersController : ControllerBase
{
[HttpGet]
public IActionResult GetUsers()
{
var users = new List<User>
{
new User { Id = 1, Name = "Alice", Email = "alice@example.com" },
new User { Id = 2, Name = "Bob", Email = "bob@example.com" }
};
return Ok(users);
}
}
Такая настройка гарантирует, что все проекты используют одну и ту же версию MyCompany.Common. Любое изменение в библиотеке сразу отражается на всех потребителях.
4. Работа с Git и CI/CD в монорепозитории
Для эффективной работы с монорепозиторием в .NET используйте следующие практики:
- Git Flow или Trunk-Based Development — выберите один подход для всей команды. В монорепозитории особенно удобен Trunk-Based, когда изменения мержатся в основную ветку несколько раз в день.
- .gitignore для .NET — обязательно добавьте стандартный .gitignore для C# (bin, obj, .vs, NuGet-пакеты).
- CI/CD с фильтрацией — настройте pipeline так, чтобы сборка запускалась только для изменённых проектов. Например, в GitHub Actions можно использовать paths-filter.
Пример YAML для GitHub Actions, который запускает тесты только для изменённых проектов: