Application Services
Complete documentation of 32+ application services with methods, DTOs, and usage examples
Application Layer Overview
Application services are the orchestrators of business logic in the Shirinzad platform. They sit between the presentation layer (API controllers) and the domain layer (entities and domain services), coordinating use cases and managing transactions.
ApplicationService base class and implement interface contracts defined in the Application.Contracts project.
Key Responsibilities
- Orchestrate domain logic and business workflows
- Handle DTO mapping between entities and API responses
- Manage transactions and unit of work
- Implement authorization and permission checks
- Integrate with caching, logging, and cross-cutting concerns
- Validate input and handle exceptions gracefully
Service Statistics
Service Architecture
Service Flow Diagram
Services by Category
Catalog Services (7 Services)
Manage products, categories, brands, tags, reviews, wishlists, and product comparison.
| Service | Key Methods | Description |
|---|---|---|
| ProductAppService |
GetAsync(id)GetListAsync(input)GetBySlugAsync(slug)CreateAsync(input)UpdateAsync(id, input)DeleteAsync(id)GetFeaturedAsync()IncrementViewCountAsync(id)
|
Complete product CRUD with caching, view tracking, featured products, slug-based lookup |
| CategoryAppService |
GetAsync(id)GetListAsync(input)GetTreeAsync()CreateAsync(input)UpdateAsync(id, input)DeleteAsync(id)
|
Hierarchical category management with parent-child relationships, tree structure retrieval |
| BrandAppService |
GetAsync(id)GetListAsync(input)CreateAsync(input)UpdateAsync(id, input)DeleteAsync(id)
|
Brand/manufacturer management |
| TagAppService |
GetAsync(id)GetListAsync(input)CreateAsync(input)UpdateAsync(id, input)DeleteAsync(id)GetPopularAsync(count)
|
Tag management with popular tags retrieval |
| ProductReviewAppService |
GetListByProductAsync(productId)CreateAsync(input)ApproveAsync(id)RejectAsync(id)AddReplyAsync(reviewId, input)LikeReviewAsync(reviewId)
|
Product reviews with moderation, replies, likes, image uploads |
| WishlistAppService |
GetMyWishlistsAsync()GetDefaultWishlistAsync()CreateWishlistAsync(input)AddItemAsync(input)RemoveItemAsync(itemId)MoveItemAsync(itemId, targetWishlistId)
|
Multiple wishlists per user, default wishlist, item management |
| ComparisonAppService |
GetMyComparisonListAsync()AddProductAsync(productId)RemoveProductAsync(productId)ClearAsync()CompareProductsAsync(productIds)
|
Product comparison feature, side-by-side specification comparison |
| ProductSearchAppService |
SearchAsync(query)AdvancedSearchAsync(filter)GetSuggestionsAsync(term)FilterByCategoryAsync(categoryId, filter)
|
Advanced product search with filters, auto-suggestions, category filtering |
Order Services (5 Services)
Shopping cart, checkout, order management, payments, and shipments.
| Service | Key Methods | Description |
|---|---|---|
| CartAppService |
GetMyCartAsync()AddItemAsync(input)UpdateQuantityAsync(itemId, quantity)RemoveItemAsync(itemId)ClearCartAsync()ApplyDiscountCodeAsync(code)CalculateTotalsAsync()
|
Shopping cart management for guests and registered users, discount codes, total calculation |
| OrderAppService |
GetAsync(id)GetByOrderNumberAsync(orderNumber)GetUserOrdersAsync(input)CreateFromCartAsync(input)UpdateStatusAsync(id, status)CancelOrderAsync(id, reason)GetOrderStatisticsAsync()
|
Complete order lifecycle management, status updates, order history, cancellations |
| DiscountAppService |
GetAsync(id)GetListAsync(input)CreateAsync(input)UpdateAsync(id, input)DeleteAsync(id)ValidateDiscountCodeAsync(code)CalculateDiscountAsync(code, subtotal)
|
Discount and promo code management, validation, calculation |
| PaymentAppService |
InitiatePaymentAsync(orderId, methodId)VerifyPaymentAsync(transactionId)GetPaymentStatusAsync(paymentId)RequestRefundAsync(paymentId, reason)GetPaymentMethodsAsync()
|
Payment gateway integration, transaction verification, refund processing |
| ShipmentAppService |
CreateShipmentAsync(orderId, input)GetShipmentAsync(id)UpdateStatusAsync(id, status)AssignTrackingNumberAsync(id, tracking)TrackShipmentAsync(trackingNumber)GetShippingMethodsAsync()
|
Shipment creation, tracking, status updates, carrier integration |
User & Identity Services (3 Services)
User profiles, addresses, and authentication tracking.
| Service | Key Methods | Description |
|---|---|---|
| UserProfileAppService |
GetMyProfileAsync()UpdateMyProfileAsync(input)UploadAvatarAsync(file)ChangePasswordAsync(input)GetProfileStatisticsAsync()
|
User profile management, avatar uploads, password changes, user statistics |
| UserAddressAppService |
GetMyAddressesAsync()GetDefaultAddressAsync()CreateAsync(input)UpdateAsync(id, input)DeleteAsync(id)SetDefaultAsync(id)
|
Manage user shipping/billing addresses, default address selection |
| AccountLockoutService |
RecordLoginAttemptAsync(userId, isSuccessful)IsAccountLockedAsync(userId)GetFailedAttemptsAsync(userId)UnlockAccountAsync(userId)
|
Track login attempts, account lockout after failed attempts, security monitoring |
Notification Services (2 Services)
In-app notifications, email, SMS, and user preferences.
| Service | Key Methods | Description |
|---|---|---|
| NotificationAppService |
GetMyNotificationsAsync(input)GetUnreadCountAsync()MarkAsReadAsync(id)MarkAllAsReadAsync()DeleteAsync(id)SendNotificationAsync(input)
|
User notifications (in-app), read/unread status, notification management |
| EmailNotificationService |
SendOrderConfirmationAsync(orderId)SendShippingNotificationAsync(shipmentId)SendPaymentReceiptAsync(paymentId)SendWelcomeEmailAsync(userId)SendPasswordResetAsync(userId, token)
|
Transactional emails using templates, order confirmations, shipping updates |
Infrastructure Services (8 Services)
Caching, file storage, auditing, reporting, and system health.
| Service | Key Methods | Description |
|---|---|---|
| CacheService |
GetAsync(key)GetOrCreateAsync(key, factory, expiration)SetAsync(key, value, expiration)RemoveAsync(key)RemoveByPrefixAsync(prefix)
|
Redis distributed caching abstraction with TTL management |
| CacheInvalidationService |
InvalidateProductCacheAsync(productId)InvalidateCategoryCacheAsync(categoryId)InvalidateUserCacheAsync(userId)InvalidateAllCacheAsync()
|
Smart cache invalidation based on entity changes |
| CacheWarmingService |
WarmFeaturedProductsAsync()WarmCategoriesAsync()WarmPopularProductsAsync()WarmAllAsync()
|
Preload frequently accessed data into cache on startup |
| FileStorageAppService |
UploadAsync(file, category)UploadMultipleAsync(files, category)GetAsync(id)DeleteAsync(id)GenerateThumbnailAsync(fileId)
|
File upload management, thumbnail generation, local/cloud storage support |
| AuditLogAppService |
GetListAsync(filter)GetByUserAsync(userId, input)GetByEntityAsync(entityType, entityId)LogActionAsync(actionType, entityType, entityId)
|
Comprehensive audit trail, user actions, entity changes tracking |
| DashboardAppService |
GetStatisticsAsync()GetRevenueChartAsync(days)GetTopProductsAsync(count)GetRecentOrdersAsync(count)GetCustomerInsightsAsync()
|
Admin dashboard statistics, charts, insights, KPIs |
| ReportAppService |
GenerateSalesReportAsync(filter)GenerateProductReportAsync(filter)GenerateCustomerReportAsync(filter)ExportToCsvAsync(reportType, filter)ExportToExcelAsync(reportType, filter)
|
Business reports generation, export to CSV/Excel |
| ShopHealthCheckService |
CheckDatabaseAsync()CheckRedisAsync()CheckExternalServicesAsync()GetSystemStatusAsync()
|
System health monitoring, database/cache/API availability checks |
Security & Validation Services (3 Services)
Rate limiting, input validation, and security monitoring.
| Service | Key Methods | Description |
|---|---|---|
| RateLimitService |
CheckRateLimitAsync(key, maxRequests, window)IncrementCounterAsync(key)GetRemainingRequestsAsync(key)ResetLimitAsync(key)
|
API rate limiting, DDoS protection, request throttling |
| FileValidationService |
ValidateImageAsync(file)ValidateFileSizeAsync(file, maxSize)ValidateFileTypeAsync(file, allowedTypes)ScanForMalwareAsync(file)
|
File upload validation, size limits, type restrictions, security scanning |
| GoogleRecaptchaService |
VerifyAsync(token)ValidateScoreAsync(token, minScore)GetCaptchaScoreAsync(token)
|
Google reCAPTCHA v3 integration for bot protection |
Configuration Service (1 Service)
| Service | Key Methods | Description |
|---|---|---|
| ShirinzadConfigAppService |
GetConfigAsync()UpdateConfigAsync(input)GetPublicConfigAsync()UpdateLogoAsync(file)UpdateFaviconAsync(file)
|
Site-wide configuration management, contact info, social media, SEO settings |
DTO Mapping with AutoMapper
AutoMapper Configuration
All services use AutoMapper for entity-to-DTO mapping. Mapping profiles are defined in the Application project:
// Example AutoMapper Profile
public class ShopApplicationAutoMapperProfile : Profile
{
public ShopApplicationAutoMapperProfile()
{
// Product mappings
CreateMap<Product, ProductDto>()
.ForMember(d => d.BrandName, opt => opt.MapFrom(s => s.Brand.Name))
.ForMember(d => d.CategoryIds, opt => opt.Ignore())
.ForMember(d => d.CategoryNames, opt => opt.Ignore());
CreateMap<CreateProductDto, Product>()
.ForMember(d => d.Id, opt => opt.Ignore());
CreateMap<UpdateProductDto, Product>()
.ForMember(d => d.Id, opt => opt.Ignore());
// Order mappings
CreateMap<Order, OrderDto>()
.ForMember(d => d.Items, opt => opt.MapFrom(s => s.Items));
CreateMap<OrderItem, OrderItemDto>();
CreateMap<CreateOrderDto, Order>()
.ForMember(d => d.Id, opt => opt.Ignore())
.ForMember(d => d.OrderNumber, opt => opt.Ignore());
}
}
Common DTO Patterns
| DTO Type | Purpose | Example |
|---|---|---|
| EntityDto | Output - return entity data | ProductDto, OrderDto |
| CreateDto | Input - create new entity | CreateProductDto, CreateOrderDto |
| UpdateDto | Input - update existing entity | UpdateProductDto, UpdateOrderDto |
| ListDto | Output - simplified list items | ProductListDto, OrderSummaryDto |
| FilterDto | Input - search/filter parameters | ProductFilterDto, OrderFilterDto |
Service Implementation Examples
Example 1: ProductAppService - Get with Caching
public class ProductAppService : ApplicationService, IProductAppService
{
private readonly IRepository<Product, Guid> _productRepository;
private readonly IRepository<Brand, Guid> _brandRepository;
private readonly CacheService _cacheService;
public async Task<ProductDto> GetAsync(Guid id)
{
// Try cache first (2-hour TTL)
var cacheKey = CacheKeys.GetProductDetailsKey(id);
var cachedDto = await _cacheService.GetOrCreateAsync(
cacheKey,
async () => await GetProductDtoAsync(id),
TimeSpan.FromHours(2)
);
return cachedDto;
}
private async Task<ProductDto> GetProductDtoAsync(Guid id)
{
var product = await _productRepository.GetAsync(id);
var dto = ObjectMapper.Map<Product, ProductDto>(product);
// Load related data
if (product.BrandId.HasValue)
{
var brand = await _brandRepository.FindAsync(product.BrandId.Value);
dto.BrandName = brand?.Name;
}
return dto;
}
public async Task<ProductDto> CreateAsync(CreateProductDto input)
{
// Validate slug uniqueness
var existingProduct = await _productRepository
.FirstOrDefaultAsync(x => x.Slug == input.Slug);
if (existingProduct != null)
{
throw new UserFriendlyException("این نام مستعار قبلاً استفاده شده است");
}
// Create entity
var product = new Product(GuidGenerator.Create(), input.Name, input.Slug);
product.Price = input.Price;
product.Description = input.Description;
// ... set other properties
await _productRepository.InsertAsync(product);
// Invalidate related caches
await _cacheInvalidationService.InvalidateProductCacheAsync(product.Id);
return ObjectMapper.Map<Product, ProductDto>(product);
}
}
Example 2: OrderAppService - Create from Cart
public class OrderAppService : ApplicationService, IOrderAppService
{
private readonly IRepository<Order, Guid> _orderRepository;
private readonly IRepository<Cart, Guid> _cartRepository;
private readonly IRepository<Product, Guid> _productRepository;
private readonly ICurrentUser _currentUser;
public async Task<OrderDto> CreateFromCartAsync(CreateOrderFromCartDto input)
{
// Authorize
if (!_currentUser.IsAuthenticated)
{
throw new UserFriendlyException("لطفاً وارد حساب کاربری خود شوید");
}
// Get user's cart
var cart = await _cartRepository
.Include(c => c.Items)
.FirstOrDefaultAsync(c => c.UserId == _currentUser.Id && c.IsActive);
if (cart == null || !cart.Items.Any())
{
throw new UserFriendlyException("سبد خرید شما خالی است");
}
// Validate stock availability
foreach (var item in cart.Items)
{
var product = await _productRepository.GetAsync(item.ProductId);
if (product.StockQuantity < item.Quantity)
{
throw new UserFriendlyException($"محصول {product.Name} موجود نیست");
}
}
// Create order
var orderNumber = await GenerateOrderNumberAsync();
var order = new Order(GuidGenerator.Create(), orderNumber);
order.UserId = _currentUser.Id;
order.CustomerName = input.CustomerName;
order.CustomerEmail = input.CustomerEmail;
order.CustomerPhone = input.CustomerPhone;
order.ShippingAddress = input.ShippingAddress;
order.Subtotal = cart.Subtotal;
order.Total = cart.Total;
order.Status = OrderStatus.Pending;
// Add order items from cart
foreach (var cartItem in cart.Items)
{
var orderItem = new OrderItem(GuidGenerator.Create());
orderItem.OrderId = order.Id;
orderItem.ProductId = cartItem.ProductId;
orderItem.ProductName = cartItem.ProductName;
orderItem.Quantity = cartItem.Quantity;
orderItem.UnitPrice = cartItem.Price;
orderItem.LineTotal = cartItem.Quantity * cartItem.Price;
order.Items.Add(orderItem);
// Decrease product stock
var product = await _productRepository.GetAsync(cartItem.ProductId);
product.DecreaseStock(cartItem.Quantity);
await _productRepository.UpdateAsync(product);
}
await _orderRepository.InsertAsync(order);
// Clear cart
cart.IsActive = false;
await _cartRepository.UpdateAsync(cart);
// Send notification
await _notificationService.SendOrderConfirmationAsync(order.Id);
return ObjectMapper.Map<Order, OrderDto>(order);
}
private async Task<string> GenerateOrderNumberAsync()
{
// Generate unique order number: ORD-20251004-0001
var date = DateTime.Now.ToString("yyyyMMdd");
var count = await _orderRepository
.CountAsync(o => o.OrderNumber.StartsWith($"ORD-{date}"));
return $"ORD-{date}-{(count + 1):D4}";
}
}
Example 3: CartAppService - Apply Discount
public async Task<CartDto> ApplyDiscountCodeAsync(string code)
{
// Get user's cart
var cart = await GetOrCreateCartAsync();
// Validate discount code
var discount = await _discountRepository
.FirstOrDefaultAsync(d => d.Code == code && d.IsActive);
if (discount == null)
{
throw new UserFriendlyException("کد تخفیف معتبر نیست");
}
// Check date range
if (discount.StartDate > DateTime.Now || discount.EndDate < DateTime.Now)
{
throw new UserFriendlyException("کد تخفیف منقضی شده است");
}
// Check minimum order amount
if (discount.MinimumOrderAmount.HasValue &&
cart.Subtotal < discount.MinimumOrderAmount.Value)
{
throw new UserFriendlyException(
$"حداقل مبلغ سفارش برای این کد تخفیف {discount.MinimumOrderAmount:N0} تومان است"
);
}
// Calculate discount amount
decimal discountAmount = 0;
if (discount.DiscountType == DiscountType.Percentage)
{
discountAmount = cart.Subtotal * (discount.DiscountValue / 100);
if (discount.MaximumDiscountAmount.HasValue)
{
discountAmount = Math.Min(discountAmount, discount.MaximumDiscountAmount.Value);
}
}
else
{
discountAmount = discount.DiscountValue;
}
// Apply discount
cart.DiscountCode = code;
cart.DiscountAmount = discountAmount;
cart.Total = cart.Subtotal - discountAmount;
await _cartRepository.UpdateAsync(cart);
// Invalidate cache
await _cacheInvalidationService.InvalidateUserCacheAsync(_currentUser.Id.Value);
return ObjectMapper.Map<Cart, CartDto>(cart);
}
Service Development Best Practices
Key Guidelines
Do's
- Always use async/await for database operations
- Use DTOs for all inputs and outputs
- Implement proper authorization checks
- Validate inputs before processing
- Use caching for frequently accessed data
- Invalidate cache when data changes
- Log important operations and errors
- Return user-friendly error messages (Persian)
- Use transactions for multi-step operations
- Follow single responsibility principle
Don'ts
- Don't expose entities directly to API
- Don't perform business logic in controllers
- Don't use synchronous database calls
- Don't forget to check permissions
- Don't return generic error messages
- Don't perform N+1 queries (use Include)
- Don't cache user-specific data globally
- Don't bypass validation for "convenience"
- Don't ignore exception handling
- Don't create god services (keep focused)
Performance Optimization
| Technique | Implementation | Benefit |
|---|---|---|
| Caching | Use CacheService.GetOrCreateAsync() for expensive queries |
80-95% response time reduction |
| Projections | Use Select() to fetch only needed columns |
40-60% memory reduction |
| AsNoTracking | Add .AsNoTracking() for read-only queries |
20-30% faster queries |
| Eager Loading | Use Include() to avoid N+1 queries |
Prevent multiple round-trips to database |
| Pagination | Always use Skip()/Take() for lists |
Prevent loading thousands of records |
| Batch Operations | Use InsertManyAsync() for bulk inserts |
10x faster than individual inserts |