Технологический стек
Alifshop API построен на современном стеке технологий .NET с акцентом на производительность, масштабируемость и надежность.
Основная платформа
.NET 8.0
- Версия: .NET 8.0 (LTS)
- Среда выполнения: ASP.NET Core 8.0
- Языки: C# 12
- Модель развертывания: Self-contained deployment
ASP.NET Core
// Конфигурация приложения
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllers()
.AddNewtonsoftJson();
builder.Services.AddAuthentication("Bearer")
.AddJwtBearer("Bearer", options => {
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true,
ValidateAudience = true,
ValidateLifetime = true,
ValidateIssuerSigningKey = true
};
});
var app = builder.Build();
Архитектурные паттерны
CQRS + MediatR
Разделение команд и запросов:
// Команда
public class CreateProductCommand : IRequest<int>
{
public string Name { get; set; }
public decimal Price { get; set; }
}
// Запрос
public class GetProductQuery : IRequest<ProductDto>
{
public int ProductId { get; set; }
}
// Регистрация
services.AddMediatR(typeof(CreateProductCommand).Assembly);
Dependency Injection (Autofac)
// Модуль конфигурации
public class CatalogModule : Module
{
protected override void Load(ContainerBuilder builder)
{
builder.RegisterType<ProductRepository>()
.As<IProductRepository>()
.InstancePerLifetimeScope();
builder.RegisterType<ProductService>()
.As<IProductService>()
.InstancePerLifetimeScope();
}
}
Хранение данных
MySQL - Основная база
Версия: MySQL 8.0 ORM: Entity Framework Core 8.0
{
"ConnectionStrings": {
"CatalogConnectionString": "Server=localhost;Database=catalog;Uid=user;Pwd=password;",
"OrdersConnectionString": "Server=localhost;Database=orders;Uid=user;Pwd=password;"
}
}
Redis - Кеширование
Версия: Redis 7.0 Библиотека: StackExchange.Redis
// Конфигурация Redis
services.AddStackExchangeRedisCache(options =>
{
options.Configuration = "localhost:6379";
options.InstanceName = "AlifshopCache";
});
// Использование
public class ProductService
{
private readonly IDistributedCache _cache;
public async Task<Product> GetProductAsync(int id)
{
var cacheKey = $"product_{id}";
var cachedProduct = await _cache.GetStringAsync(cacheKey);
if (cachedProduct != null)
return JsonSerializer.Deserialize<Product>(cachedProduct);
var product = await _repository.GetByIdAsync(id);
await _cache.SetStringAsync(cacheKey, JsonSerializer.Serialize(product),
new DistributedCacheEntryOptions
{
AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(30)
});
return product;
}
}
MongoDB - Документы
Версия: MongoDB 7.0 Использование: Корзины, сессии
// Конфигурация MongoDB
services.Configure<MongoDbSettings>(
configuration.GetSection("MongoDb"));
services.AddSingleton<IMongoClient>(serviceProvider =>
{
var settings = serviceProvider.GetRequiredService<IOptions<MongoDbSettings>>().Value;
return new MongoClient(settings.ConnectionString);
});
// Модель документа
public class CartDocument
{
public ObjectId Id { get; set; }
public string UserId { get; set; }
public List<CartItem> Items { get; set; } = new();
public DateTime CreatedAt { get; set; }
public DateTime UpdatedAt { get; set; }
}
Elasticsearch - Поиск
Версия: Elasticsearch 7.17 Библиотека: NEST
// Конфигурация Elasticsearch
services.AddSingleton<IElasticClient>(provider =>
{
var settings = new ConnectionSettings(new Uri("https://localhost:9200"))
.DefaultIndex("products")
.BasicAuthentication("elastic", "password")
.CertificateFingerprint("fingerprint");
return new ElasticClient(settings);
});
// Поиск товаров
public async Task<IEnumerable<Product>> SearchProductsAsync(string query)
{
var searchResponse = await _elasticClient.SearchAsync<Product>(s => s
.Query(q => q
.MultiMatch(m => m
.Fields(f => f.Field(p => p.Name).Field(p => p.Description))
.Query(query)
.Fuzziness(Fuzziness.Auto)
)
)
.Size(50)
);
return searchResponse.Documents;
}
Интеграции и коммуникации
Event Bus (Kafka)
Версия: Apache Kafka 3.0 Библиотека: KafkaFlow
// Конфигурация Kafka
services.AddKafka(kafka => kafka
.UseBootstrapServers("localhost:9092")
.AddCluster(cluster => cluster
.WithBrokers(new[] { "localhost:9092" })
.AddConsumer(consumer => consumer
.Topic("product-events")
.WithGroupId("catalog-service")
.WithBufferSize(100)
.WithWorkersCount(10)
.AddHandler<ProductEventHandler>()
)
.AddProducer<ProductEventProducer>()
)
);
// Обработка событий
public class ProductEventHandler : IMessageHandler<ProductCreatedEvent>
{
public async Task Handle(IMessageContext context, ProductCreatedEvent message)
{
// Обработка события создания товара
await _searchService.IndexProductAsync(message.Product);
}
}
HTTP клиенты
Resilience: Polly для retry/circuit breaker
// Конфигурация HTTP клиентов
services.AddHttpClient<IWarehouseService, WarehouseService>(client =>
{
client.BaseAddress = new Uri("https://warehouse-service.internal");
client.DefaultRequestHeaders.Add("Authorization", "Bearer token");
})
.AddPolicyHandler(GetRetryPolicy())
.AddPolicyHandler(GetCircuitBreakerPolicy());
// Политики устойчивости
static IAsyncPolicy<HttpResponseMessage> GetRetryPolicy()
{
return HttpPolicyExtensions
.HandleTransientHttpError()
.WaitAndRetryAsync(
retryCount: 3,
sleepDurationProvider: retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt))
);
}
Аутентификация и авторизация
JWT Bearer Token
// Конфигурация JWT
services.AddAuthentication("Bearer")
.AddJwtBearer("Bearer", options =>
{
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true,
ValidateAudience = true,
ValidateLifetime = true,
ValidateIssuerSigningKey = true,
ValidIssuer = "service-shop",
ValidAudience = "http://localhost:8080",
IssuerSigningKey = new SymmetricSecurityKey(
Encoding.UTF8.GetBytes("secret-key")
)
};
});
// Политики авторизации
services.AddAuthorization(options =>
{
options.AddPolicy("AdminOnly", policy =>
policy.RequireRole("Admin"));
options.AddPolicy("MerchantAccess", policy =>
policy.RequireRole("Merchant", "Admin"));
});
Мониторинг и наблюдаемость
Sentry - Отслеживание ошибок
// Конфигурация Sentry
services.AddSentry(options =>
{
options.Dsn = "https://sentry-dsn";
options.Debug = false;
options.TracesSampleRate = 1.0;
});
Serilog - Логирование
// Конфигурация Serilog
Log.Logger = new LoggerConfiguration()
.ReadFrom.Configuration(configuration)
.WriteTo.Console()
.WriteTo.Sentry()
.CreateLogger();
// Структурированные логи
logger.Information("Product created with {ProductId} for {UserId}",
productId, userId);
OpenTelemetry - Метрики
// Конфигурация OpenTelemetry
services.AddOpenTelemetry()
.WithTracing(tracing => tracing
.AddAspNetCoreInstrumentation()
.AddHttpClientInstrumentation()
.AddSource("Alifshop.API")
)
.WithMetrics(metrics => metrics
.AddAspNetCoreInstrumentation()
.AddHttpClientInstrumentation()
.AddPrometheusExporter()
);
Дополнительные библиотеки
Валидация - FluentValidation
public class CreateProductCommandValidator : AbstractValidator<CreateProductCommand>
{
public CreateProductCommandValidator()
{
RuleFor(x => x.Name)
.NotEmpty()
.MaximumLength(200);
RuleFor(x => x.Price)
.GreaterThan(0)
.LessThan(1000000);
}
}
Спецификации - Ardalis.Specification
public class ActiveProductsSpecification : Specification<Product>
{
public ActiveProductsSpecification()
{
Query.Where(p => p.IsActive && p.Stock > 0);
}
}
Задачи - Quartz.NET
// Фоновая задача
public class UpdateInventoryJob : IJob
{
public async Task Execute(IJobExecutionContext context)
{
// Синхронизация остатков
await _inventoryService.SyncInventoryAsync();
}
}
// Регистрация задачи
services.AddQuartz(q =>
{
q.AddJob<UpdateInventoryJob>(opts => opts.WithIdentity("UpdateInventory"));
q.AddTrigger(opts => opts
.ForJob("UpdateInventory")
.WithCronSchedule("0 0 * * * ?") // Каждый час
);
});
Развертывание
Docker
FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS base
WORKDIR /app
EXPOSE 80
EXPOSE 443
FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build
COPY . .
RUN dotnet publish "Alif.ServiceShop.API.csproj" -c Release -o /app/publish
FROM base AS final
WORKDIR /app
COPY --from=build /app/publish .
ENTRYPOINT ["dotnet", "Alif.ServiceShop.API.dll"]
Docker Compose
version: '3.8'
services:
app:
build: .
ports:
- "8080:80"
depends_on:
- mysql
- redis
- kafka
environment:
- ASPNETCORE_ENVIRONMENT=Production
mysql:
image: mysql:8.0
environment:
MYSQL_ROOT_PASSWORD: password
redis:
image: redis:7.0
kafka:
image: confluentinc/cp-kafka:latest
Такой технологический стек обеспечивает высокую производительность, надежность и возможность масштабирования системы под растущие нагрузки.