Analyse Results
This commit is contained in:
660
Versuche/Versuch 03/Ergenisse/software/Business_Rules.md
Normal file
660
Versuche/Versuch 03/Ergenisse/software/Business_Rules.md
Normal 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.
|
||||
Reference in New Issue
Block a user