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

API Contract and DTO Boundary Model

Model API contract dan DTO boundary untuk enterprise CPQ/Quote/Order/Billing systems, termasuk REST/JAX-RS resource model, request/response DTO, command DTO, read DTO, versioning, backward compatibility, validation, error model, pagination, filtering, idempotency key, correlation ID, and production correctness.

12 min read2215 words
PrevNext
Lesson 4782 lesson track46–68 Deepen Practice
#enterprise-data-modelling#api-contract#dto#jax-rs+6 more

API Contract and DTO Boundary Model

1. Core Idea

API contract adalah bentuk publik/eksternal dari domain data model.

Dalam enterprise CPQ / Quote / Order / Billing / Telco BSS/OSS, data model tidak hanya hidup di database. Ia juga muncul sebagai:

  • REST resource,
  • JAX-RS endpoint,
  • request DTO,
  • response DTO,
  • command DTO,
  • event payload,
  • integration payload,
  • error response,
  • pagination/filtering contract,
  • validation error,
  • OpenAPI schema,
  • client SDK model.

Mental model:

Database model is not API model. Entity is not DTO. Internal domain model is not external contract.

API contract harus stabil, aman, versioned, backward-compatible, dan tidak membocorkan detail internal yang tidak boleh menjadi kontrak.


2. Why API Contract Modelling Matters

API modelling yang lemah menyebabkan masalah production:

  • client rusak karena field diubah/rename,
  • internal enum bocor ke external API,
  • database schema change menjadi breaking API change,
  • entity JPA/MyBatis langsung diserialisasi,
  • sensitive field seperti margin/cost muncul di response,
  • PATCH update melewati lifecycle command,
  • generic DTO membolehkan illegal state change,
  • error response tidak bisa diproses client,
  • pagination tidak stabil,
  • filter query lambat karena tidak align dengan index,
  • idempotency key tidak dipakai untuk create/convert,
  • correlation ID hilang dari request chain.

API contract adalah boundary. Boundary yang buruk membuat internal model menjadi sulit berubah dan rentan salah.


3. Entity vs Domain Model vs DTO

LayerPurpose
Database tablePersistence and query optimization.
Entity/recordPersistence or domain representation.
Domain modelBusiness behavior and invariant.
Request DTOExternal input contract.
Response DTOExternal output contract.
Command DTOSemantic business action request.
Read model DTOQuery/projection optimized output.
Event payloadAsync integration contract.

Bad pattern:

@GET
@Path("/quotes/{id}")
public QuoteEntity getQuote(...) {
    return quoteRepository.find(id);
}

Risks:

  • exposes internal fields,
  • lazy-loading serialization bug,
  • circular references,
  • sensitive fields leak,
  • database change breaks API,
  • lifecycle invariants bypassed by generic updates.

Better:

QuoteEntity -> QuoteDomain -> QuoteResponseDto
CreateQuoteRequestDto -> CreateQuoteCommand -> QuoteDomain

4. API Resource Model

REST resources should align with domain concepts, not table names blindly.

Examples:

/quotes
/quotes/{quoteId}
/quotes/{quoteId}/items
/quotes/{quoteId}/submit-for-approval
/quotes/{quoteId}/accept
/quotes/{quoteId}/convert-to-order

/orders
/orders/{orderId}
/orders/{orderId}/items
/orders/{orderId}/cancel
/orders/{orderId}/amendments

/billing-accounts/{billingAccountId}
/product-instances/{productInstanceId}
/approval-requests/{approvalRequestId}

Endpoint should express business operation.

Avoid:

PATCH /quote-table/{id}
PATCH /status-column/{id}
POST /execute?action=approve

Unless there is a strong internal reason.


5. Command-Oriented APIs

Lifecycle operations should be command APIs, not generic patch.

Examples:

POST /quotes/{id}/submit-for-approval
POST /quotes/{id}/approve
POST /quotes/{id}/accept
POST /quotes/{id}/cancel
POST /quotes/{id}/convert-to-order

Command request example:

{
  "reasonCode": "CUSTOMER_ACCEPTED",
  "comment": "Signed acceptance received",
  "idempotencyKey": "accept-Q-100-v4-001"
}

Why command API is safer:

  • explicit intent,
  • specific validation,
  • authorization per command,
  • audit reason required,
  • lifecycle transition controlled,
  • side effect/event controlled,
  • easier idempotency,
  • clearer documentation.

6. Request DTO Design

Request DTO should contain only fields client is allowed to provide.

Example create quote request:

{
  "customerId": "customer-id",
  "accountId": "account-id",
  "billingAccountId": "billing-account-id",
  "currency": "USD",
  "channel": "SALES_PORTAL",
  "items": [
    {
      "productOfferingId": "offering-id",
      "quantity": 1,
      "siteId": "site-id",
      "configuration": {
        "bandwidth": "500Mbps"
      }
    }
  ]
}

Do not allow client to set:

  • internal ID,
  • quote number if system-generated,
  • approval status,
  • lifecycle status,
  • total amount if calculated,
  • created_by,
  • margin,
  • audit fields,
  • internal workflow ID,
  • converted order ID.

Request DTO is an allowlist, not a mirror of entity.


7. Response DTO Design

Response DTO should be stable and consumer-friendly.

Example:

{
  "id": "quote-id",
  "quoteNumber": "Q-10001",
  "version": 4,
  "status": "PRICED",
  "customer": {
    "id": "customer-id",
    "name": "GlobalCorp"
  },
  "accountId": "account-id",
  "currency": "USD",
  "totalAmount": "500000.00",
  "validUntil": "2026-08-01T00:00:00Z",
  "links": {
    "self": "/quotes/quote-id",
    "items": "/quotes/quote-id/items"
  }
}

Response DTO may include:

  • stable identifiers,
  • display values,
  • summary values,
  • status,
  • version,
  • links,
  • current allowed actions if useful.

It should not automatically include every internal child collection.


8. Command DTO vs Update DTO

A generic update DTO often creates bugs.

Bad:

{
  "status": "APPROVED",
  "discountPercent": 25,
  "validUntil": "2026-12-31"
}

This mixes multiple commands:

  • approve quote,
  • change discount,
  • extend validity.

Better:

ApplyDiscountCommand
ApproveQuoteCommand
ExtendQuoteValidityCommand

Each command has:

  • specific validation,
  • specific permission,
  • specific audit reason,
  • specific side effects,
  • specific reapproval rules.

9. Read DTO vs Write DTO

Read and write DTOs should often differ.

Read DTO:

{
  "quoteNumber": "Q-10001",
  "status": "APPROVED",
  "totalAmount": "500000.00",
  "approvalSummary": {
    "status": "APPROVED",
    "approvedAt": "2026-07-12T10:00:00Z"
  }
}

Write DTO:

{
  "reasonCode": "CUSTOMER_REQUEST",
  "comment": "Please cancel this quote"
}

Do not reuse response DTO as update DTO.

Response contains derived/read-only fields. Write DTO should contain command input only.


10. DTO Versioning

APIs evolve.

Common strategies:

StrategyExample
URL versioning/v1/quotes
Header versioningAccept: application/vnd.company.quote.v1+json
Additive fieldsAdd optional response fields.
New endpointCreate new contract for breaking change.
Field deprecationKeep old field while adding new field.

Backward-compatible changes:

  • add optional response field,
  • add optional request field with default,
  • add enum value if clients tolerate unknown,
  • add new endpoint.

Breaking changes:

  • remove field,
  • rename field,
  • change type,
  • change meaning,
  • make optional field required,
  • change enum semantics,
  • change pagination contract,
  • change error shape.

11. Enum Contract

Internal enum and external enum may differ.

Example internal:

QUOTE_STATUS_APPROVAL_PENDING_INTERNAL_REVIEW

External:

PENDING_APPROVAL

External enum should be stable and documented.

Clients must be prepared for unknown enum values if possible.

Do not expose temporary/internal states unless clients need them.

Example:

{
  "status": "IN_PROGRESS",
  "statusReason": "WAITING_FOR_APPROVAL"
}

This can be more stable than dozens of status enum values.


12. Money and Decimal DTO

Money must be modelled carefully.

Avoid floating point.

Use string decimal or exact decimal representation:

{
  "amount": "1234.56",
  "currency": "USD"
}

Do not use:

{
  "amount": 1234.559999999
}

Money DTO should include:

  • amount,
  • currency,
  • scale/precision convention if needed,
  • tax included/excluded semantics,
  • period if recurring/usage.

13. Time and Date DTO

Time fields must be explicit.

Use ISO-8601.

Examples:

{
  "createdAt": "2026-07-12T10:00:00Z",
  "validUntil": "2026-08-01T00:00:00Z",
  "billingStartDate": "2026-08-01"
}

Distinguish:

  • timestamp/instant,
  • local date,
  • business period,
  • timezone,
  • effective date.

Do not serialize local server time without timezone.


14. Validation Error Model

Validation errors should be structured.

Example:

{
  "errorCode": "VALIDATION_FAILED",
  "message": "Request validation failed",
  "correlationId": "corr-123",
  "violations": [
    {
      "field": "items[0].siteId",
      "code": "REQUIRED",
      "message": "siteId is required for this product offering"
    },
    {
      "field": "billingAccountId",
      "code": "BILLING_ACCOUNT_INACTIVE",
      "message": "Billing account is not active"
    }
  ]
}

Client should not parse free-text error message.


15. Business Error Model

Business errors differ from validation errors.

Examples:

  • illegal state transition,
  • quote expired,
  • approval required,
  • insufficient authority,
  • idempotency conflict,
  • product not serviceable,
  • billing account on credit hold,
  • price changed/reprice required,
  • order already converted.

Business error response:

{
  "errorCode": "QUOTE_APPROVAL_REQUIRED",
  "message": "Quote requires approval before acceptance",
  "correlationId": "corr-123",
  "details": {
    "quoteId": "quote-id",
    "requiredApprovalType": "DISCOUNT_APPROVAL"
  }
}

Use stable error codes.


16. HTTP Status Strategy

Common mapping:

HTTP statusUse
200Successful read/update response.
201Created resource.
202Accepted asynchronous command.
204Successful command with no body.
400Malformed/invalid request.
401Not authenticated.
403Authenticated but not allowed.
404Resource not found or not visible.
409State conflict/idempotency conflict/concurrent modification.
422Business validation failed if used by API standard.
429Rate limit.
500Unexpected server error.
503Service unavailable.

Define strategy consistently across services.


17. Pagination Model

For list endpoints:

GET /quotes?customerId=...&status=APPROVED&pageSize=50&pageToken=...

Response:

{
  "items": [],
  "nextPageToken": "token",
  "hasMore": true
}

Offset pagination can be unstable for high-change data. Cursor/token pagination is often safer.

Pagination contract should define:

  • max page size,
  • default page size,
  • sort order,
  • stable cursor,
  • filter semantics,
  • total count availability.

18. Filtering and Sorting

Filtering must align with indexed query patterns.

Bad:

GET /orders?filter=anyFieldContainsAnything

This invites slow queries and inconsistent semantics.

Better:

GET /orders?customerId=...&status=IN_PROGRESS&updatedAfter=...
GET /product-instances?billingAccountId=...&status=ACTIVE

For complex search, use dedicated search/read model.

Do not let public API expose arbitrary SQL-like filters unless intentionally designed.


19. Partial Update and PATCH

PATCH is dangerous if it allows arbitrary field mutation.

Safe PATCH needs:

  • field allowlist,
  • optimistic locking,
  • validation,
  • audit,
  • reason for sensitive fields,
  • material change detection,
  • permission per field/action.

For lifecycle or commercial changes, prefer command endpoints.

Example acceptable PATCH:

PATCH /quote-drafts/{id}
{
  "displayName": "Enterprise Renewal Quote"
}

Example risky PATCH:

PATCH /quotes/{id}
{
  "status": "APPROVED",
  "totalAmount": "1.00"
}

20. Optimistic Locking in API

For mutable resources, expose version/ETag.

Example:

GET /quotes/{id}
ETag: "quote-v4"

PUT /quotes/{id}
If-Match: "quote-v4"

Or request field:

{
  "expectedVersion": 4,
  "changes": {}
}

If version mismatch:

409 Conflict

This prevents lost updates.


21. Idempotency Key

Create and command endpoints should support idempotency where duplicate request is harmful.

Examples:

  • create quote from external request,
  • accept quote,
  • convert quote to order,
  • create order,
  • create billing adjustment,
  • trigger billing charge,
  • submit fulfillment request.

Header:

Idempotency-Key: quote-123-v4-convert-001

Store:

idempotency_key
request_hash
response_reference
status
created_at
expires_at

If same key with different payload arrives, return conflict.


22. Correlation ID

Every API should accept/return correlation ID.

Header:

X-Correlation-Id: corr-123

Use it in:

  • audit,
  • logs,
  • outbox events,
  • workflow,
  • integration messages,
  • error responses.

If client does not provide one, generate it.

Correlation ID is essential for incident debugging.


23. Sensitive Field Exposure

Response DTO must enforce field-level access.

Sensitive fields:

  • margin,
  • cost,
  • internal discount threshold,
  • credit score/status,
  • payment method details,
  • tax identifiers,
  • billing address,
  • service/resource identifiers,
  • internal workflow IDs,
  • raw payload references.

Use projection per role:

QuotePublicDto
QuoteSalesDto
QuoteDealDeskDto
QuoteFinanceDto

Or dynamic field filtering with strong tests.


24. OpenAPI and Schema Governance

OpenAPI should document:

  • endpoints,
  • request/response DTOs,
  • enums,
  • error codes,
  • pagination,
  • headers,
  • idempotency key,
  • correlation ID,
  • examples,
  • deprecation notices,
  • security scheme.

But OpenAPI generated from code is not enough if semantics are unclear.

Document business meaning:

validUntil is exclusive timestamp.
status is external lifecycle status.
totalAmount excludes tax unless taxIncluded = true.

25. JAX-RS Implementation Considerations

In Java/JAX-RS:

  • keep resource classes thin,
  • map DTO to command,
  • validate DTO at boundary,
  • call application service,
  • map domain/read model to response DTO,
  • handle exceptions centrally,
  • add correlation ID filter,
  • avoid exposing entity directly.

Structure:

QuoteResource
  -> QuoteMapper
  -> QuoteCommandService
  -> QuoteQueryService
  -> ErrorMapper

Example:

@POST
@Path("/{quoteId}/convert-to-order")
public Response convertToOrder(
    @PathParam("quoteId") UUID quoteId,
    @HeaderParam("Idempotency-Key") String idempotencyKey,
    ConvertQuoteRequest request
) {
    ConvertQuoteCommand command = mapper.toCommand(quoteId, idempotencyKey, request);
    ConvertQuoteResult result = service.convert(command);
    return Response.accepted(mapper.toResponse(result)).build();
}

26. DTO Mapping Anti-Patterns

Avoid:

  • entity returned directly,
  • entity accepted as request body,
  • Map<String,Object> everywhere,
  • generic update endpoint for lifecycle changes,
  • exposing DB IDs without external reference strategy,
  • leaking internal enum,
  • response DTO with lazy-loaded cycles,
  • DTO containing both input and derived output fields,
  • silently ignoring unknown fields without policy,
  • changing field meaning without versioning.

27. Data Model for API Request Tracking

For critical APIs:

api_request_audit
- id
- method
- path_template
- actor_id
- client_id
- idempotency_key
- correlation_id
- request_hash
- response_status
- error_code
- started_at
- completed_at

Do not store full payload if sensitive. Store hash/reference.

This helps:

  • client dispute,
  • retry debugging,
  • duplicate command analysis,
  • incident review.

28. API Contract and Event Contract Alignment

Synchronous API and async event should align semantically.

Example:

POST /quotes/{id}/accept

should produce:

QuoteAccepted event

But response DTO and event payload do not need to be identical.

API response serves caller. Event serves subscribers.

Both should share stable domain meaning.


29. Testing API Contracts

Contract tests should cover:

  • required fields,
  • optional fields,
  • unknown enum behavior,
  • error response shape,
  • idempotency behavior,
  • optimistic lock conflict,
  • permission failure,
  • validation failure,
  • backward compatibility,
  • OpenAPI compatibility,
  • serialization of money/date/time.

For enterprise APIs, test more than happy path.


30. Failure Modes

Failure modeSymptomLikely causePrevention
Client breaks after deployField removed/renamedNo API versioningBackward compatibility governance
Sensitive data leakMargin/payment exposedEntity serialized directlyDTO allowlist/field permissions
Illegal transitionStatus patched directlyGeneric update endpointCommand API
Duplicate orderClient retries convertNo idempotency keyIdempotency store
Lost updateTwo users edit quoteNo optimistic lockingETag/version
Slow list APIArbitrary filtersQuery not aligned to indexGoverned filters/read model
Client cannot handle errorsFree-text errorNo error code modelStructured error response
Wrong money precisionRounding mismatchFloating point JSON/Java doubleDecimal/string money model
Timezone disputeValidity off by one dayAmbiguous date/timeISO/timezone convention
API becomes DB contractSchema change breaks APIEntity-as-DTOMapping layer

31. PR Review Checklist

When reviewing API/DTO changes, ask:

  • Is this request DTO, response DTO, command DTO, or event payload?
  • Does it expose internal entity fields?
  • Are sensitive fields protected?
  • Is the endpoint command-oriented where needed?
  • Does it require idempotency?
  • Does it require optimistic locking?
  • Is error response stable and structured?
  • Are money/date/time fields precise?
  • Are enum values external and documented?
  • Is pagination/filtering stable and indexed?
  • Is correlation ID propagated?
  • Does this change break existing clients?
  • Is OpenAPI updated?
  • Are contract tests updated?
  • Are permission/authority checks server-side?
  • Are audit fields generated by system, not client?

32. Internal Verification Checklist

Verify these in the internal CSG/team context:

  • API versioning standard.
  • JAX-RS resource conventions.
  • DTO mapping framework or manual mapper style.
  • Whether entities are ever exposed directly.
  • Standard error response format.
  • Standard correlation ID header.
  • Standard idempotency key header/behavior.
  • Optimistic locking strategy.
  • Pagination/filtering conventions.
  • OpenAPI generation/publishing process.
  • Field-level permission strategy.
  • Sensitive fields in quote/order/billing APIs.
  • API compatibility testing process.
  • Whether external API differs from internal service API.
  • Whether TM Forum APIs are used/adapted and how extensions are handled.
  • Whether incidents mention breaking API change, leaked field, duplicate create, or illegal status patch.

33. Summary

API contract and DTO boundary model protect service boundaries.

A strong API data model must define:

  • resource model,
  • command endpoints,
  • request DTO,
  • response DTO,
  • read DTO,
  • DTO/entity separation,
  • versioning,
  • enum strategy,
  • money/time formats,
  • error model,
  • pagination/filtering,
  • idempotency,
  • optimistic locking,
  • correlation ID,
  • sensitive field protection,
  • OpenAPI/schema governance,
  • contract testing.

The key principle:

Do not let internal data structures become public contracts. API DTOs are boundary models that must preserve business semantics while protecting internal flexibility and production correctness.

Lesson Recap

You just completed lesson 47 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.