# Business Rules and Logic Patterns - Centron Enterprise Application

## Overview

This document provides comprehensive coverage of business logic patterns and implementations within the Centron .NET 8 enterprise application. Business rules encompass decision logic, workflow patterns, process orchestration, state management, and calculation patterns that enforce business constraints and processes.

## Business Logic Architecture

### **Core Business Logic Patterns**

#### **1. Business Logic Layer (BL) Pattern**
The foundation of all business rule implementation follows a consistent 2-layer architecture:

```csharp
// Base BL Class - Core Business Logic
public class AccountBL : BaseBL
{
    public Result<Account> SaveAccount(Account account)
    {
        // Business rule validation
        if (account.CustomerNumber <= 0)
            return Result<Account>.AsError("Customer number must be positive");

        // Business logic execution
        var validationResult = ValidateAccountBusinessRules(account);
        if (validationResult.Status != ResultStatus.Success)
            return Result<Account>.FromResult(validationResult);

        // Data persistence
        return _accountDAO.SaveAccount(account);
    }

    private Result ValidateAccountBusinessRules(Account account)
    {
        // Complex business rule validation logic
        if (account.CreditLimit < 0 && !account.IsSpecialCustomer)
            return Result.AsError("Negative credit limit only allowed for special customers");

        return Result.AsSuccess();
    }
}

// WebService BL Class - DTO Conversion + Business Logic
public class AccountWebServiceBL : BaseBL
{
    private readonly AccountBL _accountBL;

    public AccountWebServiceBL()
    {
        _accountBL = new AccountBL();
    }

    public Result<AccountDTO> SaveAccount(AccountDTO accountDTO)
    {
        // Convert DTO to Entity
        var account = ConvertAccountDTOToAccount(accountDTO);

        // Execute business logic
        var result = _accountBL.SaveAccount(account);

        // Convert back to DTO
        return result.Status == ResultStatus.Success
            ? Result<AccountDTO>.AsSuccess(ObjectMapper.Map<AccountDTO>(result.Data))
            : Result<AccountDTO>.FromResult(result);
    }
}
```

## Decision Logic and Rule Implementations

### **1. Rule Engine Pattern**

#### **Business Rule Validation Chain**
```csharp
public class BusinessRuleValidator<T>
{
    private readonly List<IBusinessRule<T>> _rules = new();

    public BusinessRuleValidator<T> AddRule(IBusinessRule<T> rule)
    {
        _rules.Add(rule);
        return this;
    }

    public Result ValidateAll(T entity)
    {
        foreach (var rule in _rules)
        {
            var result = rule.Validate(entity);
            if (result.Status != ResultStatus.Success)
                return result;
        }
        return Result.AsSuccess();
    }
}

// Example Business Rule Implementation
public class CustomerCreditLimitRule : IBusinessRule<Account>
{
    public Result Validate(Account account)
    {
        if (account.CreditLimit > 100000 && account.PaymentTerms > 30)
            return Result.AsError("High credit limit requires payment terms <= 30 days");

        return Result.AsSuccess();
    }
}

// Usage in Business Logic
public Result<Account> ProcessAccountApproval(Account account)
{
    var validator = new BusinessRuleValidator<Account>()
        .AddRule(new CustomerCreditLimitRule())
        .AddRule(new CustomerTypeValidationRule())
        .AddRule(new RegionalComplianceRule());

    var validationResult = validator.ValidateAll(account);
    if (validationResult.Status != ResultStatus.Success)
        return Result<Account>.FromResult(validationResult);

    return ApproveAccount(account);
}
```

### **2. Conditional Business Logic Pattern**

#### **Strategy Pattern for Business Decisions**
```csharp
public interface IPricingStrategy
{
    Result<decimal> CalculatePrice(PricingContext context);
}

public class StandardPricingStrategy : IPricingStrategy
{
    public Result<decimal> CalculatePrice(PricingContext context)
    {
        var basePrice = context.Article.BasePrice;
        var discount = CalculateStandardDiscount(context.Customer);
        return Result<decimal>.AsSuccess(basePrice * (1 - discount));
    }
}

public class VipPricingStrategy : IPricingStrategy
{
    public Result<decimal> CalculatePrice(PricingContext context)
    {
        var basePrice = context.Article.BasePrice;
        var vipDiscount = CalculateVipDiscount(context.Customer, context.Volume);
        return Result<decimal>.AsSuccess(basePrice * (1 - vipDiscount));
    }
}

// Business Logic Using Strategy
public class PricingBL : BaseBL
{
    public Result<decimal> CalculateArticlePrice(int customerId, int articleId, int quantity)
    {
        var context = GetPricingContext(customerId, articleId, quantity);
        var strategy = GetPricingStrategy(context.Customer.CustomerType);

        return strategy.CalculatePrice(context);
    }

    private IPricingStrategy GetPricingStrategy(CustomerType customerType)
    {
        return customerType switch
        {
            CustomerType.Vip => new VipPricingStrategy(),
            CustomerType.Corporate => new CorporatePricingStrategy(),
            _ => new StandardPricingStrategy()
        };
    }
}
```

## Business Workflow Patterns

### **1. Process Orchestration Pattern**

#### **Receipt Processing Workflow**
```csharp
public class ReceiptProcessingWorkflow
{
    private readonly IReceiptLogic _receiptLogic;
    private readonly IInventoryLogic _inventoryLogic;
    private readonly IFinancialLogic _financialLogic;

    public async Task<Result<ProcessedReceipt>> ProcessReceipt(ReceiptDTO receipt)
    {
        // Step 1: Validate Receipt
        var validationResult = await ValidateReceiptBusinessRules(receipt);
        if (validationResult.Status != ResultStatus.Success)
            return Result<ProcessedReceipt>.FromResult(validationResult);

        // Step 2: Reserve Inventory
        var reservationResult = await _inventoryLogic.ReserveInventory(receipt.Items);
        if (reservationResult.Status != ResultStatus.Success)
        {
            await CompensateReservation(reservationResult.Data);
            return Result<ProcessedReceipt>.FromResult(reservationResult);
        }

        // Step 3: Process Payment
        var paymentResult = await _financialLogic.ProcessPayment(receipt.PaymentInfo);
        if (paymentResult.Status != ResultStatus.Success)
        {
            await CompensateInventory(reservationResult.Data);
            return Result<ProcessedReceipt>.FromResult(paymentResult);
        }

        // Step 4: Finalize Receipt
        var finalResult = await _receiptLogic.FinalizeReceipt(receipt, paymentResult.Data);

        return Result<ProcessedReceipt>.AsSuccess(new ProcessedReceipt
        {
            Receipt = finalResult.Data,
            ReservationId = reservationResult.Data.ReservationId,
            PaymentId = paymentResult.Data.PaymentId
        });
    }

    private async Task<Result> ValidateReceiptBusinessRules(ReceiptDTO receipt)
    {
        // Business rule: Receipt total must match item totals
        var calculatedTotal = receipt.Items.Sum(i => i.Quantity * i.Price);
        if (Math.Abs(receipt.Total - calculatedTotal) > 0.01m)
            return Result.AsError("Receipt total does not match item totals");

        // Business rule: Customer credit limit check
        var customerResult = await _receiptLogic.GetCustomer(receipt.CustomerId);
        if (customerResult.Status == ResultStatus.Success)
        {
            var creditCheck = await CheckCreditLimit(customerResult.Data, receipt.Total);
            if (creditCheck.Status != ResultStatus.Success)
                return creditCheck;
        }

        return Result.AsSuccess();
    }
}
```

### **2. State Machine Implementation Pattern**

#### **Helpdesk Ticket State Management**
```csharp
public enum HelpdeskState
{
    Created,
    Assigned,
    InProgress,
    WaitingForCustomer,
    Resolved,
    Closed,
    Cancelled
}

public class HelpdeskStateMachine
{
    private static readonly Dictionary<HelpdeskState, List<HelpdeskState>> _validTransitions = new()
    {
        { HelpdeskState.Created, new() { HelpdeskState.Assigned, HelpdeskState.Cancelled } },
        { HelpdeskState.Assigned, new() { HelpdeskState.InProgress, HelpdeskState.Cancelled } },
        { HelpdeskState.InProgress, new() { HelpdeskState.WaitingForCustomer, HelpdeskState.Resolved, HelpdeskState.Cancelled } },
        { HelpdeskState.WaitingForCustomer, new() { HelpdeskState.InProgress, HelpdeskState.Cancelled } },
        { HelpdeskState.Resolved, new() { HelpdeskState.Closed, HelpdeskState.InProgress } },
        { HelpdeskState.Closed, new() { HelpdeskState.InProgress } }, // Reopen capability
        { HelpdeskState.Cancelled, new() { HelpdeskState.Created } } // Reinstate capability
    };

    public Result<HelpdeskState> TransitionState(HelpdeskState currentState, HelpdeskState targetState, HelpdeskTransitionContext context)
    {
        // Validate state transition
        if (!_validTransitions.ContainsKey(currentState) ||
            !_validTransitions[currentState].Contains(targetState))
        {
            return Result<HelpdeskState>.AsError($"Invalid state transition from {currentState} to {targetState}");
        }

        // Apply business rules for transition
        var businessRuleResult = ValidateTransitionBusinessRules(currentState, targetState, context);
        if (businessRuleResult.Status != ResultStatus.Success)
            return Result<HelpdeskState>.FromResult(businessRuleResult);

        // Execute transition logic
        ExecuteTransitionLogic(currentState, targetState, context);

        return Result<HelpdeskState>.AsSuccess(targetState);
    }

    private Result ValidateTransitionBusinessRules(HelpdeskState from, HelpdeskState to, HelpdeskTransitionContext context)
    {
        switch (to)
        {
            case HelpdeskState.Resolved:
                if (string.IsNullOrEmpty(context.ResolutionNotes))
                    return Result.AsError("Resolution notes are required when resolving a ticket");
                break;

            case HelpdeskState.Assigned:
                if (context.AssignedToUserId <= 0)
                    return Result.AsError("Must specify assigned user when assigning ticket");
                break;

            case HelpdeskState.Closed:
                if (context.CustomerApproval == null || !context.CustomerApproval.Value)
                    return Result.AsError("Customer approval required before closing ticket");
                break;
        }

        return Result.AsSuccess();
    }
}

// Usage in Business Logic
public class HelpdeskBL : BaseBL
{
    private readonly HelpdeskStateMachine _stateMachine = new();

    public Result UpdateHelpdeskState(int helpdeskId, HelpdeskState targetState, HelpdeskTransitionContext context)
    {
        var helpdesk = GetHelpdesk(helpdeskId);
        if (helpdesk.Status != ResultStatus.Success)
            return Result.FromResult(helpdesk);

        var transitionResult = _stateMachine.TransitionState(helpdesk.Data.State, targetState, context);
        if (transitionResult.Status != ResultStatus.Success)
            return Result.FromResult(transitionResult);

        helpdesk.Data.State = transitionResult.Data;
        helpdesk.Data.LastModified = DateTime.UtcNow;

        return SaveHelpdesk(helpdesk.Data);
    }
}
```

## Business Calculation Patterns

### **1. Financial Calculation Engine**

#### **Complex Pricing Calculation Pattern**
```csharp
public class PricingCalculationEngine
{
    public Result<PricingResult> CalculateComplexPricing(PricingRequest request)
    {
        var calculator = new PricingCalculator()
            .WithBasePrice(request.Article.BasePrice)
            .WithQuantityDiscounts(request.QuantityBreaks)
            .WithCustomerDiscounts(request.Customer.DiscountTiers)
            .WithRegionalPricing(request.DeliveryRegion)
            .WithSeasonalAdjustments(request.CalculationDate)
            .WithVatCalculation(request.VatRate);

        return calculator.Calculate();
    }
}

public class PricingCalculator
{
    private decimal _basePrice;
    private List<QuantityDiscount> _quantityDiscounts = new();
    private List<CustomerDiscount> _customerDiscounts = new();
    private RegionalPricing _regionalPricing;
    private SeasonalAdjustment _seasonalAdjustment;
    private decimal _vatRate;

    public PricingCalculator WithBasePrice(decimal basePrice)
    {
        _basePrice = basePrice;
        return this;
    }

    public Result<PricingResult> Calculate()
    {
        try
        {
            // Step 1: Apply quantity discounts
            var netPrice = ApplyQuantityDiscounts(_basePrice);

            // Step 2: Apply customer discounts
            netPrice = ApplyCustomerDiscounts(netPrice);

            // Step 3: Apply regional pricing adjustments
            netPrice = ApplyRegionalAdjustments(netPrice);

            // Step 4: Apply seasonal adjustments
            netPrice = ApplySeasonalAdjustments(netPrice);

            // Step 5: Calculate VAT
            var vatAmount = netPrice * _vatRate;
            var grossPrice = netPrice + vatAmount;

            return Result<PricingResult>.AsSuccess(new PricingResult
            {
                BasePrice = _basePrice,
                NetPrice = netPrice,
                VatAmount = vatAmount,
                GrossPrice = grossPrice,
                DiscountApplied = _basePrice - netPrice,
                CalculationBreakdown = GetCalculationBreakdown()
            });
        }
        catch (Exception ex)
        {
            return Result<PricingResult>.AsError($"Pricing calculation failed: {ex.Message}");
        }
    }
}
```

### **2. Inventory Management Business Rules**

#### **Stock Level and Allocation Logic**
```csharp
public class InventoryBL : BaseBL
{
    public Result<AllocationResult> AllocateInventory(AllocationRequest request)
    {
        // Business Rule: Check available stock
        var stockResult = GetAvailableStock(request.ArticleId, request.WarehouseId);
        if (stockResult.Status != ResultStatus.Success)
            return Result<AllocationResult>.FromResult(stockResult);

        var availableStock = stockResult.Data;

        // Business Rule: Priority allocation logic
        var allocationStrategy = DetermineAllocationStrategy(request.Priority);
        var allocationResult = allocationStrategy.Allocate(availableStock, request);

        if (allocationResult.AllocatedQuantity < request.RequestedQuantity)
        {
            // Business Rule: Handle partial allocation
            var partialResult = HandlePartialAllocation(request, allocationResult);
            if (partialResult.Status != ResultStatus.Success)
                return Result<AllocationResult>.FromResult(partialResult);
        }

        // Business Rule: Update stock levels
        var updateResult = UpdateStockLevels(request.ArticleId, request.WarehouseId, allocationResult.AllocatedQuantity);
        if (updateResult.Status != ResultStatus.Success)
        {
            // Compensate allocation
            CompensateAllocation(allocationResult);
            return Result<AllocationResult>.FromResult(updateResult);
        }

        return Result<AllocationResult>.AsSuccess(allocationResult);
    }

    private IAllocationStrategy DetermineAllocationStrategy(AllocationPriority priority)
    {
        return priority switch
        {
            AllocationPriority.VipCustomer => new VipAllocationStrategy(),
            AllocationPriority.LargeOrder => new BulkAllocationStrategy(),
            AllocationPriority.Express => new ExpressAllocationStrategy(),
            _ => new StandardAllocationStrategy()
        };
    }
}
```

## Process Orchestration Patterns

### **1. Saga Pattern for Distributed Transactions**

#### **Order Processing Saga**
```csharp
public class OrderProcessingSaga
{
    private readonly List<ISagaStep> _steps = new();
    private readonly List<ISagaStep> _executedSteps = new();

    public OrderProcessingSaga()
    {
        _steps.Add(new ValidateOrderStep());
        _steps.Add(new ReserveInventoryStep());
        _steps.Add(new ProcessPaymentStep());
        _steps.Add(new CreateShipmentStep());
        _steps.Add(new SendConfirmationStep());
    }

    public async Task<Result<OrderProcessingResult>> ExecuteAsync(OrderProcessingContext context)
    {
        foreach (var step in _steps)
        {
            try
            {
                var stepResult = await step.ExecuteAsync(context);
                if (stepResult.Status != ResultStatus.Success)
                {
                    await CompensateAsync();
                    return Result<OrderProcessingResult>.FromResult(stepResult);
                }

                _executedSteps.Add(step);
                context.AddStepResult(step.Name, stepResult.Data);
            }
            catch (Exception ex)
            {
                await CompensateAsync();
                return Result<OrderProcessingResult>.AsError($"Saga failed at step {step.Name}: {ex.Message}");
            }
        }

        return Result<OrderProcessingResult>.AsSuccess(new OrderProcessingResult
        {
            OrderId = context.OrderId,
            ProcessingSteps = _executedSteps.Select(s => s.Name).ToList(),
            CompletedAt = DateTime.UtcNow
        });
    }

    private async Task CompensateAsync()
    {
        // Execute compensation in reverse order
        var stepsToCompensate = _executedSteps.AsEnumerable().Reverse();

        foreach (var step in stepsToCompensate)
        {
            try
            {
                await step.CompensateAsync();
            }
            catch (Exception ex)
            {
                // Log compensation failure but continue
                LogCompensationFailure(step.Name, ex);
            }
        }
    }
}

// Example Saga Step Implementation
public class ProcessPaymentStep : ISagaStep
{
    public string Name => "ProcessPayment";

    public async Task<Result> ExecuteAsync(OrderProcessingContext context)
    {
        var paymentResult = await ProcessPayment(context.Order.PaymentInfo);
        if (paymentResult.Status == ResultStatus.Success)
        {
            context.PaymentId = paymentResult.Data.PaymentId;
        }
        return paymentResult;
    }

    public async Task CompensateAsync()
    {
        // Reverse payment if possible
        if (context.PaymentId.HasValue)
        {
            await RefundPayment(context.PaymentId.Value);
        }
    }
}
```

## Rule Engine and Policy Patterns

### **1. Configurable Business Rules**

#### **Dynamic Rule Configuration**
```csharp
public class ConfigurableBusinessRuleEngine
{
    private readonly Dictionary<string, IBusinessRule> _rules = new();

    public void RegisterRule(string ruleId, IBusinessRule rule)
    {
        _rules[ruleId] = rule;
    }

    public Result<T> ApplyRules<T>(string context, T entity) where T : class
    {
        var applicableRules = GetApplicableRules(context);

        foreach (var rule in applicableRules)
        {
            var result = rule.Apply(entity);
            if (result.Status != ResultStatus.Success)
                return Result<T>.FromResult(result);

            entity = (T)result.Data;
        }

        return Result<T>.AsSuccess(entity);
    }

    private IEnumerable<IBusinessRule> GetApplicableRules(string context)
    {
        return _rules.Values.Where(r => r.AppliesTo(context));
    }
}

// Example: Customer Validation Rules
public class CustomerCreditCheckRule : IBusinessRule<Customer>
{
    private readonly decimal _minimumCreditScore;

    public CustomerCreditCheckRule(decimal minimumCreditScore)
    {
        _minimumCreditScore = minimumCreditScore;
    }

    public bool AppliesTo(string context) => context == "CustomerApproval";

    public Result<Customer> Apply(Customer customer)
    {
        if (customer.CreditScore < _minimumCreditScore)
        {
            customer.ApprovalStatus = ApprovalStatus.RequiresManualReview;
            customer.ApprovalNotes = $"Credit score {customer.CreditScore} below minimum {_minimumCreditScore}";
        }
        else
        {
            customer.ApprovalStatus = ApprovalStatus.AutoApproved;
        }

        return Result<Customer>.AsSuccess(customer);
    }
}
```

## Business Rule Implementation Best Practices

### **1. Rule Separation and Organization**
- **Single Responsibility**: Each rule class handles one specific business constraint
- **Clear Naming**: Rule names clearly indicate their purpose and scope
- **Context-Aware**: Rules know when and where they should be applied
- **Composable**: Rules can be combined to create complex validation chains

### **2. Error Handling and Messaging**
- **Descriptive Messages**: Business rule violations provide clear, actionable error messages
- **Localization**: Error messages support multiple languages
- **User-Friendly**: Technical errors are translated to business-friendly language
- **Audit Trail**: All rule evaluations are logged for compliance and debugging

### **3. Performance Considerations**
- **Rule Caching**: Expensive rule evaluations are cached when appropriate
- **Lazy Evaluation**: Rules are evaluated only when necessary
- **Batch Processing**: Multiple entities can be validated in batches
- **Asynchronous Processing**: Long-running rule evaluations use async patterns

## Conclusion

The Centron application implements comprehensive business logic patterns that ensure:

- **Consistency**: All business rules are applied uniformly across the application
- **Maintainability**: Business logic is separated from infrastructure concerns
- **Testability**: Business rules can be tested in isolation
- **Flexibility**: New rules can be added without modifying existing code
- **Performance**: Rule evaluation is optimized for production use
- **Compliance**: All business operations are properly validated and audited

These patterns provide the foundation for reliable, scalable business logic that can evolve with changing business requirements while maintaining architectural integrity.