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) между разработчиками и бизнесом
- Четкое разделение ответственности
Инкапсуляция логики
- Бизнес-правила находятся в доменном слое
- Защита от некорректных состояний
- Упрощение тестирования
Масштабируемость
- Независимые контексты
- Возможность отдельной эволюции модулей
- Простота добавления новых контекстов
Такая архитектура позволяет команде разработчиков эффективно работать с бизнес-логикой, обеспечивая высокое качество кода и соответствие требованиям предметной области.