Enterprise C#: как писать код, который не стыдно показать на code review

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

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

Начать курс

Enterprise C#: почему код для банка и код для пет-проекта — это две разные вселенные

Если вы когда-нибудь переходили из маленькой веб-студии в крупную корпорацию или финтех, то знаете это чувство лёгкого культурного шока. Вчера вы могли закоммитить «горячий фикс» без тестов и с одним комментарием //TODO: refactor, а сегодня вам прилетает ревью с 20 замечаниями, где каждый публичный метод требует XML-документации, а любая зависимость — регистрации в DI-контейнере.

Разработка на C# в enterprise — это не про «сделать быстро», а про «сделать так, чтобы не сломалось через три года, когда автора кода уже уволят». Это про предсказуемость, масштабирование и бесконечные code review. Давайте разберёмся, из чего на самом деле состоит enterprise-разработка на C#, и как не вылететь с первой же недели.

1. Архитектура: SOLID — это не модное слово, а закон

В стартапе вы можете позволить себе монолит с «магическими» строками и гигантскими классами на 2000 строк. В enterprise — нет. Здесь каждый принцип SOLID — это не абстракция, а руководство к действию.

Принцип единственной ответственности (SRP)

Ваш класс не должен знать, как отправлять письма, сохранять в базу и парсить XML одновременно. Если вы видите в одном классе три и более «ответственностей» — это запах. В enterprise такой код завернут на code review с формулировкой «нарушение SRP, переписать». Пример плохого кода:

public class OrderService
{
    public void ProcessOrder(Order order)
    {
        // 1. Валидация
        // 2. Сохранение в БД
        // 3. Отправка email
        // 4. Логирование
    }
}

Правильный подход — разбить на отдельные сервисы и использовать паттерн Mediator или Pipeline:

public class ProcessOrderCommandHandler : IRequestHandler<ProcessOrderCommand, bool>
{
    private readonly IOrderRepository _repo;
    private readonly IEmailService _email;
    private readonly ILogger<ProcessOrderCommandHandler> _logger;

    public ProcessOrderCommandHandler(IOrderRepository repo, IEmailService email, ILogger<ProcessOrderCommandHandler> logger)
    {
        _repo = repo;
        _email = email;
        _logger = logger;
    }

    public async Task<bool> Handle(ProcessOrderCommand request, CancellationToken cancellationToken)
    {
        var order = await _repo.GetByIdAsync(request.OrderId);
        // логика
        await _email.SendAsync(order.CustomerEmail, "Order processed");
        _logger.LogInformation("Order {Id} processed", order.Id);
        return true;
    }
}

Обратите внимание: каждый сервис делает ровно одно дело. Тестировать такой код в разы проще.

2. Dependency Injection — ваш главный друг

В enterprise-проектах на C# вы почти никогда не увидите new MyService() внутри класса. Вместо этого — DI-контейнер (обычно Autofac или встроенный Microsoft.Extensions.DependencyInjection).

Правило простое: если класс создаёт свои зависимости сам — он нарушает Dependency Inversion Principle. В enterprise это считается ошибкой архитектуры. Пример:

// ❌ Плохо: жёсткая связка
public class ReportGenerator
{
    private readonly PdfExporter _exporter = new PdfExporter();
    public byte[] Generate() => _exporter.Export();
}

// ✅ Хорошо: инверсия зависимостей
public class ReportGenerator
{
    private readonly IExporter _exporter;
    public ReportGenerator(IExporter exporter) => _exporter = exporter;
    public byte[] Generate() => _exporter.Export();
}

Регистрация в стартапе:

services.AddScoped<IExporter, PdfExporter>();
services.AddScoped<ReportGenerator>();

Зачем это нужно? Во-первых, тесты: вы можете замокать IExporter и не генерировать реальные PDF. Во-вторых, гибкость: захотели экспорт в Excel — просто заменили реализацию в контейнере.

3. Code Review — это не пытка, а школа

В крупных компаниях code review — это священный ритуал. Ни один коммит не попадает в master без аппрува. И это прекрасно. Что обычно проверяют на ревью в enterprise C# проектах:

  • Стиль кода — отступы, именование, соответствие .editorconfig.
  • Обработка ошибок — нет пустых catch, есть корректные логи и проброс исключений.
  • Производительность — нет лишних асинхронных вызовов, нет Task.Run без необходимости.
  • Безопасность — нет SQL-инъекций, нет хранения паролей в открытом виде.
  • Тесты — если фича, то должны быть unit-тесты.

Совет: относитесь к code review как к возможности научиться. Если вам пишут «используй IReadOnlyCollection вместо List» — это не придирка, а best practice.

4. Тестирование — три уровня защиты

Enterprise-разработка на C# без тестов — это как прыжок с парашютом без запасного. Обычно используется пирамида тестов:

  • Unit-тесты (xUnit, NUnit) — покрывают бизнес-логику. Изолированы от внешних систем.
  • Integration-тесты — проверяют взаимодействие с БД, API, файловой системой.
  • E2E-тесты — редко, только для критических сценариев.

Пример unit-теста для сервиса с DI:

public class OrderServiceTests
{
    [Fact]
    public async Task ProcessOrder_ValidOrder_ReturnsTrue()
    {
        // Arrange
        var repoMock = new Mock<IOrderRepository>();
        var emailMock = new Mock<IEmailService>();
        var loggerMock = new Mock<ILogger<ProcessOrderCommandHandler>>();
        var handler = new ProcessOrderCommandHandler(repoMock.Object, emailMock.Object, loggerMock.Object);

        // Act
        var result = await handler.Handle(new ProcessOrderCommand { OrderId = 1 }, CancellationToken.None);

        // Assert
        Assert.True(result);
        emailMock.Verify(e => e.SendAsync(It.IsAny<string>(), It.IsAny<string>()), Times.Once);
    }
}

В enterprise-проектах требование «покрытие тестами не менее 80%» — это не шутка, а реальный порог в CI/CD пайплайне.

5. Логирование и мониторинг — без них вы слепы

В продакшене крупного проекта вы не можете просто поставить breakpoint. Всё, что остаётся — это логи. В enterprise C# стандарт де-факто — Serilog или NLog с структурированным логированием.

// Плохо: текстовая строка
_logger.LogInformation("User " + userId + " logged in");

// Хорошо                

Блоги

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