
What happens when an e-commerce backend needs transactional consistency, flexible product data, asynchronous processing, and reliable audit logging—all at the same time?
A single database and tightly coupled services aren't always the best fit for every workload. While working on ShopEase, my full-stack e-commerce project, I worked on a backend architecture that separates storage responsibilities and decouples downstream processing using Apache Kafka.
This engineering work focuses on three areas:
- Polyglot Persistence Architecture
- CQRS-inspired, event-driven processing
- An isolated asynchronous compliance and audit pipeline
Let's explore the architecture and the problems these patterns are designed to address.
1. Polyglot Persistence: The Right Database for the Right Job
Different types of application data have different requirements. Transactional records, dynamic product attributes, and compliance logs don't necessarily benefit from the same storage model.
For ShopEase, I designed a hybrid storage architecture using three database technologies.
MySQL 8.4 — Core Transactional Data
Orders, payments, and inventory require reliable transactional operations and consistency.
MySQL handles these core transactional workloads, where ACID transactions are important for maintaining data integrity.
MongoDB — Dynamic Product Catalog
Product catalogs can contain different attributes depending on the product category. A laptop, a T-shirt, and a smartphone don't necessarily share the same fields.
MongoDB's BSON document model supports flexible, polymorphic product structures without forcing every product into an identical document shape.
PostgreSQL — Compliance and Audit Logs
Audit records have different access and retention requirements from transactional application data.
For this pipeline, PostgreSQL stores append-only compliance logs using JSONB, with GIN indexing to support queries over relevant JSON fields.
The architectural principle: choose storage based on the workload, data structure, and consistency requirements—not simply because a particular database is popular.
2. CQRS-Inspired Processing with Apache Kafka
Checkout workflows can involve several downstream operations: payment processing, inventory updates, notifications, and audit events.
Coupling all of these operations directly to the checkout request can increase dependencies and make failures harder to isolate.
In ShopEase, I worked on an event-driven design using Apache Kafka to decouple primary checkout processing from downstream services.
The high-level flow is:
- A checkout request enters the order-processing workflow.
- The relevant event is published to Kafka.
- Downstream consumers process events for their respective responsibilities.
- Payment, inventory, and notification processing can proceed independently of the synchronous request path where the workflow permits.
- Failed event processing can be routed through retry or Dead Letter Queue (DLQ) handling.
The implementation also uses manual Kafka offset acknowledgements, allowing message acknowledgement to be coordinated with processing outcomes.
This requires careful handling of retries and duplicate deliveries. Kafka delivery guarantees alone do not automatically make database updates or external payment operations idempotent.
Where CQRS fits
CQRS separates command responsibilities from query responsibilities. It is not synonymous with Kafka or microservices.
In ShopEase, the event-driven decoupling supports a CQRS-inspired architectural approach. The exact command/query separation depends on how individual read and write models are implemented.
3. An Isolated Asynchronous Audit Pipeline
Audit logging can introduce additional database writes into application workflows. If those writes share critical request-processing resources, they can contribute to contention and additional latency.
To address this architectural concern, I worked on an asynchronous audit ingestion pipeline using Spring's @Async and dedicated secondary data sources.
The intended processing flow is:
Application events → Audit listener → Asynchronous processing → PostgreSQL audit storage
This separates audit-write responsibilities from the core transaction-processing path.
The design aims to:
- Reduce coupling between core business operations and audit persistence.
- Keep audit ingestion off the main request-processing thread where configured.
- Store structured audit data using PostgreSQL JSONB.
- Support JSON-field queries using appropriate GIN indexes.
- Improve traceability and operational separation.
One important engineering consideration is reliability: asynchronous execution alone does not guarantee that every audit event will be persisted. Durable event delivery, retry policies, monitoring, and failure recovery must be considered according to the compliance requirements.
4. Putting the Architecture Together
The main components of this ShopEase backend architecture are:
- Spring Boot: application services and backend APIs.
- MySQL 8.4: core transactional data.
- MongoDB: flexible product catalog documents.
- PostgreSQL: compliance and audit records.
- Apache Kafka: asynchronous event-driven communication.
-
Spring
@Async: asynchronous audit processing. - JSONB and GIN indexes: structured audit data and JSON-oriented query support.
Each component has a distinct responsibility, helping the architecture address different data and processing requirements.
5. Key Engineering Lessons
Working on these architectural patterns reinforced several important backend engineering principles:
Database selection is a design decision. Multiple databases can be useful when workloads have genuinely different requirements, but they also introduce operational complexity.
Asynchronous processing changes failure handling. Retries, duplicate events, offset acknowledgement, and dead-letter handling need deliberate design.
Audit logging deserves its own reliability strategy. Separating audit ingestion can reduce coupling, but durability and recovery must be designed explicitly.
Distributed systems require observability. Logs, metrics, traces, and consumer-lag monitoring help identify problems across service boundaries.
What's Next?
The next step is to strengthen this architecture through integration testing, failure-injection tests, end-to-end tracing, and reproducible performance benchmarks. These help validate not just whether individual components work, but whether the complete system behaves correctly under failures and concurrent workloads.
About ShopEase
ShopEase is a full-stack e-commerce application developed by Shitanshu Jha, focused on Java backend engineering, database design, secure transactions, performance optimization, and scalable application architecture.
GitHub repositories:
Building ShopEase has been a practical journey in understanding the trade-offs behind modern backend systems—and turning architectural concepts into implementation work.
Top comments (0)