Analyse Results

This commit is contained in:
2026-02-19 11:21:18 +01:00
parent c41919b280
commit a5d2f5490c
144 changed files with 35994 additions and 22326 deletions

View File

@@ -0,0 +1,660 @@
# 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.