Series MapLesson 14 / 50
Focus mode active/Press Alt+Shift+R to toggle/Esc to exit
Build CoreOrdered learning track

Endpoint Engineering

Production-Grade HTTP Endpoint Engineering

Merancang endpoint JAX-RS sebagai kontrak, authorization boundary, transaction boundary, compatibility surface, dan production-operable interface dengan semantics, idempotency, conditional requests, pagination, errors, observability, dan failure modelling yang eksplisit.

21 min read4110 words
PrevNext
Lesson 1450 lesson track10–27 Build Core
#http-api#jax-rs#endpoint#idempotency+4 more

Part 014 — Production-Grade HTTP Endpoint Engineering

Endpoint production bukan sekadar method Java yang diberi @GET atau @POST. Endpoint adalah kontrak distribusi yang harus tetap bermakna ketika request diulang, response hilang, client dan server berbeda versi, data berubah concurrent, downstream gagal, dan deployment berjalan bertahap. Desain dimulai dari semantics dan invariant, bukan annotation.

Daftar Isi

  1. Target kompetensi
  2. Scope dan baseline
  3. Mental model: endpoint as distributed contract
  4. Endpoint lifecycle dan ambiguous outcomes
  5. Five endpoint contracts
  6. Resource dan URI modelling
  7. HTTP method semantics
  8. Safe, idempotent, dan retry-safe
  9. Command versus query
  10. Request dan response DTO design
  11. Status-code strategy
  12. Header strategy
  13. Create operations
  14. Read operations dan representations
  15. PUT, PATCH, dan DELETE semantics
  16. Business actions dan state transitions
  17. Bulk operations
  18. Pagination
  19. Filtering, sorting, dan sparse fields
  20. Content negotiation
  21. Cache-Control, ETag, dan conditional GET
  22. Optimistic concurrency dan lost-update prevention
  23. Idempotency keys
  24. Duplicate requests dan outcome recovery
  25. Validation, authorization, dan tenant boundaries
  26. Temporal dan monetary correctness
  27. Transaction boundary dan integration consistency
  28. Read-after-write dan consistency
  29. Long-running operations dan 202
  30. Error model
  31. Rate limiting, timeout, dan overload
  32. Versioning, compatibility, dan deprecation
  33. OpenAPI dan contract governance
  34. Security hardening
  35. Logging, metrics, tracing, dan audit
  36. Performance dan scalability
  37. Test strategy
  38. Architecture patterns dan anti-patterns
  39. Failure-model matrix
  40. Debugging playbook
  41. PR review checklist
  42. Trade-off yang harus dipahami senior engineer
  43. Internal verification checklist
  44. Latihan verifikasi
  45. Ringkasan
  46. Referensi resmi

Target kompetensi

Setelah menyelesaikan part ini, Anda harus mampu:

  • mendefinisikan endpoint dari business invariant dan HTTP semantics;
  • membedakan safe, idempotent, dan retry-safe operations;
  • memilih GET, POST, PUT, PATCH, dan DELETE berdasarkan contract;
  • membuat status-code dan header strategy yang konsisten;
  • mendesain create, read, replace, partial update, delete, action, dan bulk endpoints;
  • mencegah duplicate mutations melalui natural idempotency atau idempotency keys;
  • menangani ambiguous outcome ketika response hilang setelah mutation;
  • menggunakan ETag dan preconditions untuk caching serta lost-update prevention;
  • merancang pagination yang stabil di tengah concurrent writes;
  • menempatkan validation, authorization, tenancy, dan transaction boundaries;
  • mendesain long-running operation yang durably accepted dan reconcilable;
  • membuat error contract yang stabil, aman, machine-readable, dan operable;
  • melakukan compatibility, failure, performance, security, dan PR review.

Scope dan baseline

Part ini menggunakan:

  • HTTP semantics modern;
  • Jakarta REST 4.x;
  • Java 17+;
  • multi-instance enterprise services;
  • gateways, retries, timeouts, and asynchronous dependencies;
  • illustrative CPQ/quote/order examples.

Part ini tidak mengklaim detail internal CSG. Internal conventions untuk API governance, idempotency, error codes, tenancy, transactions, and rollout harus diverifikasi.

Endpoint production adalah durable public/internal contract. Refactoring Java tidak boleh mengubah semantics secara tidak sengaja.


Mental model: endpoint as distributed contract

flowchart TD Intent[Client Intent] HTTP[HTTP Contract] Boundary[Parse / Validate / Authenticate / Authorize] Domain[Domain Invariants] Durable[Transaction / Durable State] Integration[Events / Downstream Effects] Outcome[Response / Operation Status] Ops[Metrics / Trace / Audit / SLO] Intent --> HTTP --> Boundary --> Domain --> Durable Durable --> Integration --> Outcome --> Ops

Endpoint harus menjawab:

What does this operation mean?
Who may perform it?
Which state/invariant changes?
Can it be repeated safely?
When is success durable?
What if the response is lost?
How does a client recover?
How can an operator prove the outcome?
How can the contract evolve?

Status code saja bukan kontrak. Kontrak juga mencakup representation, headers, retry behavior, consistency, authorization, and observability.


Endpoint lifecycle dan ambiguous outcomes

sequenceDiagram participant C as Client participant G as Gateway participant J as JAX-RS Boundary participant D as Domain Service participant DB as Database participant E as Event/Integration C->>G: request G->>J: normalized request J->>D: validated command/query D->>DB: transaction DB-->>D: commit/failure D->>E: outbox/integration intent D-->>J: result J-->>G: response G-->>C: response or timeout

Possible combinations:

Client seesDurable stateMeaning
successcommittedclear success
controlled errorunchangedclear failure if guaranteed
timeout/resetunchangedambiguous
timeout/resetcommittedambiguous success
5xxcommitted before response failuredangerous ambiguity

Mutating endpoint design must provide outcome lookup or idempotent replay.


Five endpoint contracts

Transport

Method, URI, media types, headers, status codes, caching, size limits, timeout.

Data

Fields, types, required/optional/null, enums, precision, dates, schema evolution.

Behavioral

State transition, side effects, ordering, idempotency, consistency, retry semantics.

Security

Identity, permission, object access, tenant, masking, audit.

Operational

SLO, rate limits, telemetry, runbooks, ownership, deprecation.

OpenAPI can describe much of transport/data, but behavioral and operational contracts require explicit descriptions, tests, and review.


Resource dan URI modelling

Prefer stable resource nouns:

/quotes
/quotes/{quoteId}
/orders/{orderId}
/product-catalogs/{catalogId}/versions/{versionId}

Avoid leaking table names or persistence structure:

/quote_header
/order_line_tbl

Resource questions:

  • Is child lifecycle actually owned by parent?
  • Is identifier globally unique or tenant-scoped?
  • Can resource move without URI breaking?
  • Is operation a resource, query, or command?
  • Does path reveal sensitive existence?

Identifiers should be canonicalized, validated early, and never treated as authorization.


HTTP method semantics

MethodSemanticsSafeIdempotent
GETretrieve representationyesyes
HEADGET metadata without bodyyesyes
OPTIONScommunication optionsyesyes
POSTresource-specific processing/create subordinate resourcenono by default
PUTcreate/replace target statenoyes
DELETEremove target association/resourcenoyes
PATCHpartial modificationnoformat/operation dependent

Semantic violation:

@GET
@Path("/{id}/activate")
public Response activate(@PathParam("id") String id) {
    service.activate(id);
    return Response.ok().build();
}

Caches, prefetchers, crawlers, retries, and security tooling assume GET is safe.


Safe, idempotent, dan retry-safe

Safe

Requested semantics do not mutate resource state.

Idempotent

Repeating the same request has the same intended state effect.

Retry-safe

Includes implementation and side effects, not method label alone.

Example:

PUT replaces state idempotently,
but each attempt publishes a new non-deduplicated billing event.

The HTTP state may be idempotent while the end-to-end operation is not retry-safe.

Review:

  • Is there a stable logical request identity?
  • What happens if response is lost?
  • Are downstream events/effects deduplicated?
  • How long is duplicate detection retained?
  • Can same key be reused with different payload?

Command versus query

Query endpoints:

  • safe;
  • cacheable when appropriate;
  • should not trigger requested mutation;
  • may read from replica/projection if consistency is documented.

Command endpoints request state transitions:

POST /quotes/{id}/actions/submit
POST /orders/{id}/actions/cancel

A first-class command resource is useful when command has lifecycle:

POST /order-cancellations
GET  /order-cancellations/{requestId}

Choose based on status tracking, idempotency, audit, workflow, and retry needs—not aesthetic preference for CRUD.


Request dan response DTO design

Request DTO represents client intent:

public record CreateQuoteRequest(
    String customerId,
    String catalogVersion,
    OffsetDateTime requestedEffectiveAt,
    Currency currency,
    List<CreateQuoteItemRequest> items
) {}

Rules:

  • separate from persistence entity;
  • reject/define unknown fields;
  • distinguish omitted versus explicit null for partial updates;
  • cap strings, lists, nesting, and payload size;
  • exclude server-owned fields;
  • use explicit date/time and decimal semantics;
  • avoid unsafe polymorphic deserialization.

Response DTO represents stable API concepts, not database rows. Define ordering, null/empty semantics, monetary precision, effective time, version, and sensitive-field masking.


Status-code strategy

Use a bounded, governed set.

Success

StatusMeaning
200successful with result/representation
201resource created; usually include Location
202durably accepted for later processing, not completed
204success with no content

Request/current state

StatusMeaning
400malformed or generic invalid request
401authentication required/invalid
403not permitted
404absent or intentionally concealed
405method unsupported
406no acceptable response media type
409domain/current-state conflict
412HTTP precondition failed
413payload too large
415unsupported request media type
422semantically invalid content where policy uses it
428precondition required
429rate limit/overload rejection

Use HTTP status for broad protocol outcome and a stable application error code for detail.


Header strategy

Common headers:

HeaderPurpose
Locationcreated/status resource URI
ETagselected representation validator
Last-Modifiedtime validator
Cache-Controlcaching policy
Varyrequest fields affecting representation
Allowsupported methods
Retry-Afterretry timing guidance
Linktyped relationships/pagination
Idempotency-Keyorganization-defined logical request key
traceparentW3C trace context

Rules:

  • prefer standard headers where possible;
  • set size limits;
  • redact sensitive values;
  • document gateway stripping/rewriting;
  • include Vary correctly;
  • never emit internal hostnames in Location.

Create operations

Server-generated ID:

POST /quotes
Content-Type: application/json
Idempotency-Key: 6fb...

Success:

HTTP/1.1 201 Created
Location: /quotes/q-123
ETag: "7"
@POST
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public Response create(
        @HeaderParam("Idempotency-Key") String key,
        @Valid CreateQuoteRequest request,
        @Context UriInfo uriInfo) {

    CreatedQuote created = service.create(key, request);
    URI location = uriInfo.getAbsolutePathBuilder()
            .path(created.id())
            .build();

    return Response.created(location)
            .tag(Long.toString(created.version()))
            .entity(mapper.toResponse(created))
            .build();
}

Client-selected IDs may use PUT when client owns target URI and replacement/create semantics are explicit.


Read operations dan representations

GET should:

  • have no requested mutation;
  • enforce authorization before exposing state/validator;
  • define consistency source;
  • avoid unbounded graph loading;
  • provide deterministic ordering;
  • use conditional requests where valuable.
@GET
@Path("/{id}")
public Response get(
        @PathParam("id") String id,
        @Context Request request) {

    QuoteView quote = service.getAuthorized(id);
    EntityTag tag = new EntityTag(Long.toString(quote.version()));

    Response.ResponseBuilder precondition =
            request.evaluatePreconditions(tag);
    if (precondition != null) {
        return precondition.tag(tag).build();
    }

    return Response.ok(mapper.toResponse(quote))
            .tag(tag)
            .build();
}

Do not leak existence across tenant/security boundaries through status, timing, cache, or ETag.


PUT, PATCH, dan DELETE semantics

PUT

Best for complete desired state of target. Define whether omission clears fields, whether target may be created, and whether If-Match is required.

PATCH

Choose a format:

  • JSON Merge Patch;
  • JSON Patch;
  • domain-specific update command.

Generic patch formats increase representation coupling and field-level authorization complexity. Domain-specific commands often communicate intent better.

DELETE

Clarify:

  • hard/soft delete;
  • retention/legal hold;
  • cascading effects;
  • asynchronous cleanup;
  • repeated delete response;
  • restore and identifier reuse;
  • event/audit behavior.

Idempotent state effect does not require every repeated response status to be identical, but predictable governance helps clients.


Business actions dan state transitions

Some transitions are not natural CRUD:

POST /quotes/{id}/actions/submit
POST /quotes/{id}/actions/approve
POST /orders/{id}/actions/reprice

Rules:

  • define valid source states;
  • authorize the specific action;
  • support idempotency or natural command identity;
  • return a stable conflict/error when transition invalid;
  • audit actor, reason, source state, resulting state;
  • keep transition logic in domain/application layer.

Avoid generic endpoints such as /execute, /process, or /updateStatus without explicit domain semantics.


Bulk operations

Bulk design must state atomicity.

Atomic

All succeed or rollback. Suitable only for bounded size and one acceptable transaction/lock window.

Best effort

{
  "results": [
    {"id":"q1","status":"SUCCESS"},
    {"id":"q2","status":"FAILED","errorCode":"INVALID_STATE"}
  ]
}

Define:

  • per-item idempotency;
  • max batch size;
  • ordering;
  • overall HTTP status;
  • retry of failed subset;
  • partial timeout;
  • audit volume;
  • rate-limit cost.

Large bulk work usually belongs behind an operation/job resource.


Pagination

Offset

Simple and supports page jumps, but large offsets are expensive and concurrent writes cause duplicates/skips.

Cursor/keyset

GET /quotes?limit=50&after=opaqueCursor

Requirements:

  • deterministic total order;
  • tie-breaker;
  • index alignment;
  • opaque cursor;
  • integrity protection;
  • cursor version;
  • binding to tenant/filter/sort;
  • expiry policy.

Bad:

ORDER BY created_at

Better:

ORDER BY created_at, id

Exact total count can be expensive and unstable. Consider hasNext, approximate count, or optional count.


Filtering, sorting, dan sparse fields

Filtering and sorting grammars are public contracts and query-risk surfaces.

Controls:

  • allowlist fields/operators;
  • typed parsing;
  • parameterized SQL;
  • independent tenant predicate;
  • query complexity limits;
  • index/cost review;
  • canonical filter for cursor binding;
  • deterministic tie-breaker.

Never interpolate arbitrary sort input into SQL.

Sparse fields:

GET /quotes/q-123?fields=id,status,total

Benefits smaller payload, but costs include cache-key complexity, field authorization, query-plan combinations, and testing. Purpose-specific summary/detail representations may be simpler.


Content negotiation

Relevant headers:

Content-Type
Accept
Accept-Language
Accept-Encoding

Versioned media type example:

application/vnd.example.quote.v2+json
@GET
@Produces({
    "application/vnd.example.quote.v1+json",
    "application/vnd.example.quote.v2+json"
})
public Response get(...) { ... }

If representation varies by header, cache behavior and Vary must be correct. Hidden representation differences create cache and compatibility bugs.


Cache-Control, ETag, dan conditional GET

Caching questions:

  • Is response tenant/user-specific?
  • Is it sensitive?
  • Can shared caches store it?
  • What staleness is acceptable?
  • Which request headers select representation?

Examples:

Cache-Control: no-store
Cache-Control: private, max-age=60

no-cache means revalidate before reuse, not “never store.”

ETag:

ETag: "quote-123-v17"

Conditional GET:

If-None-Match: "quote-123-v17"

If unchanged, return 304. A version-based ETag is valid only if the version changes whenever the selected representation meaning changes.


Optimistic concurrency dan lost-update prevention

Race:

A reads version 10
B reads version 10
A updates → 11
B updates stale version 10

Require:

If-Match: "10"

If stale:

412 Precondition Failed

Database check:

UPDATE quote
SET requested_effective_at = ?, version = version + 1
WHERE id = ? AND version = ?;

HTTP validator and database version must represent the same state semantics.

Use 428 Precondition Required if policy requires preconditions and client omits them.


Idempotency keys

For non-idempotent commands:

POST /orders
Idempotency-Key: 8fbb4e...

Store:

tenant/client scope
key
request fingerprint
status
result resource/response reference
created/expiry timestamps
stateDiagram-v2 [*] --> Absent Absent --> InProgress: atomic claim InProgress --> Succeeded InProgress --> FailedFinal InProgress --> FailedRetryable Succeeded --> Succeeded: replay same result FailedFinal --> FailedFinal: replay terminal result

Requirements:

  • atomic unique claim;
  • same key/different payload rejected;
  • retention exceeds retry/duplicate-risk window;
  • sensitive responses not blindly stored;
  • downstream event/effect identity preserved.

Duplicate requests dan outcome recovery

Ambiguous outcomes occur when mutation may commit but response is not observed:

  • TCP reset after commit;
  • gateway timeout;
  • pod termination after outbox commit;
  • serialization failure;
  • client crash after send.

Recovery mechanisms:

  1. retry with same idempotency key;
  2. query operation status;
  3. lookup by client business key;
  4. reconciliation job;
  5. operator audit lookup.

End-to-end invariant:

same logical operation identity
→ same durable business outcome
→ same event/effect identity
→ replayable client result

HTTP deduplication alone is incomplete if Kafka or downstream side effects duplicate.


Validation, authorization, dan tenant boundaries

Validation layers:

LayerExamples
transportJSON/media type/path conversion
API shaperequired, length, format
domainvalid state transition
referencecatalog/product/effective version
databaseunique/FK/check constraints

Bean Validation protects shape; domain protects business invariants; database protects durable races.

Authorization dimensions:

  • endpoint permission;
  • resource ownership;
  • field/action permission;
  • tenant;
  • current state;
  • admin/delegated access.

Tenant context must come from trusted identity/mapping, not an arbitrary header. Propagate tenant through DB predicates, cache keys, idempotency scope, events, outbound calls, logs, and audit.


Temporal dan monetary correctness

Quote/order APIs often depend on:

  • effective date;
  • validity window;
  • catalog/pricing version;
  • timezone;
  • currency;
  • scale and rounding;
  • tax boundary.

Prefer explicit offset/instant:

{"requestedEffectiveAt":"2027-01-01T09:00:00+07:00"}

Use BigDecimal and explicit currency:

public record MoneyAmount(
    Currency currency,
    BigDecimal amount
) {}

Define scale, rounding mode, net/gross/tax semantics, authoritative calculator, and serialization. Never use double for authoritative monetary calculations.


Transaction boundary dan integration consistency

Resource method is an HTTP adapter, not automatically the correct transaction boundary.

Application service:

public SubmitQuoteResult submit(SubmitQuoteCommand command) {
    Quote quote = repository.lockOrLoad(
        command.tenantId(), command.quoteId());
    quote.submit(command.actor(), command.expectedVersion());
    repository.save(quote);
    outbox.append(QuoteSubmittedEvent.from(quote));
    return SubmitQuoteResult.from(quote);
}

Review:

  • which reads/writes must be atomic;
  • lock/isolation requirements;
  • deadlock/timeout mapping;
  • whether remote calls occur inside transaction;
  • event/audit consistency;
  • response failure after commit;
  • retry semantics.

Avoid slow remote calls while holding DB locks. Use outbox, saga, orchestration, reservation, or compensation where appropriate.


Read-after-write dan consistency

A follow-up GET may read from:

  • primary DB;
  • replica;
  • cache;
  • search index;
  • event projection;
  • materialized view.

Define:

  • strong consistency;
  • eventual consistency;
  • bounded staleness;
  • session/version-token consistency.

Do not return 201 Location to a resource that immediately returns 404 unless projection delay and recovery are part of the contract.

Mutation responses can return a version or operation token to help clients request/observe at least that state.


Long-running operations dan 202

For work exceeding practical HTTP timeouts, create an operation resource:

POST /quote-imports
→ 202 Accepted
Location: /operations/op-123
stateDiagram-v2 [*] --> ACCEPTED ACCEPTED --> RUNNING RUNNING --> SUCCEEDED RUNNING --> FAILED RUNNING --> CANCELLING CANCELLING --> CANCELLED

Define idempotency, durability, polling, retention, cancellation, result location, partial results, authorization, retries, and reconciliation.

Anti-pattern:

return 202 after putting work only into process-local memory

A crash loses work that was claimed as accepted.


Error model

A stable error document separates protocol and application semantics:

{
  "type": "https://errors.example.com/quote/invalid-state",
  "title": "Quote cannot be submitted",
  "status": 409,
  "detail": "The quote is already expired.",
  "instance": "/requests/req-abc",
  "code": "QUOTE_INVALID_STATE",
  "violations": []
}

Rules:

  • stable application code;
  • no Java exception class as contract;
  • no stack traces;
  • no unauthorized identifiers;
  • machine code separate from localized text;
  • retryability documented safely;
  • correlation reference for operator lookup;
  • field violations structured consistently.

Rate limiting, timeout, dan overload

Rate limits may scope by client, user, tenant, route, operation cost, or global capacity.

Distinguish:

contract quota
abuse protection
service overload
downstream bulkhead saturation

They may not share the same error/retry behavior.

Deadline hierarchy:

outer gateway deadline
> application budget
> downstream timeout
> cleanup/response margin

Client disconnect does not automatically rollback committed work. For mutations, outcome lookup and idempotency remain necessary.

Retry guidance should account for jitter, retry budgets, and retry storms.


Versioning, compatibility, dan deprecation

Compatibility includes:

  • URI/method;
  • status and headers;
  • fields/types/null semantics;
  • enums;
  • defaults;
  • validation strictness;
  • ordering;
  • side effects;
  • authorization;
  • cursor format;
  • error codes;
  • SLO behavior.

Common breaking changes:

  • removing/renaming fields;
  • changing type;
  • new required field;
  • stricter validation;
  • changed default sort;
  • new enum that generated clients reject;
  • changed idempotency or permission behavior.

Rolling deployment means old and new instances coexist. Database and contract changes must support both.

Deprecation needs owner, replacement, usage telemetry, support window, migration tracking, sunset decision, and removal plan.


OpenAPI dan contract governance

Governance gates can include:

  • valid OpenAPI;
  • lint rules;
  • operation ID uniqueness;
  • standard errors/security;
  • pagination convention;
  • compatibility diff;
  • generated client compile/test;
  • examples;
  • ownership metadata;
  • deprecation markers;
  • implementation conformance.

Code-first can drift from behavior. Contract-first can drift from implementation. A strong model uses reviewed contract plus automated conformance tests.

Schema compatibility alone is insufficient; behavioral compatibility must be reviewed.


Security hardening

Threats:

  • broken object-level authorization;
  • mass assignment;
  • injection;
  • excessive data exposure;
  • tenant leakage;
  • SSRF through supplied URLs;
  • replay;
  • unbounded payloads;
  • sensitive error/log leakage.

Controls:

  • typed DTO allowlist;
  • explicit mapping;
  • object/tenant-aware data access;
  • size/depth limits;
  • canonical IDs;
  • parameterized SQL;
  • outbound URL allowlists/network policy;
  • idempotency/replay controls;
  • response masking;
  • rate limiting;
  • durable audit.

Avoid generic map-to-entity updates. Invoke explicit domain mutations for authorized fields.


Logging, metrics, tracing, dan audit

Logs should record events, not arbitrary payload dumps.

Suggested fields:

operation
resource_id if policy allows
tenant/actor if policy allows
result
error_code
trace_id
duration_ms

Metrics:

  • route-normalized request rate/latency;
  • validation/auth failures;
  • conflicts and precondition failures;
  • idempotency hits/conflicts;
  • transaction retries;
  • downstream time;
  • response size;
  • rate-limit rejections.

Audit is not debug logging. It records actor, tenant, action, target, reason, state transition summary, effective time, request reference, and outcome under stricter access/retention.


Performance dan scalability

Cost:

parse + validate + authorize
+ DB reads/writes/locks
+ serialization
+ downstream calls
+ events/audit
+ response write

Query controls:

  • bounded pagination;
  • index-backed filter/sort;
  • no N+1;
  • bounded expansion;
  • correct cache/tenant semantics.

Mutation controls:

  • short transactions;
  • idempotency;
  • conflict handling;
  • outbox;
  • bounded synchronous fan-out.

Measure realistic data volume, worst-case filters, serialization/compression CPU, response size, slow-client duration, and pool/lock contention.


Test strategy

Unit

Domain transitions, mapping, validators, status/error mapping, ETag, idempotency state machine.

JAX-RS

Routing, media types, filters, exception mapping, headers, statuses.

Integration

Real PostgreSQL constraints/locking, transaction commit/rollback, idempotency race, outbox, tenant predicates.

Contract

OpenAPI conformance, generated clients, old/new client-server matrix, unknown fields/enums.

Failure

Response loss after commit, duplicate races, stale ETag, deadlock, downstream timeout, pod termination, cursor tampering, rate-limit overload.

Performance

Realistic dataset and expensive-but-valid query combinations.


Architecture patterns dan anti-patterns

Patterns

  1. Thin resource → typed command/query → application service.
  2. Idempotent command envelope with tenant/client scope and fingerprint.
  3. ETag + If-Match + DB version check.
  4. Operation resource for long-running work.
  5. Transactional outbox for state/event handoff.
  6. CI compatibility gate.

Anti-patterns

  • GET with mutation;
  • POST /doEverything;
  • persistence entity as DTO;
  • validation-only durable correctness;
  • blind retry of mutation;
  • trusting client totals;
  • unstable pagination;
  • arbitrary SQL filter/sort;
  • remote calls inside long transaction;
  • always returning 200;
  • 202 before durable acceptance;
  • stack traces/full payload logs;
  • compatibility judged only by schema.

Failure-model matrix

FailureRiskDetectionDesign response
duplicate POSTduplicate resource/effectidempotency/auditatomic key + fingerprint
response lost after commitambiguous successtrace/DB/outboxreplay same outcome
stale updatelost changesversion conflictIf-Match + DB check
unstable paginationduplicates/skipsconcurrent paging testdeterministic keyset
dynamic query abuseinjection/DB overloadSQL/plan metricsallowlist + cost limits
remote wait in transactionlock/pool exhaustiontrace/DB locksshorten transaction
non-durable 202lost accepted workreconciliation gapdurable enqueue first
outbox lagintegration delayoutbox ageretry/alert/reconcile
wrong tenant cache keydata leakisolation testtenant-bound key
enum addition breaks clientrollout failurecompatibility testtolerant client/versioning
gateway timeout shorterorphan mutationtimeline tracealigned deadlines/idempotency
response serialization failureambiguous/partial responsepost-commit errorspre-map and recover by key
old/new instances disagreenondeterminismversion-tagged metricsbackward-compatible rollout

Debugging playbook

Client timeout after mutation

  1. Capture idempotency/request ID.
  2. Locate gateway and access logs.
  3. Find distributed trace.
  4. Determine DB commit.
  5. Inspect outbox/event state.
  6. Inspect idempotency record.
  7. Query result by stable key.
  8. Replay outcome, not mutation.
  9. Fix timeout hierarchy/runbook.

Pagination duplicates

Verify deterministic tie-breaker, cursor decode, filter/sort binding, concurrent changes, and query plan.

202 never completes

Check durable acceptance, worker deployment, retries/DLQ, locks/leases, operation state, and reconciliation.

Duplicate event despite HTTP dedupe

Trace identity through HTTP record, business transaction, outbox event, producer retry, consumer dedupe, and final side-effect key.


PR review checklist

Semantics and contract

  • Method and URI express correct intent.
  • Safe/idempotent/retry behavior documented.
  • Status/header/error conventions followed.
  • Request/response DTO semantics explicit.
  • Size, precision, date/time, and ordering defined.

Correctness and concurrency

  • Transaction boundary explicit.
  • Durable constraints enforce invariants.
  • Lost-update prevention exists.
  • Idempotency claim/fingerprint/retention correct.
  • Events/downstream effects deduplicate.

Query/security

  • Pagination deterministic and bounded.
  • Filter/sort allowlisted and indexed.
  • Object/tenant authorization occurs before exposure.
  • Cache/ETag do not leak tenant/user data.
  • Logs and errors redact sensitive fields.

Compatibility/operations

  • OpenAPI and compatibility diff updated.
  • Old/new rollout works.
  • Metrics, traces, audit, and runbook exist.
  • Timeout/rate-limit/load-shedding behavior known.
  • Failure tests cover ambiguous outcomes.

Trade-off yang harus dipahami senior engineer

DecisionOption AOption B
Domain transitionCRUD representationExplicit action/command
UpdatePUT complete statePATCH partial state
PagingOffset simplicityCursor stability/performance
Work completionSynchronous final result202 operation lifecycle
Read consistencyPrimary/strongProjection/eventual
Query flexibilityGeneric filter/patchPurpose-specific API

Generic mechanisms reduce endpoint count but increase authorization, query-cost, semantics, and compatibility complexity. Optimize for durable domain intent and operability, not minimal route count.


Internal verification checklist

Governance

  • Internal API style guide.
  • Status/header/error conventions.
  • OpenAPI source of truth and linting.
  • Compatibility tooling.
  • Versioning/deprecation policy.
  • Generated client strategy.

Endpoint patterns

  • URI/action conventions.
  • PUT/PATCH semantics.
  • Pagination/filter/sort grammar.
  • Max page/bulk/payload limits.
  • ETag/precondition policy.

Idempotency/transactions/events

  • Header/key format and scope.
  • Storage, fingerprint, retention, replay.
  • Transaction manager/isolation.
  • Outbox/inbox and event identity.
  • Duplicate/reconciliation policy.

Security/tenancy

  • Identity and permission model.
  • Tenant source and propagation.
  • Object-level authorization pattern.
  • 403/404 concealment.
  • Audit and redaction policy.

Temporal/money/platform

  • Timezone/effective-date conventions.
  • Currency/scale/rounding/tax ownership.
  • Gateway retries/timeouts.
  • Rate-limit/load-shedding layer.
  • Rolling deployment compatibility.

Evidence

  • Resource classes and shared API libraries.
  • Contracts/client SDKs.
  • Gateway policies.
  • DB schema/migrations.
  • Event/outbox definitions.
  • Dashboards, incidents, and runbooks.

Latihan verifikasi

  1. Write all five contracts for one create endpoint.
  2. Drop the response after DB commit and prove idempotent replay.
  3. Race two requests with the same idempotency key.
  4. Reuse a key with different payload and verify rejection.
  5. Reproduce stale ETag update.
  6. Compare offset and keyset pagination under concurrent inserts.
  7. Attack filter/sort grammar with invalid and expensive queries.
  8. Kill a pod immediately after 202 and verify durable completion/reconciliation.
  9. Run old and new server/client versions together.
  10. Test tenant isolation through path, cache, cursor, and idempotency scope.
  11. Test monetary rounding and temporal edge cases.
  12. Perform a full PR review using the checklist and record unresolved internal evidence.

Ringkasan

Mental model:

An endpoint is a distributed contract and state-transition boundary,
not merely a Java method annotated with an HTTP verb.

Key invariants:

  1. Method semantics matter.
  2. Safe, idempotent, and retry-safe are different.
  3. Mutations require ambiguous-outcome recovery.
  4. Idempotency spans DB, events, and downstream effects.
  5. Request/response DTOs are not persistence entities.
  6. Validation, domain invariants, and DB constraints have different roles.
  7. Object and tenant authorization precede exposure.
  8. ETag/preconditions can align HTTP and DB concurrency.
  9. Pagination requires deterministic ordering.
  10. 202 requires durable acceptance and observable lifecycle.
  11. Compatibility includes behavior, defaults, auth, ordering, and side effects.
  12. Telemetry must explain logical business outcome.

Referensi resmi

Lesson Recap

You just completed lesson 14 in build core. 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.