Монорепозиторий с C#: организация кода для начинающих
В современной разработке на C# (.NET) рано или поздно встает вопрос: как организовать код, когда у вас несколько проектов — библиотека классов, веб-API, консольное приложение, тесты? Многие начинающие разработчики создают отдельные репозитории для каждой части, что приводит к путанице в версиях и зависимостях. Альтернатива — монорепозиторий (monorepo). В этой статье мы разберем, что такое монорепозиторий, как его настроить в C# и почему он упрощает жизнь новичкам.
Что такое монорепозиторий?
Монорепозиторий — это подход, при котором весь код нескольких связанных проектов хранится в одном репозитории Git. Например, у вас может быть общая библиотека MyApp.Shared, веб-приложение MyApp.Web и консольный инструмент MyApp.Cli — все в одной папке, с одним .sln файлом.
В контексте C# (.NET) монорепозиторий обычно означает одно Visual Studio Solution, содержащее несколько проектов. Это особенно удобно для:
- Совместного использования кода (через Project References, а не NuGet пакеты).
- Единого процесса сборки и CI/CD.
- Упрощения работы с версиями (одна ветка — все изменения сразу).
Преимущества монорепозитория для начинающих
Если вы только начинаете изучать C#, монорепозиторий поможет избежать типичных ошибок:
- Простота навигации — все проекты в одном месте, не нужно переключаться между репозиториями.
- Мгновенная связь — изменение в библиотеке сразу видно в зависимых проектах.
- Единый стиль — можно настроить один
.editorconfigилиDirectory.Build.propsдля всех проектов. - Легко запускать тесты — одной командой
dotnet testпроверяется всё решение.
Создание структуры монорепозитория: пошаговое руководство
Шаг 1. Создаем корневую папку и решение
Откройте терминал и выполните:
mkdir MyMonorepo
cd MyMonorepo
dotnet new sln -n MyApp
Теперь у вас есть файл MyApp.sln — это точка входа для Visual Studio или Rider.
Шаг 2. Добавляем проекты
Создадим три проекта: библиотеку классов, веб-API и консольное приложение. Для удобства поместим их в подпапки src:
mkdir src
cd src
dotnet new classlib -n MyApp.Shared
dotnet new webapi -n MyApp.Web
dotnet new console -n MyApp.Cli
cd ..
Теперь привяжем их к решению:
dotnet sln add src/MyApp.Shared/MyApp.Shared.csproj
dotnet sln add src/MyApp.Web/MyApp.Web.csproj
dotnet sln add src/MyApp.Cli/MyApp.Cli.csproj
Шаг 3. Настраиваем зависимости между проектами
Допустим, MyApp.Web и MyApp.Cli используют классы из MyApp.Shared. Вместо того чтобы публиковать библиотеку в NuGet, просто добавьте Project Reference:
dotnet add src/MyApp.Web/MyApp.Web.csproj reference src/MyApp.Shared/MyApp.Shared.csproj
dotnet add src/MyApp.Cli/MyApp.Cli.csproj reference src/MyApp.Shared/MyApp.Shared.csproj
Теперь в файле MyApp.Web.csproj появится секция:
<ItemGroup>
<ProjectReference Include="..\MyApp.Shared\MyApp.Shared.csproj" />
</ItemGroup>
Шаг 4. Пишем общий код
В проекте MyApp.Shared создадим простой класс:
// src/MyApp.Shared/GreetingService.cs
namespace MyApp.Shared;
public class GreetingService
{
public string GetGreeting(string name) => $"Привет, {name}! Добро пожаловать в монорепозиторий.";
}
Теперь используем его в консольном приложении:
// src/MyApp.Cli/Program.cs
using MyApp.Shared;
var service = new GreetingService();
Console.WriteLine(service.GetGreeting("Алексей"));
И в веб-API (например, в контроллере):
// src/MyApp.Web/Controllers/HomeController.cs
using Microsoft.AspNetCore.Mvc;
using MyApp.Shared;
[ApiController]
[Route("api/[controller]")]
public class HomeController : ControllerBase
{
[HttpGet("{name}")]
public IActionResult Get(string name)
{
var service = new GreetingService();
return Ok(new { message = service.GetGreeting(name) });
}
}
Шаг 5. Добавляем тесты
Тесты тоже лучше хранить в монорепозитории. Создадим папку tests и проект с тестами:
mkdir tests
cd tests
dotnet new xunit -n MyApp.Tests
cd ..
dotnet sln add tests/MyApp.Tests/MyApp.Tests.csproj
dotnet add tests/MyApp.Tests/MyApp.Tests.csproj reference src/MyApp.Shared/MyApp.Shared.csproj
Напишем простой тест:
// tests/MyApp.Tests/GreetingServiceTests.cs
using Xunit;
using MyApp.Shared;
public class GreetingServiceTests
{
[Fact]
public void GetGreeting_ReturnsCorrectMessage()
{
var service = new GreetingService();
var result = service.GetGreeting("Мир");
Assert.Contains("Мир", result);
}
}
Дополнительные советы по организации
Используйте Directory.Build.props
Чтобы не дублировать общие настройки (например, версию .NET, nullable), создайте в корне файл Directory.Build.props:
<Project>
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<Nullable>enable</Nullable>