service design · product domain
Service anatomy.
The domain service behind every product - so a new product in a new channel begins with a versioned SDK, not another hand-written integration.
Mike Holdsworth · monowai.com · Singapore
What if…
a new product went live as configuration
not a fresh integration project
no consumer ever guessed a contract again
the calls were known, versioned, and typed
product knowledge lived in one place
channels, entitlements and the shelf - in the language of the business
teams built in parallel, against the contract
before the upstream service even ships
the same shape served the next domain
the abstractions hold - one pattern, reused
executive summary
The reality today
The engine computes well
The vendor product engine is good at product calculations - that part works.
A thin proxy sits over it
A quotation request/response abstraction forwards calls. It owns no meaning.
Every consumer re-integrates
Each channel app hand-writes REST calls against exposed internal fields - guessing at terms, waiting on upstream deploys.
The cost is structural
onboarding cost
≈ products × consumers
Integration logic and product knowledge are distributed to every consumer - so the same work is repeated, and re-guessed, each time.
The fix is a domain-service concern - not another consumer integration.
the corporate default
How it usually gets built
Calc engine
vendor product calculation engine
“the product service”
Quote proxy
forwards quote request / response
a thin abstraction, not a domain
Channel apps
every consumer, one at a time
bespoke REST per consumer
Exposes internal fields
Consumers bind to the engine's implementation, not a business contract. Impact of change is undetectable downstream.
A thin abstraction only
Quote in, quote out - the domain shouldn't be modelled here, and isn't. Lock-step releases, no cohesion.
Guess, then wait
Understand the terms, guess the calls, wait for upstream to deploy. Every team, every time.
Three teams, three boundaries - and the domain lives in none of them.
the problem
Where the cost hides
Leaky abstraction
Internal engine fields are exposed and there is no canonical business language. Consumers bind to implementation detail.
Distributed integration
Every consumer re-implements the calls and re-derives the same product logic. The guesswork multiplies with each new client.
Sequential coupling
Wait for upstream to be enhanced and deployed, then integrate. No one can build ahead.
The result - onboarding cost scales with products × channels × consumers - the same logic, paid for again and again.
how it should be built
Expand the product domain into a platform
Calc engine - core product logic
computation stays where it belongs
Product Domain Service
canonical language · channel eligibility · entitlements · the product shelf
Product SDK / library
versioned · configurable · common queries · encapsulates integration
End-to-end tests of the consumer experience
asserts the SDK works across the product services
Every consumer
import product-sdk
A new product in a channel starts with product - not with integration.
layer 2
The Product Domain Service
The business's product vocabulary, exposed as an API - the layer the default landscape is missing.
Canonical domain language
One agreed vocabulary for the business - not engine internals leaked through a field list.
Calls the engine
Computation stays in the engine; the domain service orchestrates it behind a clean contract.
Channel & entitlement rules
Which products can be sold into each channel, and what entitlement is required to offer them.
The product shelf
Everything sellable in a channel, by a given seller - resolved in one place, one query.
Once this exists, the SDK becomes trivial - and consumers stop guessing.
layer 3
The Product SDK
Integration becomes a dependency you import - not a project each team repeats.
Versioned & configured
Clients pin a version and configure it - upgrades are deliberate, not surprises.
Encapsulates integration
All access to enabled and supported products lives inside the library, once.
Common queries built in
The everyday questions - quote, shelf, eligibility - are first-class calls.
Contract-first, not guessed
Verified against the services with Specmatic - the SDK is proven, not assumed.
// the consumer's whole integration
import { productShelf }
from 'product-sdk';
const shelf = await
productShelf({
channel: 'AGENCY',
seller: entitlement,
});
// enabled products, ready layer 4
Proven, not assumed
The SDK is verified across the product domain end-to-end - engine, domain service, SDK - and consumers inherit that guarantee.
Executable verification
Contracts are checked across engine → domain service → SDK. The stack is exercised, not eyeballed.
TDD & continuous delivery
The test framework asserts the SDK behaviour on every change - the pattern ships continuously.
Breakage fails the build
A contract change that would break a consumer fails a pipeline - not a production quote.
A breaking change fails a build, not a customer.
the pattern
The same shape serves the next domain
Underwriting, claims, onboarding - wherever rules determine the path to an outcome. Same problem, same abstractions.
product
Core engine - calculation
Product Domain Service
product-sdk
End-to-end verified
the next domain
Domain rules - question / answer
Domain Service
domain-sdk
End-to-end verified
The abstractions hold - this is a platform pattern, not a one-off. One method, applied twice, then reused across the domain.
the payoff
What changes
Today
- × Integrate per consumer
- × Guess the contract
- × Wait for upstream to deploy
- × Logic distributed everywhere
With the platform
- ✓ Configure once, in one place
- ✓ Import a typed, versioned SDK
- ✓ Build against the contract, in parallel
- ✓ Logic owned centrally
Set-up cost once; near-flat after. A new product in a new channel starts with product.
the engagement
Where we start
Beachhead: one channel
The engine and its proxy, with one consumer app as the first customer. One channel, end to end.
Stand up the pattern
Build the Product Domain Service, the SDK and the E2E test framework for that channel.
Prove it in production
The first consumer imports the SDK; a product change becomes configuration, not an integration.
Extend to the next domain
Reuse the same four-layer shape - the method, handed to the teams to own.
The ask
A bounded engagement
to establish the platform pattern and hand it to the teams to run.
A paved road for delivery - not a rebuild, and not a straitjacket.
closing thought
The service is the product.
The domain service and its SDK ship as one product - versioned, documented, built to be consumed.
A system is a collection of services and applications, composed. Integration becomes a library you import - not a rite of passage every team repeats. Design the service once, in the language of the business, and every channel starts a step ahead.
appendix
Glossary
One word, two meanings - used deliberately: the product you sell, and the product you build to sell it - the domain service and its SDK, run like one.
Calc engine
The core (often vendor) engine. Good at computing product calculations; today it exposes internal fields via its API.
Quote proxy
A thin layer over the engine with a request/response model. An abstraction, not a domain.
Product Domain Service
New. The canonical business API - language, channel eligibility, entitlements, and the product shelf.
Product SDK
Versioned, configurable client library that encapsulates all integration and access to enabled products.
Product shelf
Everything sellable in a given channel by a given seller - resolved by the domain service.
Contract-first
Define and verify the API contract (Specmatic) before and during build, so consumers never guess.