beginner 2 min answer Multiple choice

A retailer's "price a basket" capability is called by two things. The search grid calls it 40000 times a second and needs an answer in about 20 ms where a penny of error is acceptable because the basket is re-priced at checkout. Invoicing calls it 50 times a second and needs an exact figure that can be reproduced in an audit seven years later. A single shared pricing service is proposed on the grounds that there is one capability. What settles whether that is one service or two?

business-capabilitiesservice-boundariesquality-attributespricingduplication
Pick one
Show the full answer Hide the answer

What is being tested

Whether you can hold two ideas at once: a capability is one thing in the business and may legitimately be two things in the runtime. Beginners collapse the two, and the collapse goes in both directions — one service because the business says one capability, or two services because the two callers feel different.

The reasoning

What must be shared is the rule set; what may be duplicated is the implementation. A promotion rule, a tax rule and a rounding rule are business decisions, and if the grid and the invoice disagree about them the customer sees one price and is charged another. That is the failure mode that matters, and it is a failure of rules, not of code sharing.

The quality attributes, meanwhile, are irreconcilable. One caller wants 20 ms at 40000 requests a second and tolerates a stale promotion for a few minutes. The other wants an exact, reproducible figure and will happily wait 300 ms. A single service that satisfies both is sized for the grid and audited for invoicing, which means every change to a tax rule is a change to a 40000 RPS hot path, and every latency optimisation touches code that a finance auditor reads.

The shape that works in production: one versioned rule set with effective dates, published once, and two execution paths over it — a precomputed denormalised price for the grid, refreshed on rule change, and an exact calculation at invoice time. Add a daily reconciliation that re-prices a sample of baskets both ways and alerts on any difference beyond the documented rounding tolerance.

Why the other options fail

  • One service with a cache in front. This is the right instinct for a read-heavy capability and the wrong tool here, because the problem is not repeat reads of the same answer — the grid prices millions of distinct baskets. A cache also hides the divergence rather than detecting it.
  • Two services split by call volume. Volume tells you about capacity, not about boundaries. Split this way you get two services with two copies of the rules, which is the one thing you cannot afford, and the invoice path drifts quietly.
  • Two services split by team. Org structure is a reason to expect a boundary, not a reason to draw one. If pricing rules are owned by commercial and executed by two engineering teams, the ownership that matters is of the rule set, and that survives either runtime shape.

When not to split it

If both callers want the same accuracy and the same latency class, choose one service: a second deployment, a second on-call surface and a reconciliation job are real overhead. The test is quantitative: when the two callers' latency budgets sit within roughly one order of magnitude of each other and neither needs reproducibility years later, keep one service and stop.