Home / Architecture / System Architecture
System Architecture
The middleware is a Spring Boot 3.5.4 application on Java 21, packaged as a WAR and running on embedded Tomcat. This page covers the runtime stack, the code layout and the request pipeline.
Runtime environment
Component Version Purpose
Java 21 (LTS) Runtime platform
Spring Boot 3.5.4 Application framework
Maven 3.x Build tool
MariaDB 10.6+ Relational database
Flyway (via Spring Boot) Schema migrations (auto-run on startup)
Key dependencies
Dependency Purpose
spring-boot-starter-web / -data-jpa / -validation / -security / -actuator / -thymeleafREST API, ORM, bean validation, auth, monitoring, portal templating
mariadb-java-client, flyway-core, flyway-mysql, HikariCPJDBC, migrations, connection pool (max 20, min idle 5)
Apache httpclient5 Outbound HTTP to Chrilan and Oracle STS
jjwt-api/impl/jackson 0.12.6JWT parsing and validation
springdoc-openapi-starter-webmvc-ui 2.6.0Swagger UI and OpenAPI 3 spec
Server configuration
Property Value
server.port8181
server.servlet.context-path/pckzra
server.address0.0.0.0
server.forward-headers-strategynative (Nginx reverse proxy support)
Package structure
src/main/java/com/micros/taxintchrilan/
├── TaxintChrilanMiddlewareApplication.java # Spring Boot entry point
├── config/
│ ├── SecurityConfig.java # Dual filter chain (API stateless + Web session)
│ ├── AsyncConfig.java # Thread pool for async operations
│ └── DataInitializer.java # Seed data on startup
├── controller/
│ ├── TaxintController.java # Core POS API (/api/taxint/*)
│ ├── StoreController.java # Store CRUD API (/api/stores/*)
│ ├── ReportsController.java # Reports API (/api/reports/*)
│ └── WebController.java # Thymeleaf portal (/, /dashboard, /stores, /reports)
├── service/
│ ├── ConversionService.java # Taxint ↔ Chrilan transformation
│ ├── ChrilanApiClient.java # HTTP client for Chrilan Smart API
│ ├── OracleStsApiClient.java # HTTP client for Oracle STS (menu + PKCE)
│ ├── LoggingService.java # Database logging for all API calls
│ ├── StoreRegistrationService.java# Store business logic
│ ├── MenuSyncService.java # Oracle → Chrilan menu sync
│ ├── MenuSyncScheduler.java # 60-second scheduled sync
│ ├── AuthenticationService.java # Portal login + OTP
│ └── WhapiService.java # WhatsApp messaging
├── dto/
│ ├── taxint/ # 15 DTOs (Taxint format)
│ ├── chrilan/ # 6 DTOs (Chrilan format)
│ └── oracle/ # 8 DTOs (Oracle STS format)
├── entity/ # ApiTransaction, ConversionLog, FiscalInvoice,
│ # StoreRegistration, User
├── repository/ # Spring Data JPA repositories (5)
├── security/
│ ├── JwtAuthenticationFilter.java
│ ├── OracleIdmJwtValidator.java # JWKS signature verification
│ └── RequestResponseLoggingFilter.java
└── exception/
├── GlobalExceptionHandler.java
└── ChrilanApiException.java
Request pipeline
HTTP request
→ RequestResponseLoggingFilter (transaction ID + raw logging, before security)
→ Security filter chain
API chain: JwtAuthenticationFilter (stateless, Bearer JWT)
Web chain: session auth (OTP-verified)
→ Controller (Taxint / Store / Reports / Web)
→ Service layer (business logic, conversion, external calls)
→ LoggingService (api_transactions, conversion_logs)
→ GlobalExceptionHandler (structured errors)
Concurrency
Property Value
Core pool size 4
Max pool size 20
Queue capacity 200
Thread prefix Taxint-Chrilan-
Rejection policy CallerRunsPolicy
Architecture decisions
Decision Rationale
Per-store API keys, no default Fiscal compliance requires store-level device registration
SellerName as primary identifier The value the POS consistently sends
Fail fast, no fallbacks Better to reject an invoice than submit wrong data to ZRA
Full request/response logging Audit trail required for fiscal debugging
Infinite data retention All logs and fiscal invoices kept forever
Fiscal invoice persistence Credit notes need original receipt lookups
PKCE for Oracle STS Required by Oracle's OAuth2 implementation
OTP via WhatsApp Most accessible 2FA channel in Zambia
Embedded Tomcat Simplifies deployment
Flyway + Hibernate validate Flyway owns structure; Hibernate validates entity alignment
priceSequence=1 pricing Dine-in price is the fiscal reference
Code standards (contributors)
One path per function — no .orElse()/.orElseGet() defaults, no ternary fallbacks; throw immediately on missing data.
Constructor injection only — all dependencies final; no field @Autowired.
SLF4J only — never System.out or printStackTrace(); all API transactions via LoggingService with transaction IDs propagated.
Per-store configuration — Chrilan keys from the database only.