Architecture Deep Dive
How It Actually Works vs. Intended Architecture
๐ฏ What You'll Learn
- How Dione's modular monolith actually works
- Real vs. intended architecture patterns
- Data flow through the system layers
- External system integration reality
- Why certain architectural decisions were made
The Real Architecture
โ ๏ธ Reality Check
This is NOT a microservices architecture. It's a modular monolith with SOAP-based web services. Understanding this is crucial to being productive.
High-Level Architecture Diagram
โโโโโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโ
โ Customer โ โ Admin โ โ Etailer โ
โ Portal โ โ Portal โ โ Portal โ
โ (JSP/jQuery) โ โ (JSP/jQuery) โ โ (JSP/jQuery) โ
โโโโโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโ
โ โ โ
โโโโโโโโโโโโโโโโโโโโโโโโโผโโโโโโโโโโโโโโโโโโโโโโโโ
โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ SOAP Web Services โ
โ (Apache CXF + Spring) โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Business Logic Layer โ
โ (Handlers + Processors) โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Data Transformation โ
โ (Transformers) โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Data Access Layer โ
โ (JPA/Hibernate + DAOs) โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ
โโโโโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโโ
โ SQL Server โ Infinispan โ External โ
โ Database โ Cache โ Systems โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Key Architectural Principles
Modular Monolith
Single deployment with clear module boundaries. Not microservices, but well-organized code.
Multi-Tenant
Single codebase serves multiple organizations with data isolation by Organization ID.
SOAP-First
All APIs are SOAP-based using Apache CXF. No REST endpoints exist.
Layered Architecture
Clear separation: Web โ Services โ Handlers โ Transformers โ DAOs โ Database
Module Structure Reality
Dione/Development/Codebase/
โโโ pom.xml # Parent POM (Maven 3.6+)
โโโ ngop.properties # Main configuration (the heart of everything)
โโโ services/ # Business logic modules
โ โโโ business/ # Core business services
โ โโโ dioneservices/ # Service orchestration
โโโ customer_portal/ # Customer-facing web app
โโโ schema/ # Database scripts
โโโ source_mapper/ # Data mapping utilities
Module Responsibilities
Services/Business Module
This is where the real work happens:
- Web Services: SOAP endpoints for all functionality
- Business Logic: Handlers containing business rules
- Data Access: JPA entities and DAO implementations
- External Integration: Touchpoints to external systems
<!-- Authentication & Session Management -->
<jaxws:endpoint id="auth"
implementor="com.currenciesdirect.gtg.ngop.business.services.auth.AuthServiceIMPL"
address="/authServices" />
<!-- Customer Management -->
<jaxws:endpoint id="customer"
implementor="com.currenciesdirect.gtg.ngop.business.services.customer.CustomerManagementIMPL"
address="/customerServices" />
<!-- Payment Processing -->
<jaxws:endpoint id="payment"
implementor="com.currenciesdirect.gtg.ngop.business.services.pfx.payment.PaymentManagementIMPL"
address="/paymentServices" />
Customer Portal Module
Traditional web application with:
- JSP Pages: Server-side rendered views
- Spring MVC Controllers: Web request handlers
- Service Invokers: SOAP client wrappers
- jQuery Frontend: Client-side interactions
๐ก Why This Architecture?
This architecture was chosen because:
- Financial Compliance: Easier to audit monolithic systems
- Transaction Consistency: ACID properties across operations
- Team Size: Small team could manage everything
- Technology Maturity: SOAP was the enterprise standard
Data Flow Pattern
Every request follows this exact pattern. Understanding this is key to debugging issues:
// 1. Web Controller (CustomerPortal)
@Controller
public class LoginController {
@RequestMapping("/login.htm")
public ModelAndView login(HttpServletRequest request) {
// Controller logic
return serviceInvoker.callLoginService(request);
}
}
// 2. Service Invoker (SOAP Call)
LoginControllerService.loginCustomer()
โ
// 3. Business Service (SOAP Implementation)
AuthServiceIMPL.login()
โ
// 4. Business Handler
LoginAndProfileHandlerImpl.createNGOPSession()
โ
// 5. Data Access
NGOPCusDaoImpl.getCustomerByEmail()
โ
// 6. Database Query (JPA)
entityManager.createQuery(SqlServerQueries.LOOKUP_CUSTOMER_BY_EMAIL)
Layer Responsibilities
| Layer | Purpose | Technologies | Location |
|---|---|---|---|
| Web Controllers | HTTP request handling | Spring MVC | customer_portal/ |
| Service Invokers | SOAP client wrappers | Apache CXF | customer_portal/ |
| Web Services | SOAP endpoints | JAX-WS + CXF | services/business/ |
| Business Handlers | Business logic | Spring Beans | services/business/ |
| Transformers | Data conversion | Custom mappers | services/business/ |
| DAOs | Data access | JPA/Hibernate | services/business/ |
Real Data Flow Example
// 1. User submits login form
POST /CustomerPortal/login.htm
{
email: "user@example.com",
password: "password123",
orgCode: "CD"
}
// 2. LoginController processes request
@RequestMapping("/login.htm")
public ModelAndView login(HttpServletRequest request) {
AuthRequest authRequest = buildAuthRequest(request);
AuthResponse response = loginControllerService.loginCustomer(authRequest);
return new ModelAndView("dashboard", "user", response.getUser());
}
// 3. Service Invoker calls SOAP service
public AuthResponse loginCustomer(AuthRequest request) {
WSAuth authService = getAuthService();
Holder<ResponseStatusHeader> statusHeader = new Holder<>();
return authService.login(statusHeader, request, "en", "WEB");
}
// 4. AuthServiceIMPL processes login
@WebService
public class AuthServiceIMPL implements WSAuth {
public AuthResponse login(Holder<ResponseStatusHeader> responseStatusHeader,
AuthRequest authRequest, String locale, String source) {
// Business logic delegation
PojoMap sessionData = loginAndProfileHandlerRef.createNGOPSession(
responseStatusHeader, authRequest, locale, source);
// Build response
AuthResponse response = new AuthResponse();
response.setSessionID(sessionData.getString("sessionID"));
response.setCustomerID(sessionData.getInteger("customerID"));
return response;
}
}
// 5. Handler implements business logic
public PojoMap createNGOPSession(Holder<ResponseStatusHeader> responseStatusHeader,
AuthRequest authRequest, String locale, String source) {
// Get customer from database
Customer customer = nGOPCusDaoRef.getCustomerByEmail(
authRequest.getEmail(), getOrganizationId(authRequest.getOrgCode()));
// Verify password
if (!verifyPassword(customer, authRequest.getPassword())) {
throw new NGOPBaseException("Invalid credentials");
}
// Create session
String sessionId = SessionManager.createSessionId();
PojoMap sessionData = SessionManager.createSessionData(customer, authRequest.getOrgCode());
// Store in cache
cacheDataConsumer.put(sessionId, sessionData, SESSION_TIMEOUT);
return sessionData;
}
External System Integration
Dione integrates with numerous external systems. Understanding these integrations is crucial for debugging:
Core External Systems
# Salesforce CRM
ngop.sf.connectionurl=https://test.salesforce.com/services/Soap/c/38.0
# Pricing Engine
pe.serivce.host=172.31.4.4
pe.serivce.url.template=http://%s:%s/PricingEngine/%s?WSDL
# Communication Hub
commhub.service.url=http://sitcommhub.currenciesdirect.com/commhub/
# Titan Payment System
titan.service.url=http://172.31.2.129:8080/titan-wrapper/services/
# Rate Alert Service
ratealert.service.rest.url=https://sitpe.currenciesdirect.com/RateAlert/rest/
Integration Architecture Pattern
All external calls go through "Touchpoint" classes that handle:
- Connection Management: HTTP clients and connection pooling
- Error Handling: Timeouts, retries, circuit breakers
- Data Transformation: Request/response mapping
- Caching: Response caching when appropriate
@Component
public class PricingEngineServiceInvoker {
@Autowired
private PropertyHandler propertyHandler;
public FxRate getExchangeRate(String fromCurrency, String toCurrency,
BigDecimal amount, Integer orgId) {
try {
// Build service URL
String host = propertyHandler.getProperty("pe.serivce.host");
String port = propertyHandler.getProperty("pe.serivce.port");
String urlTemplate = propertyHandler.getProperty("pe.serivce.url.template");
String serviceUrl = String.format(urlTemplate, host, port, "PricingEngineService");
// Create SOAP request
GetExchangeRateRequest request = new GetExchangeRateRequest();
request.setFromCurrency(fromCurrency);
request.setToCurrency(toCurrency);
request.setAmount(amount);
request.setOrganizationId(orgId);
// Call service with timeout
PricingEngineService service = createServiceClient(serviceUrl);
GetExchangeRateResponse response = service.getExchangeRate(request);
if (response.getStatus().getCode().equals("SUCCESS")) {
return response.getFxRate();
} else {
throw new NGOPBaseException("Pricing service error: " + response.getStatus().getMessage());
}
} catch (Exception e) {
// Fallback to cached rates or default pricing
LOG.error("Pricing engine unavailable, using fallback", e);
return getFallbackRate(fromCurrency, toCurrency, amount, orgId);
}
}
private FxRate getFallbackRate(String fromCurrency, String toCurrency,
BigDecimal amount, Integer orgId) {
// Fallback logic for when external service is down
return cacheService.getLastKnownRate(fromCurrency, toCurrency, orgId);
}
}
Integration Challenges
๐ซ Service Dependencies
Many critical functions depend on external services being available
๐ซ Network Timeouts
Financial networks can be slow - timeouts are common
๐ซ Data Consistency
No distributed transactions - eventual consistency only
๐ซ Error Propagation
External service errors can cascade through the system
Deployment Topology
Production Environment
โโโโโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโ
โ Load Balancer โ โ Load Balancer โ
โ (Customer) โ โ (Admin) โ
โโโโโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโ
โ โ
โผ โผ
โโโโโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโ
โ JBoss EAP โ โ JBoss EAP โ
โ Node 1 โ โ Node 2 โ
โ Customer โ โ Admin + โ
โ Portal โ โ Business โ
โ โ โ Services โ
โโโโโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโ
โ โ
โโโโโโโโโโโโโฌโโโโโโโโโโโโ
โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ SQL Server Always On โ
โ Availability Group โ
โ โโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโ
โ โ Primary โ โ Secondary โโ
โ โ Node โ โ Node โโ
โ โโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Application Server Configuration
- JBoss EAP 7.0: Not the latest, but stable and supported
- Java 8: Required for compatibility with legacy dependencies
- Connection Pooling: C3P0 for database connections
- Session Clustering: Infinispan for distributed sessions
Scaling Characteristics
Vertical Scaling
Primarily scales up (bigger servers) rather than out (more servers)
Database Bottleneck
SQL Server is usually the limiting factor, not the application servers
Cache-Heavy
Infinispan cache reduces database load significantly
Technical Debt as Features
๐ฏ Perspective Shift
What looks like "technical debt" often represents solved business problems. Understanding the context helps you work with the system, not against it.
Common "Issues" and Their Business Context
1. Hardcoded Organization Logic
// This looks like a code smell, but...
if (orgCode.equals("CD")) {
// Currencies Direct specific logic
useComplexComplianceRules();
} else if (orgCode.equals("TorFX")) {
// TorFX specific logic
useSimplifiedKYC();
}
Why it exists: Different financial regulations per organization. Each client has unique compliance requirements that can't be generalized.
2. SOAP Instead of REST
Why it exists:
- Financial services industry standard when built
- Strong typing and contracts required for compliance
- WSDL provides clear API documentation
- Better tooling for financial messaging standards
3. Monolithic Deployment
Why it exists:
- Easier to audit for financial compliance
- ACID transactions across all operations
- Small team could manage everything
- Reduced operational complexity
4. Custom Authentication
Why it exists:
- Pre-dates modern auth frameworks
- Specific multi-tenant requirements
- Integration with legacy CRM systems
- Custom audit trail requirements
Working With Legacy Decisions
โ ๏ธ Do's and Don'ts
DO:
- Understand the business context before criticizing
- Work within existing patterns
- Make small, incremental improvements
- Document your reasoning
DON'T:
- Rewrite entire subsystems "to modernize"
- Break existing APIs without migration plans
- Ignore compliance and audit requirements
- Assume newer is always better
๐ก The Bottom Line
Dione's architecture reflects 10+ years of solving real business problems in financial services. It may not be "modern," but it works, it's profitable, and it serves customers reliably. Your job is to understand it, work with it, and improve it incrementally while respecting the business context that shaped it.
๐ Chapter Quiz
Test your understanding of Dione's architecture with these questions: