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");
// Хорошо