In our previous deployment architecture case studies, we examined how structured discovery and capacity planning guided infrastructure decisions for operations of very different scale. The first, a B2B industrial equipment manufacturer operating at 300 orders per day, employed a monolithic architecture, a unified application server handling both user-facing operations and background processing, because batch jobs ran during dedicated off-peak windows. The second, a B2C fulfillment operation processing up to 11,500+ orders per day at peak, required a distributed architecture separating user-facing workloads from batch processing, because those jobs ran alongside user operations throughout the business day.
That progression illustrates something we see repeatedly: as an operation grows, the infrastructure supporting it does not simply get larger; it changes shape. One machine becomes several, and workloads that once shared a server move onto dedicated ones.
This document presents the next case along that curve: an omnichannel retailer fulfilling orders from 150 locations, a retail store network operating alongside distribution centers, with a catalog of 650,000 stock keeping units and order volume that varies several-fold between normal trading and peak season. Orders arrive continuously from an e-commerce storefront while store associates fulfill them throughout the trading day, and scheduled processing runs against the same system in parallel.
The architecture designed for that profile is documented here: the load analysis behind it, how compute is allocated across user-facing and background workloads, and the sizing rationale for each machine.
The architecture decisions documented here follow the same structured methodology detailed in our foundational engineering blogs:
This case study demonstrates how business model differences (omnichannel retail versus single-channel fulfillment, distributed store-based fulfillment versus centralized warehousing, continuous order flow versus defined batch windows) translate into distinct deployment architecture patterns. While the discovery methodology remains consistent, the infrastructure design diverges based on actual operational load patterns, user concurrency, and processing requirements.
The client operates a high-volume omnichannel retail business delivering products through both an e-commerce storefront and a physical retail network, where stores serve as fulfillment locations alongside dedicated distribution centers. Operations span 150 fulfillment locations with an extensive product catalog, predominantly single-item orders, and significant seasonal demand variation.
| Attribute | Description |
|---|---|
| Business Type | Omnichannel retail, direct to consumer, e-commerce storefront plus physical retail network |
| Core Operations | Order brokering, store fulfillment, ship from store, pre-order, back-order, inventory synchronization, inventory management, returns |
| Markets | Two national markets; secondary market on a separate cloud account and infrastructure |
| Order Volume | ~40,000 orders in the peak period; ~90,000 daily fulfillable items |
| Items per Order | Predominantly one |
| Sales Channels | E-commerce storefront, 140 retail stores, ship from store, pre-order, back-order |
| Fulfillment Model | 150 nodes: 140 retail stores plus 10 fulfillment centres |
| Catalog Size | ~650,000 stock keeping units |
| Key Integrations | E-commerce storefront, enterprise resource planning system, point-of-sale system, secure file transfer feeds, data pipeline |
| Cloud Platform | Amazon Web Services, client's own account, primary United States region plus secondary for backup replication |
| Environments | Production, user acceptance testing, secondary market account |
The business manages an extensive product catalog distributed across every fulfillment location, requiring inventory accuracy to be maintained network-wide rather than reconciled centrally. Because order brokering can select any store or distribution center as the fulfilling location, a stale availability position at any single node results in either an oversell or a wrongly rejected order. This network-wide inventory visibility is essential to both fulfillment reliability and customer experience.
Orders are predominantly single-item, so order volume and item volume track closely through the system. During peak seasons the operation sees demand rise several times above normal trading levels, and orders received in those periods are fulfilled progressively over subsequent days rather than clearing the same day. Daily fulfillment capacity and daily order intake are therefore distinct measures.
Apache OFBiz is the system of record for order state, inventory position and fulfillment status across all nodes.
Designing the deployment architecture began with understanding the expected load and usage patterns. Senior architecture designers and developers from the HotWax team worked through a structured set of discovery questions with the client, covering transaction volume, user concurrency, batch processing, integrations, availability and security.
The key questions and the finalized requirements are summarized below. The methodology behind these questions is covered in detail here.
| Discovery Area | Key Question | Client Requirements |
|---|---|---|
| Platform | Cloud provider and account ownership | Amazon Web Services, client's own account, under client security policy |
| Platform | Region and basis | Primary United States region (existing footprint); secondary region for backups |
| Order Volume | Transaction volume | ~40,000 orders peak period; ~90,000 daily fulfillable items; one item per order |
| Order Volume | Stock keeping units | ~650,000 |
| Users and Concurrency | Business and store users | 30 business, 150 store and fulfillment centre, 180 total |
| Users and Concurrency | Traffic load | 40 to 45 requests/second at peak season; 10 to 15 requests/second on normal days |
| Users and Concurrency | Observed peak traffic | 700 to 720 requests in the highest recorded minute |
| Sales Channels | Fulfillment locations | 140 retail stores, 10 fulfillment centres |
| Integrations | Third-party call volume | Stated 40 to 50/second; under review, not a plausible third-party volume |
| Integrations | Internal traffic | Stated 2 to 3/second; under review, assessed as understated |
| Batch Processing | Batch-heavy? | Yes: 47 scheduled jobs, ~1,370 executions daily |
| Infrastructure | Servers and specifications | Full specification in section 3.5 |
| Availability | Compliance and availability | General data protection regulation; high availability; two availability zones |
| Database | Disaster recovery | Multi-availability-zone enabled; multi-region planned this year; cross-region backups in place |
| Database | Backup retention | 7 days |
| Security | Internet-facing controls | Web application firewall, distributed denial of service protection, transport layer security termination, internet protocol allow-listing, administrative authentication |
Together, these processes ensure that orders move from entry to fulfillment across a distributed store network while keeping all connected systems updated with accurate inventory and status information.
To design deployment architecture that performs reliably under real production conditions, we first analyzed how orders flow through the system across different operational patterns.
The operation experiences significant seasonal variation, with major holiday periods driving peak demand:
Daily Order Overview
| Season | Orders/Period | Items/Order (avg) | Daily Fulfillable Items |
|---|---|---|---|
| Normal trading | Pending confirmation | 1 | n/a |
| Peak season | ~40,000 (peak period) | 1 | ~90,000 |
What this means:
Order volume can also be derived from the measured request rate, which is the firmer of the two inputs:
Once order volume and items per order are established, we move to how those orders generate system load across different operational areas.
User actions create API requests as store associates and business users interact with the fulfillment applications and the commerce platform.
Store and Fulfillment Centre User Load:
Business User Load:
Internet-Facing Traffic:
Total User-Facing Requests:
Expressed per user across the 180-user population:
Peak season represents 3.3–3.8 times the observed normal peak (2,700 ÷ 720), and this is the figure infrastructure is sized against. Averaging request counts across the full day is not appropriate for this workload: load is concentrated rather than uniform, and a daily average produces a figure too low to design against.
Background jobs are measured by execution time (how long they run) and frequency (how often they run), not requests per minute. The deployment runs 47 scheduled jobs producing approximately 1,370 executions per day.
| Job Class | Jobs | Frequency | Runs/Day | Calculation |
|---|---|---|---|---|
| Order import and status synchronization | 12 | Every 15 min, hourly, daily | 340 | (2 × 96) + (6 × 24) + (4 × 1) = 340 |
| Product and catalog synchronization | 6 | Every 15 min, hourly, every 6 hrs, daily | 245 | (2 × 96) + (2 × 24) + (1 × 4) + (1 × 1) = 245 |
| Inventory synchronization | 7 | Every 30 min, hourly, daily | 100 | (1 × 48) + (2 × 24) + (4 × 1) = 100 |
| Pre-order and back-order catalog feeds | 2 | Every 15 min, daily | 97 | (1 × 96) + (1 × 1) = 97 |
| Returns processing | 1 | Every 15 min | 96 | (1 × 96) = 96 |
| Order brokering and re-brokering | 8 | Every 30 min (8 AM–2 PM), daily | 19 | (1 × 12) + (7 × 1) = 19 |
| Financial and reporting feeds | 4 | Every 2 hrs, daily | 15 | (1 × 12) + (3 × 1) = 15 |
| Procurement feeds | 2 | Daily | 2 | (2 × 1) = 2 |
| Order management application | 42 | n/a | 914 | Sum of the above |
| Fulfillment feed generation (integration services) | n/a | Every 20 min, every 30 min | 240 | (2 × 72) + (2 × 48) = 240 |
| Fulfillment feed transfer (data pipeline) | n/a | Every 20 min | 216 | (3 × 72) = 216 |
| TOTAL | 47 | n/a | ~1,370 | 914 + 240 + 216 = 1,370 |
The five fulfillment feed jobs each run a generation leg on the integration services machine and a transfer leg on the data pipeline machine, staggered by 5–10 minutes so the two do not collide. The highest frequency anywhere in the deployment is every 15 minutes.
Daily Total Execution Time Calculation:
Execution time per run is not instrumented, so daily totals are expressed against the formula. Applied to the 914 runs carried by the asynchronous tier:
At approximately 1 minute per run, the expected order of magnitude for feed-driven jobs of this type, daily batch execution lands at 15.2 hours, within the 13–17.9 hours measured for the B2C warehouse operation. Beyond 1.58 minutes average, the work exceeds what a single machine can complete in a day, which is what makes the two-machine asynchronous tier necessary.
Job Overlap Calculation:
What sizes an asynchronous machine is not total volume but simultaneous execution:
Peak Batch Processing Window:
Unlike the B2B manufacturing case study where batch jobs ran during dedicated off-hours (midnight–4 AM), this operation runs batch processing throughout the trading day:
Batch jobs overlap with store fulfillment operations throughout the trading day, creating resource contention. This overlap is the primary driver for distributed architecture.
Normal Day (10–15 requests/second, 30–45 orders/minute)
| Load Type | Volume | Requests per Minute |
|---|---|---|
| User-facing requests | 600–900 requests/minute | ~720 req/min at the highest recorded minute |
| Background job execution | ~1,370 executions/day | 7.6–15.2 hours/day on the asynchronous tier |
Peak Season (40–45 requests/second, 120–135 orders/minute)
| Load Type | Volume | Requests per Minute |
|---|---|---|
| User-facing requests | 2,400–2,700 requests/minute | 3.3–3.8× the observed normal peak |
| Background job execution | ~1,370 executions/day | Same job count; execution time extends under load |
Critical Observation: Unlike the B2B manufacturing deployment where batch processing consumed 4.25 hours daily during dedicated off-hours, and closer to the B2C warehouse operation which ran 13–17.9 hours during operational hours, this deployment runs approximately 15.2 hours of batch processing on its asynchronous tier, spread across the full trading day. With 20 jobs scheduled to fire in the same minute at the top of most hours, resource contention with user-facing operations is continuous rather than periodic.
Daily totals don't reveal the real-time load infrastructure must handle. During operational hours, multiple workloads compete for system resources simultaneously.
Store fulfillment operations run throughout the trading day across 150 locations, while the e-commerce storefront accepts orders continuously, including outside store trading hours. Scheduled processing runs against the same system in parallel, with recurring jobs at 15-minute, 30-minute and hourly intervals, and daily feed jobs clustering in a pre-open window (05:45–08:15) and a night window (21:00–00:15).
Concurrent User Activity:
Each active user maintains 2–3 database connections for fulfillment application operations, open pick lists, pack stations and real-time inventory reservations. Combined concurrent activity requires approximately 360–540 database connections during peak hours, with a further 30–50 held by background jobs, giving approximately 390–590 connections in total.
Apache OFBiz's entity engine translates each user action into multiple database operations. Brokering across 150 candidate fulfillment locations amplifies database load.
Order Processing – a single order involves:
Total: 50–70 database operations per order
Single-item orders reduce the line-item component of this total, but brokering across 150 candidate locations increases the routing component by a comparable amount. The per-order total therefore sits in the same band as the B2C warehouse operation despite a very different order profile.
Fulfillment – pick-pack-ship process for one order:
Total: 34–54 database operations per order fulfillment
| Scenario | Requests/Minute | Operations/Minute |
|---|---|---|
| Normal day | 600–900 | ~1,927–2,890 ops/min |
| Highest recorded minute | 700–720 | ~2,248–2,312 ops/min |
| Peak season | 2,400–2,700 | ~7,707–8,670 ops/min |
Database operations are derived at 3.2113 operations per request, the ratio measured in the B2C warehouse operation (2,280 operations per minute against 710 requests per minute at peak).
These database transaction patterns drive deployment architecture sizing decisions around database memory allocation, connection pooling, Multi-AZ configuration, and the critical decision to separate batch processing from user-facing operations.
Batch Processing During Operational Hours:
Unlike the B2B manufacturing case study where batch jobs ran during dedicated midnight–4 AM windows, this operation runs resource-intensive batch processing throughout the trading day:
Resource Contention Without Separation:
Running batch jobs on the same server as user-facing operations creates these problems:
Traffic Pattern Analysis:
Each workload class runs on its own machine, and the two heaviest run on a pair. This is a deliberate allocation rather than a consequence of scale. Dedicating separate machines to user-facing and background processing keeps each workload running smoothly without competing for the same cores, memory or connections, and allows each to be tuned and scaled independently for the work it actually does.
Order Management Main Servers (2):
Asynchronous Processing Servers (2):
Integration Services Application:
Apache Solr Search:
Without distributed architecture at this volume, a monolithic server would see user request response times degrade to 800–1,500ms during batch windows, database connection pool exhaustion at roughly double the available connections, and no ability to scale user-facing and background workloads independently.
Based on load analysis and the distributed architecture strategy, we propose the following production environment. This architecture separates user-facing operations across a pair of main application servers while isolating background processing on dedicated asynchronous servers.
Fig – Proposed Deployment Architecture for OmniChannel fulfilment
Instance sizing is grounded in measured throughput rather than estimation:
| Server | Instance | vCPU | RAM | Storage | Workload | Peak Load |
|---|---|---|---|---|---|---|
| Main App Server ×2 | 4 vCPU / 8 GB | 8 total | 16 GB total | 100 GB EBS (gp3) | User-facing ops only | 2,700 req/min |
| Async Processing Server ×2 | 8 vCPU / 16 GB | 16 total | 32 GB total | 100 GB EBS (gp3) | Background jobs only | 20 concurrent job starts |
| Integration Services | 4 vCPU / 8 GB | 4 | 8 GB | 100 GB EBS (gp3) | Third-party API traffic | 240 feed runs/day |
| Apache Solr | 4 vCPU / 16 GB | 4 | 16 GB | 100 GB EBS (gp3) | Product catalog search | 650,000 SKUs |
| Data Pipeline / SFTP | 2 vCPU / 4 GB | 2 | 4 GB | 200 GB EBS (gp3) | File feed exchange | 216 transfer runs/day |
| Monitoring | 2 vCPU / 4 GB | 2 | 4 GB | 50 GB EBS (gp3) | Metric collection | 11 instances |
| Jenkins CI/CD | 2 vCPU / 4 GB | 2 | 4 GB | 30 GB EBS (gp3) | Deployment builds | 5–10 builds/week |
Server notes:
Why the Main Tier Runs as a Pair: Peak season requires 7.5 vCPU at a 50% target, calculated as 2,700 ÷ (720 × 0.5), against the 8 provisioned across two machines. A pair distributes that load evenly at 46.9% each, absorbs the loss of one machine outside peak season at 25%, and allows rolling deployment with no maintenance window.
| Database | Instance | vCPU | RAM | Storage | Purpose |
|---|---|---|---|---|---|
| Primary Database | 8 vCPU / 32 GB (Multi-AZ) | 8 | 32 GB | SSD (gp3), auto-expanding | Production workload |
| Read Replica | 8 vCPU / 32 GB | 8 | 32 GB | SSD (gp3), auto-expanding | Production reads, reporting, backup testing |
Database notes:
Why 8 vCPUs Are Necessary: Peak load generates 8,670 database operations per minute across order processing (50–70 ops/order), fulfillment (34–54 ops/order), brokering across 150 candidate locations, 1,370 daily background job executions, and concurrent sessions from 180 users. With 50% of reads served by the replica, the primary carries 4,335 operations per minute, or 48.8% of an 8 vCPU instance at the measured 1,112 operations per minute per vCPU.
Storage Provisioning: Database storage is provisioned conservatively with auto-expansion enabled rather than allocated generously up front. Relational database volumes cannot be reduced in place; shrinking one requires taking a backup and rebuilding the instance from it, so initial provisioning is an irreversible commitment made on the least reliable information available.
Backup and Recovery Policy:
All infrastructure resides within a single AWS VPC in the primary United States region, distributed across two availability zones. The main application servers accept HTTPS traffic (port 443) from store associates via a load balancer, while asynchronous processing servers, integration services and Apache Solr remain in private subnets accessible only within the VPC. The database primary and read replica sit in private subnets with no outbound internet access.
Key Security Controls:
The network design prioritizes security without sacrificing operational efficiency: store associates across 150 locations access systems securely via HTTPS while internal components communicate over the AWS private network backbone.
AWS CloudWatch tracks infrastructure health and application performance. Key metrics include CPU utilization (alert at >70% sustained), memory usage (alert at >85%), database connection count (alert at >480), and replication lag (alert at >30 seconds).
| Severity | Response Time | Trigger Conditions | Routing |
|---|---|---|---|
| P1 Critical | Immediate | Database failures, loss of both main servers, brokering failures during operational hours | PagerDuty – immediate response |
| P2 High | 15 minutes | Single main server loss, CPU >70% sustained, memory >85%, order import failures >5% | PagerDuty |
| P3 Medium | 1 hour | Batch job delays >30 min, disk space >80%, replication lag >30 seconds | Email + Slack |
| P4 Low | Next day | Non-critical warnings, integration endpoint errors within tolerance | Email + Slack |
Application-level monitoring tracks order import success rates, brokering completion times, re-brokering volumes, inventory synchronization lag across the 150 locations, and store associate response times. Because 20 jobs are scheduled to fire in the same minute at the top of most hours, batch job execution duration is monitored as a production metric rather than an operational detail, because a job that overruns its scheduling interval queues against its own next run.
The distributed deployment architecture designed for this omnichannel retail operation demonstrates how requirements fundamentally shape infrastructure decisions. Unlike the B2B manufacturing case study where monolithic architecture efficiently served batch-processing-during-off-hours patterns, and extending beyond the B2C warehouse operation where batch processing overlapped a 9-hour operational window, this deployment separates workloads across paired machines because 47 scheduled jobs run continuously throughout the trading day while an e-commerce storefront accepts orders around the clock.
Notably, order volume alone does not determine architecture. This deployment fulfills from 150 locations against a catalog 217 times larger than the B2C warehouse operation, yet peak database throughput lands within one and a half percent of it: 2,312 operations per minute against 2,280. What drives the architecture is the shape of the workload: how many jobs run, when they run, and whether they overlap with the people depending on the system at the same moment.
This case study reinforces the core methodology: structured discovery translates operational patterns into infrastructure decisions. The same discovery process produces different architectures: monolithic with off-hours batch processing for B2B manufacturing, distributed with a single machine per workload for B2C warehouse fulfillment, and paired machines per workload class for omnichannel retail fulfilling from 150 locations, because the underlying business models and operational patterns differ fundamentally.