<\!DOCTYPE html> Core Services & Components - Dione Developer Course

🎯 What You'll Learn

  • How Dione's core services actually work
  • Real business logic implementation patterns
  • Service integration points and their quirks
  • Common debugging scenarios and solutions
  • Performance considerations and bottlenecks

Service Architecture Overview

⚠️ Important Context

Dione's services are not microservices. They're SOAP-based web services within a monolithic application. This affects how you think about service boundaries, transactions, and debugging.

Core Service Categories

🔐

Authentication & Session

Login, logout, session management, and user context

👥

Customer Management

Customer onboarding, KYC, profile management

💰

Payment Processing

Payment initiation, validation, and processing

📈

FX Trading

Currency exchange, rate management, trade execution

🏦

Open Banking

Bank account integration, transaction retrieval

📊

Reporting & Analytics

Transaction reporting, compliance data

Service Location Map

services/business/src/main/java/com/currenciesdirect/gtg/ngop/business/services/
services/
├── auth/                      # Authentication & Session Management
│   ├── AuthServiceIMPL.java
│   ├── LoginAndProfileHandlerImpl.java
│   └── SessionManager.java
├── customer/                  # Customer Management
│   ├── CustomerManagementIMPL.java
│   ├── CustomerOnboardingHandler.java
│   └── KYCProcessor.java
├── pfx/                      # Payment & FX Services
│   ├── payment/
│   │   ├── PaymentManagementIMPL.java
│   │   └── PaymentValidationHandler.java
│   ├── fx/
│   │   ├── FXTradingIMPL.java
│   │   └── RateManagementHandler.java
│   └── openbanking/
│       ├── OpenBankingIMPL.java
│       └── BankAccountHandler.java
└── reporting/                # Reporting Services
    ├── TransactionReportIMPL.java
    └── ComplianceReportHandler.java

Authentication Services

The authentication system is custom-built and handles multi-tenant login, session management, and user context.

Login Flow Implementation

AuthServiceIMPL.java
@WebService(serviceName = "WSAuth", 
            targetNamespace = "http://auth.services.business.ngop.gtg.currenciesdirect.com/")
public class AuthServiceIMPL implements WSAuth {
    
    @Autowired
    private LoginAndProfileHandlerImpl loginAndProfileHandlerRef;
    
    @Override
    public AuthResponse login(Holder<ResponseStatusHeader> responseStatusHeader,
                             AuthRequest authRequest, String locale, String source) {
        
        try {
            // Validate organization code
            if (!isValidOrganization(authRequest.getOrgCode())) {
                throw new NGOPBaseException("Invalid organization code");
            }
            
            // Create session data
            PojoMap sessionData = loginAndProfileHandlerRef.createNGOPSession(
                responseStatusHeader, authRequest, locale, source);
            
            // Build response
            AuthResponse response = new AuthResponse();
            response.setSessionID(sessionData.getString("sessionID"));
            response.setCustomerID(sessionData.getInteger("customerID"));
            response.setOrganizationId(sessionData.getInteger("organizationId"));
            
            // Set customer details
            Customer customer = (Customer) sessionData.get("customer");
            response.setCustomerName(customer.getFirstName() + " " + customer.getLastName());
            response.setCustomerEmail(customer.getEmail());
            
            return response;
            
        } catch (Exception e) {
            LOG.error("Login failed for user: " + authRequest.getEmail(), e);
            throw new NGOPBaseException("Authentication failed");
        }
    }
}

Session Management Reality

LoginAndProfileHandlerImpl.java
public PojoMap createNGOPSession(Holder<ResponseStatusHeader> responseStatusHeader,
                                AuthRequest authRequest, String locale, String source) {
    
    // Get organization ID from code
    Integer orgId = getOrganizationId(authRequest.getOrgCode());
    
    // Lookup customer by email + organization
    Customer customer = nGOPCusDaoRef.getCustomerByEmail(
        authRequest.getEmail(), orgId);
    
    if (customer == null) {
        throw new NGOPBaseException("Customer not found");
    }
    
    // Verify password (yes, it's custom password hashing)
    if (!verifyPassword(customer.getPassword(), authRequest.getPassword())) {
        throw new NGOPBaseException("Invalid credentials");
    }
    
    // Check if customer is active
    if (!customer.getActive()) {
        throw new NGOPBaseException("Account is inactive");
    }
    
    // Create session ID
    String sessionId = generateSessionId();
    
    // Build session data
    PojoMap sessionData = new PojoMap();
    sessionData.put("sessionID", sessionId);
    sessionData.put("customerID", customer.getCustomerId());
    sessionData.put("organizationId", orgId);
    sessionData.put("customer", customer);
    sessionData.put("loginTime", new Date());
    sessionData.put("source", source);
    
    // Store in cache (Infinispan)
    cacheDataConsumer.put(sessionId, sessionData, SESSION_TIMEOUT_MINUTES * 60);
    
    // Log successful login
    auditService.logLogin(customer.getCustomerId(), sessionId, source);
    
    return sessionData;
}

Session Validation Pattern

Every service call validates the session. This is the pattern you'll see everywhere:

Session Validation Pattern
// This appears in every service method
public SomeResponse someServiceMethod(Holder<ResponseStatusHeader> responseStatusHeader,
                                     SomeRequest request, String sessionId) {
    
    // Always validate session first
    PojoMap sessionData = validateSession(sessionId);
    if (sessionData == null) {
        throw new NGOPBaseException("Invalid or expired session");
    }
    
    // Extract customer context
    Integer customerId = sessionData.getInteger("customerID");
    Integer organizationId = sessionData.getInteger("organizationId");
    Customer customer = (Customer) sessionData.get("customer");
    
    // Proceed with business logic...
}

Customer Management

Customer management handles onboarding, KYC verification, profile updates, and customer lifecycle.

Customer Onboarding Flow

CustomerManagementIMPL.java - Registration
@Override
public CustomerRegistrationResponse registerCustomer(
        Holder<ResponseStatusHeader> responseStatusHeader,
        CustomerRegistrationRequest request, String locale, String source) {
    
    try {
        // Validate organization
        Integer orgId = getOrganizationId(request.getOrgCode());
        
        // Check if email already exists
        Customer existingCustomer = nGOPCusDaoRef.getCustomerByEmail(
            request.getEmail(), orgId);
        if (existingCustomer != null) {
            throw new NGOPBaseException("Email already registered");
        }
        
        // Create customer entity
        Customer customer = new Customer();
        customer.setFirstName(request.getFirstName());
        customer.setLastName(request.getLastName());
        customer.setEmail(request.getEmail());
        customer.setPassword(hashPassword(request.getPassword()));
        customer.setOrganizationId(orgId);
        customer.setActive(false); // Requires email verification
        customer.setCreatedDate(new Date());
        customer.setKycStatus("PENDING");
        
        // Save customer
        nGOPCusDaoRef.saveCustomer(customer);
        
        // Generate verification token
        String verificationToken = generateVerificationToken();
        
        // Save verification record
        CustomerVerification verification = new CustomerVerification();
        verification.setCustomerId(customer.getCustomerId());
        verification.setVerificationToken(verificationToken);
        verification.setExpiryDate(DateUtils.addHours(new Date(), 24));
        nGOPCusDaoRef.saveVerification(verification);
        
        // Send verification email
        emailService.sendVerificationEmail(customer.getEmail(), 
                                          verificationToken, locale);
        
        // Create Salesforce lead (if configured)
        if (shouldCreateSalesforceRecord(orgId)) {
            salesforceService.createLead(customer);
        }
        
        CustomerRegistrationResponse response = new CustomerRegistrationResponse();
        response.setCustomerID(customer.getCustomerId());
        response.setStatus("PENDING_VERIFICATION");
        
        return response;
        
    } catch (Exception e) {
        LOG.error("Customer registration failed", e);
        throw new NGOPBaseException("Registration failed: " + e.getMessage());
    }
}

KYC Processing

KYCProcessor.java
public void processKYCDocuments(Integer customerId, List<KYCDocument> documents) {
    
    Customer customer = nGOPCusDaoRef.getCustomerById(customerId);
    if (customer == null) {
        throw new NGOPBaseException("Customer not found");
    }
    
    // Validate required documents based on organization
    KYCRequirements requirements = getKYCRequirements(customer.getOrganizationId());
    
    boolean hasValidId = false;
    boolean hasProofOfAddress = false;
    boolean hasProofOfIncome = false;
    
    for (KYCDocument doc : documents) {
        switch (doc.getDocumentType()) {
            case "PASSPORT":
            case "DRIVING_LICENCE":
            case "NATIONAL_ID":
                hasValidId = validateIdDocument(doc);
                break;
            case "UTILITY_BILL":
            case "BANK_STATEMENT":
                hasProofOfAddress = validateAddressDocument(doc);
                break;
            case "PAYSLIP":
            case "BANK_STATEMENT_INCOME":
                hasProofOfIncome = validateIncomeDocument(doc);
                break;
        }
    }
    
    // Determine KYC status
    String kycStatus = "PENDING";
    if (hasValidId && hasProofOfAddress) {
        if (requirements.isIncomeVerificationRequired() && hasProofOfIncome) {
            kycStatus = "APPROVED";
        } else if (!requirements.isIncomeVerificationRequired()) {
            kycStatus = "APPROVED";
        }
    }
    
    // Update customer KYC status
    customer.setKycStatus(kycStatus);
    if ("APPROVED".equals(kycStatus)) {
        customer.setActive(true);
        customer.setKycApprovedDate(new Date());
    }
    
    nGOPCusDaoRef.updateCustomer(customer);
    
    // Notify external systems
    if ("APPROVED".equals(kycStatus)) {
        salesforceService.updateCustomerStatus(customer, "KYC_APPROVED");
        emailService.sendKYCApprovalEmail(customer.getEmail());
    }
}

Payment Processing

Payment processing is the heart of Dione's business logic. It handles validation, routing, and integration with external payment systems.

Payment Initiation

PaymentManagementIMPL.java
@Override
public PaymentResponse initiatePayment(Holder<ResponseStatusHeader> responseStatusHeader,
                                      PaymentRequest request, String sessionId) {
    
    // Validate session and get customer context
    PojoMap sessionData = validateSession(sessionId);
    Integer customerId = sessionData.getInteger("customerID");
    Integer organizationId = sessionData.getInteger("organizationId");
    
    try {
        // Validate payment request
        PaymentValidationHandler.validatePayment(request, customerId, organizationId);
        
        // Check customer limits
        BigDecimal customerLimit = getCustomerLimit(customerId);
        if (request.getAmount().compareTo(customerLimit) > 0) {
            throw new NGOPBaseException("Amount exceeds customer limit");
        }
        
        // Create payment record
        Payment payment = new Payment();
        payment.setCustomerId(customerId);
        payment.setOrganizationId(organizationId);
        payment.setAmount(request.getAmount());
        payment.setFromCurrency(request.getFromCurrency());
        payment.setToCurrency(request.getToCurrency());
        payment.setExchangeRate(request.getExchangeRate());
        payment.setStatus("PENDING");
        payment.setCreatedDate(new Date());
        payment.setReference(generatePaymentReference());
        
        // Save payment
        paymentDaoRef.savePayment(payment);
        
        // Get FX rate (if not provided)
        if (request.getExchangeRate() == null) {
            FxRate fxRate = fxRateService.getCurrentRate(
                request.getFromCurrency(), 
                request.getToCurrency(), 
                request.getAmount(),
                organizationId);
            payment.setExchangeRate(fxRate.getRate());
        }
        
        // Route to appropriate payment processor
        PaymentProcessor processor = getPaymentProcessor(
            request.getPaymentMethod(), organizationId);
        
        PaymentProcessingResult result = processor.processPayment(payment);
        
        // Update payment status
        payment.setStatus(result.getStatus());
        payment.setExternalReference(result.getExternalReference());
        payment.setProcessedDate(new Date());
        
        paymentDaoRef.updatePayment(payment);
        
        // Send notifications
        if ("COMPLETED".equals(result.getStatus())) {
            emailService.sendPaymentConfirmation(payment);
            smsService.sendPaymentNotification(payment);
        }
        
        // Build response
        PaymentResponse response = new PaymentResponse();
        response.setPaymentId(payment.getPaymentId());
        response.setReference(payment.getReference());
        response.setStatus(payment.getStatus());
        response.setExchangeRate(payment.getExchangeRate());
        
        return response;
        
    } catch (Exception e) {
        LOG.error("Payment initiation failed for customer: " + customerId, e);
        throw new NGOPBaseException("Payment processing failed: " + e.getMessage());
    }
}

Payment Validation Logic

PaymentValidationHandler.java
public static void validatePayment(PaymentRequest request, 
                                         Integer customerId, Integer organizationId) {
    
    List<String> errors = new ArrayList<>();
    
    // Validate amount
    if (request.getAmount() == null || request.getAmount().compareTo(BigDecimal.ZERO) <= 0) {
        errors.add("Amount must be greater than zero");
    }
    
    // Validate currencies
    if (StringUtils.isEmpty(request.getFromCurrency()) || 
        StringUtils.isEmpty(request.getToCurrency())) {
        errors.add("From and To currencies are required");
    }
    
    // Validate currency support
    if (!isCurrencySupported(request.getFromCurrency(), organizationId)) {
        errors.add("From currency not supported");
    }
    
    if (!isCurrencySupported(request.getToCurrency(), organizationId)) {
        errors.add("To currency not supported");
    }
    
    // Validate beneficiary details
    if (request.getBeneficiary() == null) {
        errors.add("Beneficiary details are required");
    } else {
        validateBeneficiary(request.getBeneficiary(), errors);
    }
    
    // Validate payment method
    if (StringUtils.isEmpty(request.getPaymentMethod())) {
        errors.add("Payment method is required");
    }
    
    // Organization-specific validation
    if ("CD".equals(getOrganizationCode(organizationId))) {
        validateCurrenciesDirectRules(request, errors);
    } else if ("TORFX".equals(getOrganizationCode(organizationId))) {
        validateTorFXRules(request, errors);
    }
    
    // Compliance checks
    if (request.getAmount().compareTo(new BigDecimal("10000")) > 0) {
        // Large amount - requires additional verification
        Customer customer = customerDao.getCustomerById(customerId);
        if (!"ENHANCED".equals(customer.getKycStatus())) {
            errors.add("Enhanced KYC required for amounts over 10,000");
        }
    }
    
    if (!errors.isEmpty()) {
        throw new NGOPBaseException("Validation failed: " + String.join(", ", errors));
    }
}

FX Trading Engine

The FX trading engine handles currency exchange rates, trade execution, and market data integration.

Rate Management

FXTradingIMPL.java - Rate Retrieval
@Override
public FXRateResponse getCurrentRate(Holder<ResponseStatusHeader> responseStatusHeader,
                                   FXRateRequest request, String sessionId) {
    
    PojoMap sessionData = validateSession(sessionId);
    Integer organizationId = sessionData.getInteger("organizationId");
    
    try {
        // Check cache first
        String cacheKey = buildRateCacheKey(request.getFromCurrency(), 
                                          request.getToCurrency(), 
                                          request.getAmount(), 
                                          organizationId);
        
        FxRate cachedRate = (FxRate) cacheDataConsumer.get(cacheKey);
        if (cachedRate != null && !isRateExpired(cachedRate)) {
            return buildRateResponse(cachedRate);
        }
        
        // Get rate from pricing engine
        FxRate fxRate = pricingEngineService.getExchangeRate(
            request.getFromCurrency(),
            request.getToCurrency(),
            request.getAmount(),
            organizationId
        );
        
        // Apply organization-specific margins
        fxRate = applyMargins(fxRate, organizationId);
        
        // Cache the rate
        cacheDataConsumer.put(cacheKey, fxRate, RATE_CACHE_TIMEOUT_SECONDS);
        
        // Log rate request
        auditService.logRateRequest(sessionData.getInteger("customerID"), 
                                   request, fxRate);
        
        return buildRateResponse(fxRate);
        
    } catch (Exception e) {
        LOG.error("Rate retrieval failed", e);
        
        // Fallback to last known rate
        FxRate fallbackRate = getFallbackRate(request.getFromCurrency(), 
                                            request.getToCurrency(), 
                                            organizationId);
        if (fallbackRate != null) {
            LOG.warn("Using fallback rate due to pricing service failure");
            return buildRateResponse(fallbackRate);
        }
        
        throw new NGOPBaseException("Unable to retrieve exchange rate");
    }
}

Trade Execution

Trade Execution Logic
public TradeExecutionResult executeTrade(Payment payment) {
    
    try {
        // Validate trade parameters
        validateTradeParameters(payment);
        
        // Check market hours
        if (!isMarketOpen(payment.getFromCurrency(), payment.getToCurrency())) {
            throw new NGOPBaseException("Market is closed for this currency pair");
        }
        
        // Execute trade with external provider
        TradeRequest tradeRequest = new TradeRequest();
        tradeRequest.setFromCurrency(payment.getFromCurrency());
        tradeRequest.setToCurrency(payment.getToCurrency());
        tradeRequest.setAmount(payment.getAmount());
        tradeRequest.setRate(payment.getExchangeRate());
        tradeRequest.setTradeType("SPOT");
        
        TradeResponse tradeResponse = tradeExecutionService.executeTrade(tradeRequest);
        
        // Update payment with trade details
        payment.setTradeReference(tradeResponse.getTradeReference());
        payment.setExecutedRate(tradeResponse.getExecutedRate());
        payment.setExecutedAmount(tradeResponse.getExecutedAmount());
        payment.setTradeDate(new Date());
        
        // Calculate settlement details
        SettlementDetails settlement = calculateSettlement(payment);
        payment.setSettlementDate(settlement.getSettlementDate());
        payment.setSettlementAmount(settlement.getSettlementAmount());
        
        TradeExecutionResult result = new TradeExecutionResult();
        result.setStatus("EXECUTED");
        result.setTradeReference(tradeResponse.getTradeReference());
        result.setExecutedRate(tradeResponse.getExecutedRate());
        
        return result;
        
    } catch (Exception e) {
        LOG.error("Trade execution failed for payment: " + payment.getPaymentId(), e);
        
        TradeExecutionResult result = new TradeExecutionResult();
        result.setStatus("FAILED");
        result.setErrorMessage(e.getMessage());
        
        return result;
    }
}

Open Banking Integration

Open Banking services connect to customer bank accounts for transaction retrieval and payment initiation.

Bank Account Connection

OpenBankingIMPL.java
@Override
public BankConnectionResponse connectBankAccount(
        Holder<ResponseStatusHeader> responseStatusHeader,
        BankConnectionRequest request, String sessionId) {
    
    PojoMap sessionData = validateSession(sessionId);
    Integer customerId = sessionData.getInteger("customerID");
    
    try {
        // Validate bank credentials
        if (!isValidBank(request.getBankCode())) {
            throw new NGOPBaseException("Unsupported bank");
        }
        
        // Connect to Open Banking provider (e.g., Plaid, TrueLayer)
        OpenBankingProvider provider = getOpenBankingProvider(request.getBankCode());
        
        // Exchange authorization code for access token
        TokenExchangeRequest tokenRequest = new TokenExchangeRequest();
        tokenRequest.setAuthorizationCode(request.getAuthorizationCode());
        tokenRequest.setClientId(getClientId(provider));
        tokenRequest.setClientSecret(getClientSecret(provider));
        
        TokenExchangeResponse tokenResponse = provider.exchangeToken(tokenRequest);
        
        // Get account information
        AccountInfoRequest accountRequest = new AccountInfoRequest();
        accountRequest.setAccessToken(tokenResponse.getAccessToken());
        
        List<BankAccount> accounts = provider.getAccountInfo(accountRequest);
        
        // Save customer bank accounts
        for (BankAccount account : accounts) {
            CustomerBankAccount customerAccount = new CustomerBankAccount();
            customerAccount.setCustomerId(customerId);
            customerAccount.setBankCode(request.getBankCode());
            customerAccount.setAccountNumber(account.getAccountNumber());
            customerAccount.setSortCode(account.getSortCode());
            customerAccount.setAccountType(account.getAccountType());
            customerAccount.setAccessToken(encrypt(tokenResponse.getAccessToken()));
            customerAccount.setRefreshToken(encrypt(tokenResponse.getRefreshToken()));
            customerAccount.setTokenExpiry(tokenResponse.getExpiresAt());
            customerAccount.setActive(true);
            customerAccount.setCreatedDate(new Date());
            
            bankAccountDaoRef.saveBankAccount(customerAccount);
        }
        
        BankConnectionResponse response = new BankConnectionResponse();
        response.setConnectionId(generateConnectionId());
        response.setAccountCount(accounts.size());
        response.setStatus("CONNECTED");
        
        return response;
        
    } catch (Exception e) {
        LOG.error("Bank connection failed for customer: " + customerId, e);
        throw new NGOPBaseException("Bank connection failed: " + e.getMessage());
    }
}

Common Service Patterns

Understanding these patterns will help you navigate and contribute to the Dione codebase effectively.

The Standard Service Pattern

Standard Service Implementation
@WebService(serviceName = "WSServiceName", 
            targetNamespace = "http://services.business.ngop.gtg.currenciesdirect.com/")
public class ServiceNameIMPL implements WSServiceName {
    
    private static final Logger LOG = LoggerFactory.getLogger(ServiceNameIMPL.class);
    
    @Autowired
    private BusinessHandlerImpl businessHandlerRef;
    
    @Autowired
    private CacheDataConsumer cacheDataConsumer;
    
    @Autowired
    private AuditService auditService;
    
    @Override
    public ResponseType serviceMethod(Holder<ResponseStatusHeader> responseStatusHeader,
                                    RequestType request, String sessionId) {
        
        // 1. Validate session
        PojoMap sessionData = validateSession(sessionId);
        Integer customerId = sessionData.getInteger("customerID");
        Integer organizationId = sessionData.getInteger("organizationId");
        
        try {
            // 2. Validate request
            validateRequest(request, customerId, organizationId);
            
            // 3. Delegate to business handler
            BusinessResult result = businessHandlerRef.handleBusinessLogic(
                request, customerId, organizationId);
            
            // 4. Audit the operation
            auditService.logOperation(customerId, "SERVICE_METHOD", request);
            
            // 5. Build response
            ResponseType response = new ResponseType();
            response.setResult(result.getValue());
            response.setStatus("SUCCESS");
            
            return response;
            
        } catch (NGOPBaseException e) {
            LOG.error("Business logic error", e);
            throw e;
        } catch (Exception e) {
            LOG.error("Unexpected error in service method", e);
            throw new NGOPBaseException("Service unavailable");
        }
    }
}

Error Handling Pattern

Error Handling Standards
// Standard error handling across all services
try {
    // Business logic here
    
} catch (NGOPBaseException e) {
    // Expected business exceptions - log and rethrow
    LOG.warn("Business logic exception: " + e.getMessage());
    throw e;
    
} catch (ValidationException e) {
    // Validation errors - convert to business exception
    LOG.warn("Validation failed: " + e.getMessage());
    throw new NGOPBaseException("Validation failed: " + e.getMessage());
    
} catch (ExternalServiceException e) {
    // External service failures - log and provide fallback
    LOG.error("External service failure", e);
    if (hasFallback()) {
        return getFallbackResponse();
    } else {
        throw new NGOPBaseException("Service temporarily unavailable");
    }
    
} catch (Exception e) {
    // Unexpected errors - log and convert to generic exception
    LOG.error("Unexpected error in service", e);
    throw new NGOPBaseException("Service unavailable");
}

Caching Pattern

Service-Level Caching
// Standard caching pattern used across services
public ExpensiveResult getExpensiveData(String key, Integer organizationId) {
    
    // Build cache key with organization isolation
    String cacheKey = "expensive_data:" + organizationId + ":" + key;
    
    // Try cache first
    ExpensiveResult cached = (ExpensiveResult) cacheDataConsumer.get(cacheKey);
    if (cached != null) {
        LOG.debug("Cache hit for key: " + cacheKey);
        return cached;
    }
    
    // Cache miss - compute the result
    LOG.debug("Cache miss for key: " + cacheKey);
    ExpensiveResult result = computeExpensiveResult(key, organizationId);
    
    // Cache the result (5 minutes TTL)
    cacheDataConsumer.put(cacheKey, result, 300);
    
    return result;
}

Debugging Services

Common debugging scenarios and how to approach them systematically.

Service Call Tracing

Debugging Service Calls
# 1. Check JBoss logs for service calls
tail -f /opt/jboss/standalone/log/server.log | grep "serviceMethod"

# 2. Look for session validation errors
grep "Invalid session" /opt/jboss/standalone/log/server.log

# 3. Check for external service timeouts
grep "ConnectTimeoutException\|SocketTimeoutException" /opt/jboss/standalone/log/server.log

# 4. Monitor cache performance
grep "Cache hit\|Cache miss" /opt/jboss/standalone/log/server.log

Common Service Issues

🚫 Session Timeouts

Sessions expire after 30 minutes of inactivity. Check session validation logic.

🚫 External Service Failures

Pricing Engine, Salesforce, or payment processors can be unreliable. Check fallback logic.

🚫 Organization Isolation

Data must be isolated by organization ID. Missing organization checks cause cross-tenant data leaks.

🚫 Cache Inconsistency

Cached data can become stale. Implement proper cache invalidation strategies.

Performance Monitoring

Service Performance Queries
-- Check slow service calls
SELECT 
    service_name,
    AVG(response_time_ms) as avg_response_time,
    COUNT(*) as call_count
FROM service_audit_log 
WHERE created_date >= DATEADD(hour, -1, GETDATE())
GROUP BY service_name
ORDER BY avg_response_time DESC;

-- Check error rates by service
SELECT 
    service_name,
    COUNT(*) as total_calls,
    SUM(CASE WHEN status = 'ERROR' THEN 1 ELSE 0 END) as error_count,
    (SUM(CASE WHEN status = 'ERROR' THEN 1 ELSE 0 END) * 100.0 / COUNT(*)) as error_rate
FROM service_audit_log 
WHERE created_date >= DATEADD(hour, -1, GETDATE())
GROUP BY service_name
ORDER BY error_rate DESC;

🎯 Key Takeaways

  • Session Management: Every service call validates session context
  • Organization Isolation: All data access must include organization ID
  • Error Handling: Consistent error handling patterns across all services
  • External Dependencies: Always implement fallback mechanisms
  • Caching: Leverage Infinispan for performance, but handle cache invalidation
  • Audit Trails: Log all significant business operations for compliance

📝 Chapter Quiz

Test your understanding of Dione's core services and components:

Question 1: What is the main responsibility of Handler classes in Dione?

Question 2: How does Dione achieve multi-tenant isolation?

Question 3: What pattern does Dione use for external service integration?