Monowai

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

1

Calc engine - core product logic

computation stays where it belongs

2

Product Domain Service

canonical language · channel eligibility · entitlements · the product shelf

3

Product SDK / library

versioned · configurable · common queries · encapsulates integration

4

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

1

Core engine - calculation

2

Product Domain Service

3

product-sdk

4

End-to-end verified

the next domain

1

Domain rules - question / answer

2

Domain Service

3

domain-sdk

4

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.