Domain-Driven Design

Alifshop API построен с использованием принципов Domain-Driven Design (DDD), что обеспечивает четкое разделение бизнес-логики и технических деталей.

Bounded Contexts

Система разделена на 6 ограниченных контекстов, каждый из которых представляет отдельную предметную область:

Catalog Context

Предметная область: Управление товарами и каталогом

  • Сущности: Product, Category, Brand, Price
  • Агрегаты: ProductAggregate
  • Доменные сервисы: PriceCalculationService
  • Репозитории: IProductRepository, ICategoryRepository

Cart Context

Предметная область: Корзина покупок

  • Сущности: CartItem, Cart
  • Агрегаты: CartAggregate
  • Доменные события: ItemAddedToCart, CartCleared
  • Репозитории: ICartRepository

Orders Context

Предметная область: Управление заказами

  • Сущности: Order, OrderItem, Delivery
  • Агрегаты: OrderAggregate
  • Доменные события: OrderCreated, OrderStatusChanged
  • Репозитории: IOrderRepository

Marketing Context

Предметная область: Маркетинг и промо-акции

  • Сущности: Promotion, Discount, Campaign
  • Агрегаты: PromotionAggregate
  • Доменные сервисы: DiscountCalculationService
  • Репозитории: IPromotionRepository

Moderation Context

Предметная область: Модерация контента

  • Сущности: Review, ModerationResult
  • Агрегаты: ReviewAggregate
  • Доменные события: ReviewSubmitted, ReviewApproved
  • Репозитории: IReviewRepository

Afghanistan Context

Предметная область: Региональная специфика

  • Сущности: Region, LocalizedContent
  • Агрегаты: RegionAggregate
  • Доменные сервисы: LocalizationService
  • Репозитории: IRegionRepository

Структура доменного слоя

Каждый контекст следует единой структуре:

Alif.ServiceShop.Modules.[Context].Domain/
├── Entities/           # Доменные сущности
├── Aggregates/         # Агрегаты
├── ValueObjects/       # Объекты-значения
├── DomainEvents/       # Доменные события
├── Services/           # Доменные сервисы
├── Repositories/       # Интерфейсы репозиториев
└── Specifications/     # Спецификации

Доменные сущности

Пример: Product Entity

public class Product : Entity
{
    public ProductId Id { get; private set; }
    public string Name { get; private set; }
    public string Description { get; private set; }
    public Money Price { get; private set; }
    public CategoryId CategoryId { get; private set; }
    
    // Доменные методы
    public void UpdatePrice(Money newPrice)
    {
        // Бизнес-логика валидации
        if (newPrice.Amount <= 0)
            throw new InvalidPriceException();
            
        Price = newPrice;
        AddDomainEvent(new PriceUpdatedEvent(Id, newPrice));
    }
}

Агрегаты

Пример: Cart Aggregate

public class CartAggregate : AggregateRoot
{
    private readonly List<CartItem> _items = new();
    
    public void AddItem(ProductId productId, int quantity)
    {
        var existingItem = _items.FirstOrDefault(x => x.ProductId == productId);
        
        if (existingItem != null)
        {
            existingItem.UpdateQuantity(existingItem.Quantity + quantity);
        }
        else
        {
            _items.Add(new CartItem(productId, quantity));
        }
        
        AddDomainEvent(new ItemAddedToCartEvent(Id, productId, quantity));
    }
}

Доменные события

События инкапсулируют важные бизнес-моменты:

public class OrderCreatedEvent : DomainEvent
{
    public OrderId OrderId { get; }
    public CustomerId CustomerId { get; }
    public Money TotalAmount { get; }
    
    public OrderCreatedEvent(OrderId orderId, CustomerId customerId, Money totalAmount)
    {
        OrderId = orderId;
        CustomerId = customerId;
        TotalAmount = totalAmount;
    }
}

Доменные сервисы

Сервисы инкапсулируют бизнес-логику, которая не относится к конкретной сущности:

public class PriceCalculationService
{
    public Money CalculateDiscountedPrice(Money originalPrice, Discount discount)
    {
        if (discount.Type == DiscountType.Percentage)
        {
            var discountAmount = originalPrice.Amount * discount.Value / 100;
            return new Money(originalPrice.Amount - discountAmount, originalPrice.Currency);
        }
        
        return new Money(originalPrice.Amount - discount.Value, originalPrice.Currency);
    }
}

Спецификации

Используются для инкапсуляции бизнес-правил запросов:

public class ActiveProductsSpecification : Specification<Product>
{
    public override Expression<Func<Product, bool>> ToExpression()
    {
        return product => product.IsActive && product.Stock > 0;
    }
}

Преимущества DDD в проекте

Ясность модели

  • Доменная модель отражает реальные бизнес-процессы
  • Единый язык (Ubiquitous Language) между разработчиками и бизнесом
  • Четкое разделение ответственности

Инкапсуляция логики

  • Бизнес-правила находятся в доменном слое
  • Защита от некорректных состояний
  • Упрощение тестирования

Масштабируемость

  • Независимые контексты
  • Возможность отдельной эволюции модулей
  • Простота добавления новых контекстов

Такая архитектура позволяет команде разработчиков эффективно работать с бизнес-логикой, обеспечивая высокое качество кода и соответствие требованиям предметной области.