An ADR that avoided a lock-in trap
A US invoicing and payments company is about to launch in India. The platform team proposes the obvious modern answer: replicate the database to a second region with a managed multi-region feature, and get local latency and disaster recovery in one move. Writing the decision down as an ADR, and asking the people it affects before deciding, showed that the proposal would have broken an Indian regulation, rewritten the finance team's reporting, and tied the company's core data to one vendor's API.
1. The situation
Ledgerly sells invoicing and payment collection to small businesses: 1,200 customers in the US, 40 engineers, one PostgreSQL database in us-east-1 holding invoices, payments and customers, about 4 TB. It has signed its first 300 customers in India, where it will collect payments through local payment methods as a licensed payment aggregator. Launch is in five months.
The platform team's RFC: "Move invoices and payments to Amazon DynamoDB global tables with replicas in us-east-1 and ap-south-1 (Mumbai). Indian users get local reads and writes, every region holds a full copy for disaster recovery, and we stop operating a database server."
2. What mattered
Following the advice process (Module 27), the author sent the draft ADR to everyone affected: the payments team, security and compliance, finance, and the data team. Their answers became the decision drivers:
- Data residency. Compliance pointed to the Reserve Bank of India's 2018 directive: payment system providers must ensure that "the entire data relating to payment systems operated by them are stored in a system only in India". A copy abroad is allowed only for the foreign leg of a cross-border transaction.
- Latency in India. Invoice pages under 300 ms at the 99th percentile for Indian users, which a US-only database can't give.
- Reporting. Finance and customers run ad hoc reports (revenue by month, ageing of unpaid invoices) as SQL with joins and grouping, 140 distinct queries today.
- Recovery. Payments must lose at most one minute of data (RPO) and be back within 30 minutes (RTO).
- Team and lock-in. Everyone knows PostgreSQL; nobody has run DynamoDB in production. The CTO asked how hard leaving would be.
3. The options
| Option | How | What the review found |
|---|---|---|
| A. DynamoDB global tables | One table, replicas in both regions, writes anywhere | Every replica holds every item, so Indian payment data would also sit in the US: fails the RBI rule. Reporting needs an export pipeline. Writes are billed as replicated write units in each region |
| B. DynamoDB, one table per region | Separate tables, no replication; customers routed to their region | Meets residency; same reporting rewrite and data-model redesign, and the vendor's API in the core |
| C. PostgreSQL regional cells | One managed PostgreSQL cluster per region; a small global directory maps each customer to its home region | Meets residency; reporting and code unchanged inside a region; two databases to run; a customer can't write in both regions |
| D. Distributed SQL, rows pinned by region | CockroachDB with regional-by-row tables | Meets residency and keeps SQL; new technology for the team; cost and operations unknown |
4. The decision
ADR-047: Regional PostgreSQL cells for customer and payment data
Status: Accepted (supersedes the RFC proposing DynamoDB global tables)
Deciders: Head of platform (decider); consulted payments, compliance, finance, data
Revisit: when a third region is planned, or customers need to write in two regions
Context: India launch in 5 months. RBI (2018) requires payment system data to be
stored only in India. Indian p99 target 300 ms. 140 SQL reports. RPO 1 min,
RTO 30 min for payments. Team runs PostgreSQL; no DynamoDB experience.
Decision drivers: residency (must), latency, reporting, recovery, team, lock-in
Options: A global tables, B per-region DynamoDB, C regional PostgreSQL cells,
D distributed SQL (scorecard linked)
Decision: Each customer has a home region (US or India), recorded in a small
global directory. All of that customer's data lives only in its home region's
managed PostgreSQL cluster, with a synchronous standby in a second zone and
cross-zone backups inside the same country.
Consequences:
+ Residency met by construction; Indian data never leaves India
+ Reporting, queries and team skills carry over; standard SQL, no API lock-in
- Two production databases; schema changes roll out to both cells
- Moving a customer between regions is a migration, not a setting
- Cross-region totals for the board come from an aggregated, non-personal feed
Compliance: fitness function blocks any replication or backup target outside
the cell's country; quarterly restore test inside India
Dissent: Platform team preferred A for zero database operations; accepted
risk: the ops load of a second cluster. Option D to be re-evaluated at
revisit time.
5. How it works
One payment from a customer in Bengaluru, under option A and then under the decision.
6. What decides it
Score the four options with the review's weights, then change the facts. Each toggle is one thing that could have been true about Ledgerly; the winner changes with them, which is why the ADR records its drivers.
Scores from 1 to 5 against residency (weight 5 when the rule applies), Indian latency (3), SQL reporting (3 when it's needed), team familiarity (3), operations (2), lock-in (2), recovery (2), cost (2) and writes from both countries for one customer (8 when needed). Must-haves are knockouts: an option that fails residency, or can't take writes in both countries when that is needed, is out whatever it scores elsewhere. The full scorecard tool is on the Templates page.
7. How it played out
The India cell launched on time. A spike for option A had already shown that the data model needed redesigning around access patterns and that the 140 reports would need a warehouse export: about four months of work the ADR avoided. Nine months later an auditor asked where Indian payment data and its backups are stored; the answer was the ADR and its fitness function, and the audit took a day.
The cost was real too: the team now runs two clusters, and one large customer with offices in both countries had to choose a home region. That trade-off is in the ADR's consequences, so when the customer complained, the discussion started from the recorded reasoning instead of from scratch.
8. Lessons
- Lock-in hides in features, not only in APIs. "Replicate everywhere automatically" is exactly the behaviour a residency rule forbids. Check what a managed feature does with your data before adopting it (Module 33a).
- The advice process found what the authors couldn't. The platform team knew databases; compliance knew the RBI rule; finance knew the reports. Asking them before deciding cost a week (Module 27).
- Write the drivers down. The scorecard's winner depends on the facts; recording them tells the next reader when to reopen the decision (Module 06).
- Must-haves are knockouts, not weights. Weighted scoring let option A rank second despite breaking the law. Apply hard constraints first and score only the options that survive them.
- Cells are a residency tool. A home region per customer turns a legal boundary into an architectural one, the same idea as tenancy silos (Module 22a).
9. Your turn
- Find the hidden replicas (10 min). List every place your system copies customer data: replicas, backups, caches, logs, analytics exports, vendors. Which country is each in?
- Draft the ADR (15 min). Use the ADR template for a database choice you face, with drivers, at least three options and the dissent.
- Name the revisit trigger (5 min). What fact would make Ledgerly's option D the right answer?
- "We're expanding to another country. Do we replicate the database there?" First, which data may leave which country: residency rules can forbid the very replication that gives you latency. A home region per customer, with each region's data stored only there, meets residency and keeps latency local.
10. Check yourself
Why did DynamoDB global tables fail Ledgerly's residency driver?
What did Ledgerly give up by choosing regional cells?
What surfaced the residency and reporting problems?
11. Sources and further reading
| Source | Why |
|---|---|
| Storage of Payment System Data (Reserve Bank of India, April 2018) | The residency rule quoted in section 2. |
| DynamoDB global tables (AWS documentation) | Multi-region, multi-active replication of a whole table. |
| DynamoDB pricing: global tables | Replicated write units billed in each replica region. |
| CockroachDB table localities | Regional-by-row tables, option D. |