Multi-Country Retailers: Keeping Hundreds of Stores in Sync with an Event-Driven Backbone

It is Saturday, 9 a.m. The weekend promotion goes live on the website on schedule: 30 percent off the spring collection, in every country storefront at once. By 10:15, the service desk is lit up. Stores in three countries still show last week's prices at the till. A customer holds up her phone with the online price, and the cashier has to call a manager to override the register. Another customer arrives for a click-and-collect order, only to learn the item shown "in stock" was sold on the shop floor the night before.

Nobody made a mistake. The price change was approved Friday evening, after the nightly store export had already run. The website picked it up directly from the central system. The stores will not see it until the next overnight batch. The website is living in Saturday. The stores are living in Friday.

If you run IT or architecture for a retail chain with stores in many countries, you have probably lived some version of this weekend.

Why stores drift out of sync

Most multi-country retailers did not design their integration estate. They accumulated it. The ERP was rolled out country by country. E-commerce came later and was wired directly to the ERP. Store systems were connected through nightly file transfers because store links were slow when they were first built. Warehouse management uses yet another interface.

That history leaves three structural problems.

Time is baked into the integration. Batch jobs move data on a schedule, not when something happens. A price change at 9 a.m. reaches stores at 2 a.m. the next day. Stock is worse, because it changes constantly and a batch only captures a snapshot.

Every connection is point-to-point. Prices flow from the ERP to the web shop, store systems, label printers and marketing through separate pipes, each with its own format and failure mode. A new consumer means a new pipe. Changing the price model means touching all of them.

Countries differ on purpose. One country runs on a different cloud because of data residency rules. Another has a local tax engine. Central teams cannot force one stack everywhere, so integrations multiply.

The result is integration spaghetti where every change takes weeks and the slowest pipe sets the pace for the business.

Why the usual workarounds fall short

Run the batch more often. Hourly beats nightly, but batches start to overlap and fail in new ways. A store whose link drops still misses a whole window.

Call central systems directly from stores. Real-time price lookups from the till work until the store connection goes down, and then the till cannot sell.

Add an integration hub and keep the same pipes. A hub can tidy up formats, but if it still models the world as "system A sends file B to system C," the coupling remains. You have moved the spaghetti into one bowl.

Let each country solve it locally. Fast local wins make group-wide promotions, shared stock and cross-border e-commerce harder every year.

None of these address the root issue: the business runs on events, but the integration runs on schedules and direct calls.

The approach: business events as the backbone

The alternative is to publish what happens in the business as events, when it happens, and let every interested system subscribe. A few principles make this work in retail.

Model business events, not table changes. PriceChanged, PromotionActivated, StockAdjusted and OrderPlaced are facts that store, web, warehouse and finance teams all understand. Each event carries enough context (country, store, SKU, effective time) for consumers to act without calling back.

Decouple producers from consumers. Pricing publishes PriceChanged once. Stores, the web shop, labels and analytics subscribe. Adding electronic shelf labels in one country does not touch the producer.

Abstract the broker. Regions may need different messaging infrastructure: a managed cloud service in one, Kafka in a central data center in another. If code talks to a pub/sub abstraction instead of a broker client, you swap brokers per region through configuration.

Use an outbox. The classic failure is saving a price change and then failing to publish the event, or the reverse. The transactional outbox writes the state change and the outgoing event in one transaction. Either both happen or neither does.

Make consumers idempotent. At-least-once delivery means an event will sometimes arrive twice. A store must not decrement stock twice for one StockAdjusted. Event IDs, version numbers or optimistic concurrency make reprocessing harmless.

Store and forward at the edge. Store links will fail. A local component should keep accepting sales and queueing outgoing events while offline, then forward them on reconnect. Inbound events carry an effective time and a per-SKU version number, so a store that reconnects late can apply them in the right order.

Use durable workflows for long-running processes. Click-and-collect means reserving stock, picking, notifying the customer, waiting days for pickup, and releasing the reservation if nobody comes. Ship-from-store means choosing a store, picking, handing to a carrier and handling exceptions. These need state that survives restarts, timers that last days, and compensation when a step fails.

Implementing it with Dapr

Many tool combinations can build this backbone. One option retail teams evaluate is Dapr, an open-source CNCF graduated project that runs as a sidecar next to each service on Kubernetes, VMs or self-hosted environments. A fashion retailer migrated to an event-driven architecture on Dapr across 33 countries and 500+ stores, which shows the model can work at multi-country scale.

Pub/sub with pluggable brokers. Services use the Dapr pub/sub API, and the broker (Kafka, RabbitMQ, Redis or a cloud service) is defined in a component file. Two regions on different clouds can run the same code with different components. Messages use CloudEvents, delivery is at-least-once, and dead-letter topics catch events that keep failing.

State management with an outbox. The state API works with stores such as PostgreSQL, Redis, Cosmos DB, DynamoDB and MongoDB, supports ETags for optimistic concurrency, and includes a transactional outbox. ETags prevent lost concurrent updates; for duplicates, record processed event IDs.

Workflows for omnichannel orders. Dapr Workflow lets you write click-and-collect as code in .NET, Java, Python, Go or JavaScript. History is event-sourced into a state store, so after a crash the workflow replays and continues without re-running completed activities. Durable timers handle "release after 48 hours," external events handle "customer arrived," and the saga pattern handles "carrier rejected the parcel, restock the item." Keep orchestration deterministic and put calls to stores and carriers in activities.

Resiliency, observability and security. Resiliency policies set timeouts, retries and circuit breakers per component, useful when one country's ERP is slow. OpenTelemetry trace context propagates across service calls and pub/sub, so you can follow one PriceChanged from pricing to a till. Sidecars use mTLS by default, and access-control policies restrict which services can call which operations.

Store-and-forward still needs design work on your side: a local service per store with a local queue or state store. Dapr's sidecar model can run on store hardware, but test it against your own store network and hardware before committing.

Running this across many countries adds operational load: upgrades, certificate rotation, configuration drift between regions, and monitoring hundreds of services. Commercial Dapr support is available from Diagrid, founded by the people who created Dapr, alongside its free Dapr Ops Dashboard for Kubernetes, which automates upgrades and certificate rotation and flags deviations from best practice. Plenty of retailers keep all of this in-house instead. A pilot in one country is the right place to check any vendor's claims.

A practical roadmap

Avoid a big-bang replacement. Pick one painful flow and prove the model.

  1. Start with prices and promotions. High visibility, clear ownership, and the weekend-promotion problem is easy to measure.
  2. Define the contract. Agree on PriceChanged and PromotionActivated schemas with country, store scope, effective time and a unique event ID. Version from day one.
  3. Publish from the source with an outbox.
  4. Run old and new side by side. Keep the batch running while one pilot country consumes events, and compare daily.
  5. Prove store-and-forward. A store disconnected for hours must catch up correctly and in order.
  6. Move to stock and orders. Publish StockAdjusted from stores and warehouses, then build click-and-collect as a durable workflow.
  7. Roll out country by country, with per-region broker components where cloud or residency rules require them.
  8. Retire batches deliberately, only after the event flow survives a peak period.

Before each country goes live, check:

  • ☐  Event schemas documented and versioned
  • ☐  Outbox in place for every producer
  • ☐  Consumers tested for duplicate delivery
  • ☐  Dead-letter topics monitored with clear ownership
  • ☐  Store offline behavior tested with real network cuts
  • ☐  End-to-end tracing from central systems to tills
  • ☐  Rollback path to batch during the pilot

Conclusion

Stale prices at the till and failed pickups are symptoms of integration built around schedules instead of business events. Publishing events as they happen, with an outbox, idempotent consumers, store-and-forward and durable workflows for omnichannel orders, lets hundreds of stores see the same truth within moments instead of overnight. Dapr is one open-source way to build that without locking every region into one broker or cloud, and if you would rather not operate long-running order processes alone, a managed durable workflow platform is worth evaluating alongside self-hosting. Start with one event stream, measure the difference at the next promotion, and expand from there.