Skip to content

Services

Huy Nguyen edited this page Apr 5, 2025 · 2 revisions

Service Development

This guide will help you understand how to develop services for Mixcore CMS. Services contain the business logic of your application and are typically used by controllers and other services.

Service Types

1. Application Services

Application services orchestrate business processes:

public interface IOrderService
{
    Task<OrderResult> PlaceOrderAsync(OrderRequest request);
    Task<OrderStatus> GetOrderStatusAsync(int orderId);
    Task CancelOrderAsync(int orderId);
}

public class OrderService : IOrderService
{
    private readonly IOrderRepository _orderRepository;
    private readonly IPaymentService _paymentService;
    private readonly IInventoryService _inventoryService;

    public OrderService(
        IOrderRepository orderRepository,
        IPaymentService paymentService,
        IInventoryService inventoryService)
    {
        _orderRepository = orderRepository;
        _paymentService = paymentService;
        _inventoryService = inventoryService;
    }

    public async Task<OrderResult> PlaceOrderAsync(OrderRequest request)
    {
        // 1. Validate inventory
        var inventoryStatus = await _inventoryService.CheckAvailabilityAsync(request.Items);
        if (!inventoryStatus.IsAvailable)
        {
            return OrderResult.Failed("Items not available");
        }

        // 2. Process payment
        var paymentResult = await _paymentService.ProcessPaymentAsync(request.Payment);
        if (!paymentResult.Success)
        {
            return OrderResult.Failed("Payment failed");
        }

        // 3. Create order
        var order = new Order
        {
            Items = request.Items,
            PaymentId = paymentResult.PaymentId,
            Status = OrderStatus.Pending
        };

        await _orderRepository.AddAsync(order);

        // 4. Update inventory
        await _inventoryService.UpdateInventoryAsync(request.Items);

        return OrderResult.Success(order.Id);
    }
}

2. Domain Services

Domain services contain core business logic:

public interface IDiscountCalculator
{
    decimal CalculateDiscount(Order order, Customer customer);
}

public class DiscountCalculator : IDiscountCalculator
{
    public decimal CalculateDiscount(Order order, Customer customer)
    {
        decimal discount = 0;

        // Loyalty discount
        if (customer.IsLoyaltyMember)
        {
            discount += order.Total * 0.1m; // 10% discount
        }

        // Bulk order discount
        if (order.Items.Count > 10)
        {
            discount += order.Total * 0.05m; // 5% discount
        }

        return discount;
    }
}

3. Infrastructure Services

Infrastructure services handle technical concerns:

public interface IEmailService
{
    Task SendEmailAsync(string to, string subject, string body);
}

public class SmtpEmailService : IEmailService
{
    private readonly SmtpClient _smtpClient;
    private readonly ILogger<EmailService> _logger;

    public SmtpEmailService(SmtpClient smtpClient, ILogger<EmailService> logger)
    {
        _smtpClient = smtpClient;
        _logger = logger;
    }

    public async Task SendEmailAsync(string to, string subject, string body)
    {
        try
        {
            var message = new MailMessage
            {
                To = { to },
                Subject = subject,
                Body = body
            };

            await _smtpClient.SendMailAsync(message);
            _logger.LogInformation("Email sent to {To}", to);
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Failed to send email to {To}", to);
            throw;
        }
    }
}

Service Registration

Register services in your module's ConfigureServices method:

public class Module : IModule
{
    public void ConfigureServices(IServiceCollection services)
    {
        // Application services
        services.AddScoped<IOrderService, OrderService>();
        
        // Domain services
        services.AddSingleton<IDiscountCalculator, DiscountCalculator>();
        
        // Infrastructure services
        services.AddSingleton<IEmailService, SmtpEmailService>();
    }
}

Service Configuration

Configure services in appsettings.json:

{
  "Email": {
    "SmtpServer": "smtp.example.com",
    "Port": 587,
    "Username": "user@example.com",
    "Password": "password"
  },
  "Order": {
    "MaxItems": 100,
    "DefaultCurrency": "USD"
  }
}

Service Dependencies

Services can depend on other services:

public class OrderProcessingService : IOrderProcessingService
{
    private readonly IOrderService _orderService;
    private readonly IInventoryService _inventoryService;
    private readonly IShippingService _shippingService;

    public OrderProcessingService(
        IOrderService orderService,
        IInventoryService inventoryService,
        IShippingService shippingService)
    {
        _orderService = orderService;
        _inventoryService = inventoryService;
        _shippingService = shippingService;
    }
}

Service Lifecycle

Services can have different lifetimes:

public class Module : IModule
{
    public void ConfigureServices(IServiceCollection services)
    {
        // Transient: New instance for each request
        services.AddTransient<ITransientService, TransientService>();
        
        // Scoped: Same instance within a request
        services.AddScoped<IScopedService, ScopedService>();
        
        // Singleton: Single instance for the application lifetime
        services.AddSingleton<ISingletonService, SingletonService>();
    }
}

Service Testing

Test services using unit tests:

public class OrderServiceTests
{
    private readonly Mock<IOrderRepository> _orderRepositoryMock;
    private readonly Mock<IPaymentService> _paymentServiceMock;
    private readonly Mock<IInventoryService> _inventoryServiceMock;
    private readonly OrderService _orderService;

    public OrderServiceTests()
    {
        _orderRepositoryMock = new Mock<IOrderRepository>();
        _paymentServiceMock = new Mock<IPaymentService>();
        _inventoryServiceMock = new Mock<IInventoryService>();
        
        _orderService = new OrderService(
            _orderRepositoryMock.Object,
            _paymentServiceMock.Object,
            _inventoryServiceMock.Object);
    }

    [Fact]
    public async Task PlaceOrderAsync_SuccessfulOrder_ReturnsSuccess()
    {
        // Arrange
        var request = new OrderRequest
        {
            Items = new List<OrderItem> { new OrderItem { Id = 1, Quantity = 2 } },
            Payment = new PaymentInfo { Amount = 100 }
        };

        _inventoryServiceMock.Setup(x => x.CheckAvailabilityAsync(It.IsAny<List<OrderItem>>()))
            .ReturnsAsync(new InventoryStatus { IsAvailable = true });

        _paymentServiceMock.Setup(x => x.ProcessPaymentAsync(It.IsAny<PaymentInfo>()))
            .ReturnsAsync(new PaymentResult { Success = true, PaymentId = "123" });

        // Act
        var result = await _orderService.PlaceOrderAsync(request);

        // Assert
        Assert.True(result.Success);
        _orderRepositoryMock.Verify(x => x.AddAsync(It.IsAny<Order>()), Times.Once);
    }
}

Best Practices

  1. Interface Segregation

    • Keep interfaces focused
    • Avoid large interfaces
    • Use multiple interfaces if needed
  2. Dependency Injection

    • Use constructor injection
    • Avoid service locator pattern
    • Keep constructors simple
  3. Error Handling

    • Use custom exceptions
    • Log errors appropriately
    • Provide meaningful error messages
  4. Performance

    • Use async/await
    • Implement caching
    • Optimize database calls

Next Steps

Need Help?

Clone this wiki locally