Focus mode active/Press Alt+Shift+R to toggle/Esc to exit
Deepen PracticeOrdered learning track

Microservice Data Ownership and Bounded Context Model

Model data ownership dan bounded context untuk enterprise microservices in CPQ/Quote/Order/Billing systems, termasuk aggregate ownership, service boundary, source of truth, shared database anti-pattern, reference by ID, local projection, read model, integration events, cross-context consistency, and production correctness.

13 min read2575 words
PrevNext
Lesson 4882 lesson track46–68 Deepen Practice
#enterprise-data-modelling#microservices#bounded-context#data-ownership+6 more

Microservice Data Ownership and Bounded Context Model

1. Core Idea

Microservice data modelling starts with one question:

Which service owns which data, and who is allowed to change it?

In enterprise CPQ / Quote / Order / Billing / Catalog / Telco BSS/OSS, many concepts are connected:

  • customer,
  • account,
  • catalog,
  • price,
  • quote,
  • order,
  • product inventory,
  • service inventory,
  • billing account,
  • charge,
  • invoice,
  • approval,
  • user/authority,
  • fulfillment task.

If every service reads and writes every table, system becomes distributed monolith.

Mental model:

Each bounded context owns its data and invariants. Other contexts reference, request, subscribe, or project that data, but do not casually mutate it.


2. Why Data Ownership Matters

Without clear ownership:

  • two services update quote status differently,
  • order service changes product inventory directly,
  • billing service recomputes quote price from internal quote tables,
  • catalog change breaks order decomposition,
  • reporting reads inconsistent tables,
  • services join across databases,
  • one schema migration breaks many services,
  • no one owns data quality,
  • incident root cause becomes unclear,
  • duplicate source of truth emerges.

Microservice correctness requires ownership boundaries.


3. Bounded Context

Bounded context is a boundary where a model has a specific meaning.

Example:

TermQuote ContextOrder ContextBilling Context
ItemQuote line/proposal componentExecution instructionInvoice/charge line
StatusQuote lifecycleOrder lifecycleInvoice/payment lifecycle
PriceProposed/accepted amountExecution billing referenceCharge/invoice amount
CustomerBuyer contextOrder party contextPayer/invoice context

Same word can mean different things in different contexts.

Do not force one universal model if contexts have different semantics.


4. Possible Bounded Contexts in Quote-to-Cash

Typical contexts:

ContextOwns
Customer/AccountCustomer, account, organization hierarchy, contacts.
CatalogProduct offering/specification, characteristics, rules.
PricingPrice lists, price rules, discount rules, price calculation.
QuoteQuote header/item, quote version, quote snapshot, approval request link.
ApprovalApproval request, step, decision, authority evidence.
OrderProduct order, order items, order lifecycle.
FulfillmentDecomposition, fulfillment task, fallout, external work tracking.
Product InventoryProduct instance, product status/history.
Service/Resource InventoryTechnical service/resource instance.
BillingBilling account, charge, invoice, payment reference.
Identity/AuthorityUsers, roles, permissions, commercial authority.
IntegrationOutbox/inbox/message tracking, external system mapping.
Reporting/AnalyticsRead models/facts, not usually source of truth.

Actual internal boundaries must be verified.


5. Source of Truth

For each entity, define source of truth.

Example table:

EntitySource of truthOther services do
Product offeringCatalog serviceReference offering ID/version.
QuoteQuote serviceRead via API/event projection.
OrderOrder serviceSubscribe to order events.
Product instanceProduct inventory serviceRequest changes via order/fulfillment.
Billing accountBilling/customer/billing systemValidate/reference/snapshot.
InvoiceBilling systemMirror status/reference.
Approval decisionApproval serviceQuery/subscribe decision.
User roleIAM/identity serviceCache/evaluate via auth service.

If two systems claim to own same mutable field, clarify ownership immediately.


6. Ownership vs Local Projection

A service may store data it does not own as local projection.

Example:

Order service owns order.

Billing service may store projection:

order_projection
- order_id
- order_number
- customer_id
- billing_account_id
- status
- last_event_version

Billing service uses this for lookup, but does not change order status.

Projection is not source of truth.

Rules:

  • mark projection table clearly,
  • update from events/sync,
  • store source version,
  • monitor lag,
  • never use projection to override owner data.

7. Reference by ID and Snapshot

Cross-context references should use stable IDs and snapshots where needed.

Example order item:

product_offering_id
product_offering_version
product_offering_name_snapshot

Why include snapshot?

  • catalog service may be unavailable,
  • offering name may change,
  • historical order must remain understandable,
  • report/export needs stable display.

Pattern:

Data typeUse
ID referenceFor lookup and identity.
Version referenceFor historical correctness.
Snapshot fieldsFor audit/display/stability.
ProjectionFor local query optimization.

8. Avoid Shared Database Anti-Pattern

Microservice anti-pattern:

Quote service writes quote table.
Order service reads quote table directly.
Billing service joins quote, order, charge tables directly.
Fulfillment service updates order_item.status directly.

Problems:

  • hidden coupling,
  • unsafe migrations,
  • invariant bypass,
  • unclear ownership,
  • no service boundary,
  • hard-to-debug side effects.

Preferred:

  • owner service exposes API/commands/events,
  • consumers maintain projections,
  • integration uses events/messages,
  • reporting uses curated data pipeline/read model.

There may be legacy exceptions. If so, document and contain them.


9. Aggregate Ownership

Aggregate is a consistency boundary.

Examples:

AggregateOwnerInvariants
QuoteQuote serviceQuote version, item totals, approval requirement.
Product orderOrder serviceOrder lifecycle, item state aggregation.
Product instanceProduct inventory serviceProduct status, active characteristics.
Billing accountBilling/customer serviceBillable status, payer profile.
Approval requestApproval serviceStep/decision aggregation, authority evidence.
Catalog offeringCatalog serviceOffering version, publication status.

Only owner should enforce aggregate invariants and mutate aggregate state.


10. Cross-Aggregate Consistency

Microservices cannot always update multiple aggregates atomically.

Example:

Quote accepted -> Order created -> Product activated -> Charge activated

This is eventually consistent.

Use:

  • events,
  • outbox/inbox,
  • saga/orchestration,
  • reconciliation,
  • idempotency,
  • compensating actions,
  • status tracking.

Do not pretend distributed transaction exists unless explicitly implemented.


11. Command Across Context

If one context needs another to act, send command/request.

Example:

Quote service should not create order rows directly.

Better:

Quote service emits QuoteConversionRequested
Order service creates ProductOrder
Order service emits ProductOrderCreatedFromQuote
Quote service records conversion completed

Or synchronous API:

POST /orders
{
  "sourceQuoteId": "...",
  "sourceQuoteVersion": 4,
  "idempotencyKey": "..."
}

The receiving service validates and owns creation.


12. Event Across Context

If one context needs to notify others, publish integration event.

Example:

ProductInstanceActivated

Billing service consumes and activates recurring charge.

Important:

  • producer owns event fact,
  • consumer owns its side effects,
  • consumer must be idempotent,
  • event schema is contract,
  • correlation ID preserved,
  • reconciliation monitors gaps.

13. Local Read Models

Read models optimize queries crossing contexts.

Example support screen needs:

  • customer name,
  • quote number,
  • order status,
  • product status,
  • billing account,
  • latest invoice,
  • open fallout.

Do not make UI call ten services synchronously if latency/reliability suffers.

Create read model:

customer_support_summary
- customer_id
- account_id
- active_product_count
- open_order_count
- open_fallout_count
- latest_invoice_status
- projection_updated_at

Read model is derived, not source of truth.


14. Reporting Context

Analytics/reporting often needs cross-domain joins.

Do not make operational services share databases just for reporting.

Better:

  • event stream to warehouse/lake,
  • CDC to analytics store,
  • curated fact/dimension models,
  • semantic layer,
  • governed metric definitions,
  • data quality checks.

Reporting can use denormalized facts, but operational command logic should use source-of-truth services.


15. Ownership Matrix

Every major entity needs ownership matrix.

Example:

Entity/fieldOwnerMutable byRead byNotes
quote.statusQuote serviceQuote service onlyOrder, reporting via events/APICommand-driven.
order.statusOrder serviceOrder service onlyQuote, fulfillment, billing projectionsDerived from item state.
product_instance.statusProduct inventoryInventory/fulfillment commandsBilling, supportUpdate after fulfillment proof.
billing_account.statusBilling/account serviceBilling/account serviceQuote/order validationMay be external-owned.
price_rulePricing servicePricing adminQuote pricingVersioned.
invoice.statusBilling systemBilling systemSupport/reporting projectionExternal reference.

This matrix is extremely useful for onboarding and PR reviews.


16. Data Contract Between Contexts

A data contract defines what one context promises another.

For events:

Event name
Schema version
Required fields
Field semantics
Ordering key
Idempotency expectation
PII classification
Retention expectation
Compatibility policy

For APIs:

Endpoint
Request/response schema
Error model
Idempotency behavior
Rate limits
Authorization model
SLA
Backward compatibility

For projections:

Source event/API
Projection owner
Freshness SLA
Rebuild strategy
Data quality checks

17. Reference Data Ownership

Reference data can be tricky.

Examples:

  • country code,
  • currency code,
  • status enum,
  • reason code,
  • product category,
  • tax category,
  • approval reason,
  • cancellation reason.

Decide ownership:

  • centralized reference-data service,
  • per bounded context,
  • static code/config,
  • external master data.

Avoid each service inventing different reason codes for the same business process.

But also avoid forcing unrelated contexts into one global enum when semantics differ.


18. Shared Kernel

Sometimes contexts share a small common model.

Examples:

  • money value object,
  • currency code,
  • correlation ID,
  • tenant ID,
  • standard error model,
  • event envelope.

Keep shared kernel small and stable.

Do not put large domain models into shared library:

shared QuoteEntity used by quote, order, billing

This recreates monolith coupling.


19. Anti-Corruption Layer

When integrating with external or legacy system, use anti-corruption layer.

Example:

External billing system uses:

Account = payer + contract + invoice group

Internal model has separate:

customer, account, billing account, agreement

Do not leak external model everywhere.

Create mapping layer:

billing_account_mapping
external_billing_account_id
local_billing_account_id
mapping_status
last_synced_at

Anti-corruption layer protects internal model integrity.


20. Cross-Service ID Strategy

IDs must be stable and unambiguous.

Options:

  • UUID internal ID,
  • business number,
  • external ID,
  • composite natural key,
  • tenant-prefixed ID.

Cross-service references should specify:

entity_type
entity_id
source_system
entity_version if needed

Avoid assuming UUID alone is globally meaningful if multiple systems can produce IDs.


21. Tenant and Data Boundary

If multi-tenant:

  • tenant_id must be part of ownership and access boundary,
  • events should carry tenant_id,
  • projections should include tenant_id,
  • indexes should support tenant-scoped queries,
  • cross-tenant references should be prevented,
  • support/admin access should be audited.

Tenant boundary is data ownership at another dimension.


22. Consistency Pattern: Saga

Saga coordinates multi-step flow.

Example quote-to-order-to-billing:

Quote accepted
  -> Create order
  -> Decompose order
  -> Fulfill order
  -> Activate product
  -> Activate charge

Saga state:

saga_instance
- id
- saga_type
- business_key
- status
- current_step
- correlation_id
- started_at
- completed_at

Saga step:

saga_step
- saga_instance_id
- step_name
- status
- retry_count
- compensation_action

Saga may be orchestrated by workflow or choreography via events.


23. Consistency Pattern: Reconciliation

Because eventual consistency can fail, reconciliation is mandatory.

Examples:

  • quote accepted but no order,
  • order fulfilled but no product instance,
  • product active but no charge,
  • billing account updated but order projection stale,
  • event published but consumer projection missing.

Reconciliation belongs to production data model, not only ops script.

Store results:

cross_context_reconciliation_result
- source_context
- target_context
- entity_type
- entity_id
- expected_state
- actual_state
- mismatch_code
- severity
- checked_at
- repair_status

24. Ownership and Authorization

Ownership affects authorization.

Example:

  • Quote service owns quote data.
  • Authorization service owns permission.
  • Quote service calls authorization/authority check before mutation.
  • Quote service still enforces quote lifecycle invariant.

Do not let downstream service mutate owned data just because it has database access.

Ownership and permission are separate:

Service owns data.
User has permission.
Application service enforces both.

25. Data Ownership and Migration

Microservice boundaries evolve.

Migration scenarios:

  • split quote and pricing service,
  • move billing account ownership to billing system,
  • extract approval service,
  • replace legacy order DB,
  • introduce product inventory service.

Migration needs:

  • dual read/write strategy,
  • ownership cutover date,
  • event backfill,
  • projection rebuild,
  • data reconciliation,
  • API compatibility,
  • legacy table freeze,
  • incident rollback plan.

Document temporary dual ownership carefully.


26. PostgreSQL Physical Design for Projections

Projection table example:

create table order_projection_for_billing (
  order_id uuid primary key,
  order_number text not null,
  customer_id uuid not null,
  billing_account_id uuid,
  source_quote_id uuid,
  status text not null,
  aggregate_version integer not null,
  last_event_id uuid not null,
  projected_at timestamptz not null
);

Projection tracking:

create table projection_checkpoint (
  projection_name text primary key,
  source_name text not null,
  last_event_id uuid,
  last_offset text,
  last_processed_at timestamptz,
  status text not null,
  error_message text
);

Read model should clearly signal source and freshness.


27. Java/JAX-RS Backend Implications

Service should expose operations for owned data.

Example:

Quote service:
  POST /quotes
  POST /quotes/{id}/accept
  GET /quotes/{id}

Order service:
  POST /orders
  POST /orders/{id}/cancel
  GET /orders/{id}

Avoid service A repository writing service B tables.

Application service should:

  • enforce ownership,
  • validate local invariants,
  • call external context via API/event,
  • store references/snapshots,
  • use outbox for events,
  • update local projection only from external events.

28. MyBatis/JPA/JDBC Implications

Data access layer should not cross bounded context.

Bad:

QuoteRepository joins order table and billing table.

Better:

Quote service stores quote-owned tables.
Quote service has order_projection if needed.
Billing data fetched via billing API/projection.

For monolith modular codebase, still apply logical ownership:

  • module-owned tables,
  • repository per module,
  • no cross-module write,
  • explicit service interface.

29. Event and Projection Failure Modes

Failure modeSymptomCausePrevention
Projection staleUI shows old order statusConsumer lagProjection checkpoint/lag monitor
Duplicate projection updateWrong countNon-idempotent consumerAggregate version/inbox
Owner bypassedInvalid stateDirect DB write by another serviceOwnership enforcement
Data driftProjection differs from sourceMissed eventReconciliation
Schema migration breaks consumerRuntime failureShared DB or event breaking changeContract versioning
Circular dependencyServices call each other synchronouslyPoor boundary designEvents/read models
Chatty integrationHigh latency/failureNo local projectionDenormalized read model
Dual ownershipConflicting updatesUnclear source of truthOwnership matrix

30. Reporting and Analytics Impact

Operational ownership differs from analytical modelling.

Analytics may create:

  • quote fact,
  • order fact,
  • product snapshot fact,
  • billing revenue fact,
  • customer dimension,
  • product dimension,
  • account hierarchy dimension.

Analytics can join across domains, but must preserve:

  • source system,
  • data freshness,
  • metric definition,
  • historical hierarchy,
  • event/effective date,
  • data quality flags.

Do not let analytical fact table become operational source of truth.


31. Data Quality Checks

-- Projection stale compared to expected freshness
select projection_name, last_processed_at, status
from projection_checkpoint
where status <> 'HEALTHY'
   or last_processed_at < now() - interval '15 minutes';

-- Projection version regression check pattern
-- Store and compare aggregate_version; consumers should reject lower versions.

-- Orders created from quote but quote projection missing source quote
select order_id, order_number
from order_projection_for_billing
where source_quote_id is null
  and order_number is not null;

Actual checks depend on projection tables.


32. Security and Privacy

Ownership protects privacy too.

Risks:

  • projection contains more data than consumer needs,
  • service stores sensitive copied data without retention policy,
  • events broadcast PII/margin/billing data,
  • access controls differ between source and projection,
  • deleted/anonymized data remains in projections.

Controls:

  • minimal projection fields,
  • data classification,
  • access control on read models,
  • projection purge/anonymization,
  • event payload minimization,
  • ownership of retention policy.

33. PR Review Checklist

When reviewing cross-service data changes, ask:

  • Which bounded context owns this data?
  • Is this source-of-truth or projection?
  • Is any service writing data it does not own?
  • Is reference by ID/version enough or is snapshot needed?
  • Is integration via API, event, or shared DB?
  • Are events versioned and outbox-backed?
  • Are consumers idempotent?
  • Is projection freshness monitored?
  • Is reconciliation available?
  • Is data duplicated for read model with clear ownership?
  • Does this create circular dependency?
  • Does this expose sensitive data to another service?
  • Does reporting rely on operational projection incorrectly?
  • Is ownership matrix updated?
  • Are migration/cutover implications handled?

34. Internal Verification Checklist

Verify these in the internal CSG/team context:

  • Actual service/module boundaries for quote, order, catalog, billing, product inventory, approval, fulfillment.
  • Which services own which tables/entities.
  • Whether database-per-service exists or shared DB with logical ownership.
  • Whether any service writes another service's tables.
  • Whether local projections exist and how they are updated.
  • Whether ownership matrix exists.
  • Whether outbox/inbox is used between contexts.
  • Whether APIs or events are primary integration mechanism.
  • Whether shared libraries contain domain models.
  • Whether external systems own billing/customer/inventory data.
  • Whether anti-corruption layer exists for external/legacy models.
  • Whether reporting reads operational DBs or curated analytics store.
  • Whether reconciliation exists between contexts.
  • Whether incidents mention projection drift, shared table coupling, duplicate source of truth, or cross-service migration failure.

35. Summary

Microservice data ownership is a correctness boundary.

A strong model must define:

  • bounded contexts,
  • aggregate ownership,
  • source of truth,
  • reference vs snapshot,
  • local projection,
  • read model,
  • integration events,
  • command/API boundaries,
  • shared database restrictions,
  • cross-context consistency,
  • saga/reconciliation,
  • projection freshness,
  • ownership matrix,
  • migration strategy,
  • security and retention for copied data.

The key principle:

In microservices, the most important data modelling question is not “where is the table?” but “who owns the truth, who may change it, and how everyone else learns about it safely.”

Lesson Recap

You just completed lesson 48 in deepen practice. Use the series map if you want to review the broader track, or continue directly into the next lesson while the context is still warm.

Continue The Track

Keep the momentum while the lesson is still fresh. Move backward for review or continue forward into the next concept.