# Low-Level Design: [Component name]

> Low-level design (LLD), also called detailed design, for one component
> of a solution: its internal modules and classes, the API and event
> contracts it provides and consumes, its data model and mappings, key
> sequences and states, business rules, error handling and retries,
> configuration, security implementation, telemetry, performance budget,
> test approach and build and deployment notes. It is the build
> specification that a developer can implement and a reviewer can check.
> Design views follow the concepts of IEEE 1016-2009 (design concerns,
> viewpoints and views); contracts are expressed in OpenAPI 3.2 and
> AsyncAPI 3.1.0, and error responses in the RFC 9457 problem details
> format. Replace every `[bracketed]` value and every row marked
> *Example*.

<!--
Guidance: How to use this template
- Write one LLD per component listed in the High-Level Design
  (`architecture/high-level-design`). The HLD fixes the component's
  responsibility, interfaces, NFR budgets, security controls and
  resilience tier; this LLD shows how the component meets them. If you
  need to change one of those inputs, raise it against the HLD first.
- Start with sections 2 (inputs) and 5 (contracts). Contracts are what
  other teams depend on; internal structure can change later without
  telling anyone.
- Keep the machine-readable artefacts (OpenAPI, AsyncAPI, migration
  scripts, configuration schema) in the repository and link them; this
  document explains them, it does not duplicate them.
- How this differs from neighbours: Software Design Description
  (`architecture/software-design-description`) covers a whole software
  system with the full IEEE 1016 viewpoint set; Technical Design Doc
  (`architecture/technical-design-doc`) proposes a change and argues
  alternatives; Microservice Specification
  (`architecture/microservice-spec`) is a shorter service card. The LLD
  is the complete, implementable design of one component inside an
  approved HLD.
-->

## Document control

<!-- Guidance: Tie the LLD to its HLD version and component ID, so reviewers know which fixed inputs apply. -->

| Field | Value |
|---|---|
| Document ID | [LLD-002] |
| Component | [CMP-002 Order service] |
| Parent HLD | [HLD-001, version x.y] |
| Repository | [Repository URL and path] |
| Version | [0.1] |
| Status | [Draft / In review / Approved / Superseded] |
| Owner | [@component-tech-lead] |
| Approvers/reviewers | [@solution-architect], [@security-champion], [@sre], [@qa-lead] |
| Owning team | [Team name] |
| Last updated | YYYY-MM-DD |
| Classification | [Internal / Confidential] |

### Approvals

| Role | Name | Decision | Date |
|---|---|---|---|
| Component technical lead (author) | [@name] | [Submitted] | YYYY-MM-DD |
| Solution architect | [@name] | [Approve / Approve with conditions / Reject] | YYYY-MM-DD |
| Security champion | [@name] | [Decision] | YYYY-MM-DD |
| SRE or operations | [@name] | [Decision] | YYYY-MM-DD |
| Quality lead | [@name] | [Decision] | YYYY-MM-DD |

### Revision history

| Version | Date | Author | Change summary |
|---|---|---|---|
| 0.1 | YYYY-MM-DD | [@name] | First draft |
| 0.2 | YYYY-MM-DD | [@name] | *Example:* Added refund flow and error catalogue after review |
| 1.0 | YYYY-MM-DD | [@name] | *Example:* Approved for implementation of release 1 |

## Contents

<!-- Guidance: Keep this list aligned with the headings. -->

1. [Component identification and scope](#1-component-identification-and-scope)
2. [Design inputs and constraints](#2-design-inputs-and-constraints)
3. [Internal structure](#3-internal-structure)
4. [Classes and modules](#4-classes-and-modules)
5. [Interfaces and contracts](#5-interfaces-and-contracts)
6. [Data design and mappings](#6-data-design-and-mappings)
7. [Behaviour: sequences and states](#7-behaviour-sequences-and-states)
8. [Business rules and algorithms](#8-business-rules-and-algorithms)
9. [Error handling, retries and idempotency](#9-error-handling-retries-and-idempotency)
10. [Concurrency, transactions and consistency](#10-concurrency-transactions-and-consistency)
11. [Configuration and feature flags](#11-configuration-and-feature-flags)
12. [Security implementation](#12-security-implementation)
13. [Logging, metrics and tracing](#13-logging-metrics-and-tracing)
14. [Performance and capacity](#14-performance-and-capacity)
15. [Test approach](#15-test-approach)
16. [Build, deployment and runtime](#16-build-deployment-and-runtime)
17. [Traceability and open issues](#17-traceability-and-open-issues)
18. [Glossary and references](#18-glossary-and-references)

## 1. Component identification and scope

<!-- Guidance: Restate the component's single responsibility from the HLD in your own team's words, and say what it must never do. Pitfall: scope creep, where the LLD quietly takes on responsibilities that belong to another component. -->

| Item | Value |
|---|---|
| Responsibility | *Example:* Own the order lifecycle from submission to completion: validate, price snapshot, persist, transition states and publish order events |
| Must not | *Example:* Call the payment provider directly, hold card data, write to the warehouse system |
| Provides | *Example:* IF-001 Orders REST API, IF-003 order events |
| Consumes | *Example:* IF-005 Payment orchestrator create-intent API, IF-006 payment events, IF-004 daily price file (through the price cache), identity provider token keys |
| Runtime | *Example:* Containerised service on the platform Kubernetes cluster, [language and version], [framework and version] |
| Design concerns addressed | *Example:* Correctness of state transitions, idempotent submission, latency budget, auditability |

## 2. Design inputs and constraints

<!-- Guidance: List the fixed inputs from the HLD and other documents with their IDs. Everything below must be consistent with this list. -->

| Input | ID | Value taken from source | Source |
|---|---|---|---|
| Latency budget *(Example)* | NFR-PE-001 | 300 ms p95 inside the Order service for POST /orders; payment intent (IF-005) budgeted separately at 350 ms p95 with a 450 ms timeout | HLD section 9, ADR-004 |
| Availability tier *(Example)* | NFR-RE-001 | T1, 99.98 % allocation, three replicas over three zones | HLD sections 9 and 11 |
| Throughput *(Example)* | NFR-PE-002 | 50 orders per second minimum, 60 target, sustained for 60 min | NFR Catalogue |
| Recovery objectives *(Example)* | NFR-RE-002 | RPO 5 min, RTO 1 h (database owned by platform) | HLD section 11 |
| Broker independence *(Example)* | ADR-003 | Order capture continues while the broker is down; events wait in the outbox | HLD section 11 failure table |
| Event publication *(Example)* | ADR-003 | Transactional outbox, at-least-once delivery | Decision log |
| Audit *(Example)* | NFR-SE-006 | Every agent change to an order is logged with actor and reason | NFR Catalogue |
| Coding standard *(Example)* | Not applicable | Organisation coding standard version [x] | Coding Standards (`dev/coding-standards`) |
| [Input] | [ID] | [Value] | [Source] |

## 3. Internal structure

<!-- Guidance: Show the modules or layers inside the component and the allowed dependency direction. Questions: where do business rules live? Which module talks to the outside world? Pitfall: domain logic leaking into controllers or repositories, which makes rules impossible to test in isolation. -->

```mermaid
flowchart TB
  API["api: HTTP controllers, request validation"] --> APP["application: use cases, transactions"]
  CONS["messaging: event consumers"] --> APP
  APP --> DOM["domain: Order aggregate, rules, state machine"]
  APP --> PORTS["ports: repository and client interfaces"]
  ADP["adapters: database, payment client, cache, outbox relay"] --> PORTS
```

| Module | Responsibility | May depend on | Must not depend on |
|---|---|---|---|
| api *(Example)* | Map HTTP to commands and queries, validate against OpenAPI schema, map errors to problem details | application | adapters, database driver |
| application *(Example)* | One class per use case, transaction boundary, authorisation checks on resource ownership | domain, ports | api, framework web types |
| domain *(Example)* | Order aggregate, value objects, state machine, pricing snapshot rules | Nothing outside the standard library | Any framework or I/O |
| ports *(Example)* | Interfaces for repositories, payment client, clock, ID generator | domain | adapters |
| adapters *(Example)* | Implement ports: SQL repository, HTTP payment client, outbox relay, cache client | ports, libraries | api |
| messaging *(Example)* | Consume payment and shipment events, call use cases | application | adapters directly |
| [Module] | [Responsibility] | [Allowed] | [Forbidden] |

Enforce the dependency rules with an automated architecture test in the
build (see Fitness Functions (`architecture/fitness-functions`)).

## 4. Classes and modules

<!-- Guidance: Show the key types and their relationships, not every class. Include anything another developer must understand before changing the code. -->

```mermaid
classDiagram
  class Order {
    +OrderId id
    +CustomerId customerId
    +OrderStatus status
    +Money total
    +List~OrderLine~ lines
    +confirmPayment(PaymentRef)
    +cancel(Reason)
    +markShipped(ShipmentRef)
  }
  class OrderLine {
    +Sku sku
    +int quantity
    +Money unitPrice
  }
  class OrderRepository {
    <<interface>>
    +get(OrderId) Order
    +save(Order, List~DomainEvent~)
  }
  class SubmitOrderHandler {
    +handle(SubmitOrder) OrderId
  }
  Order "1" *-- "1..*" OrderLine
  SubmitOrderHandler ..> OrderRepository
  SubmitOrderHandler ..> Order
```

| Type | Kind | Purpose | Invariants or notes |
|---|---|---|---|
| Order *(Example)* | Aggregate root | Holds order state and enforces transitions | Total equals sum of lines; status changes only through methods |
| Money *(Example)* | Value object | Amount and ISO 4217 currency | Never a floating-point type; one currency per order |
| SubmitOrderHandler *(Example)* | Use case | Validates command, checks idempotency key, creates order, requests payment intent | Single transaction for order row and idempotency record |
| OutboxRelay *(Example)* | Adapter | Reads unpublished outbox rows and publishes them | Publishes in commit order per order ID |
| [Type] | [Kind] | [Purpose] | [Notes] |

## 5. Interfaces and contracts

<!-- Guidance: Specify every operation this component provides and every call it makes, with the contract file as the source of truth. Questions: what is idempotent? What does each error mean to the caller? How are versions changed? Pitfall: documenting only the happy path response. -->

### 5.1 Provided API (IF-001)

Contract file: `[path]/orders-v1.openapi.yaml` (OpenAPI 3.2). Rules: see
API Design Guidelines (`architecture/api-design-guidelines`).

| Operation | Method and path | Request | Success | Errors | Idempotent | Auth scope |
|---|---|---|---|---|---|---|
| Submit order *(Example)* | POST /v1/orders | Lines, delivery address, channel ID, `Idempotency-Key` header | 201 with order ID and payment redirect | 400, 401, 403, 409, 422, 429, 503 | Yes, by key for 24 h | `orders:write` |
| Get order *(Example)* | GET /v1/orders/{id} | Path ID | 200 with order | 401, 403, 404 | Yes | `orders:read` |
| List my orders *(Example)* | GET /v1/orders?cursor= | Cursor, page size up to 50 | 200 with page and next cursor | 400, 401 | Yes | `orders:read` |
| Cancel order *(Example)* | POST /v1/orders/{id}/cancel | Reason code | 200 with updated order | 404, 409 when picking has started or the order is shipped (BR-003) | Yes | `orders:write` |
| [Operation] | [Method path] | [Request] | [Response] | [Codes] | [Yes / No] | [Scope] |

### 5.2 Published events (IF-003)

Contract file: `[path]/order-events.asyncapi.yaml` (AsyncAPI 3.1.0).
Envelope: [CloudEvents 1.0.2 attributes, or the organisation envelope].

| Event | Trigger | Key | Payload summary | Ordering | Consumers |
|---|---|---|---|---|---|
| OrderConfirmed *(Example)* | Payment authorised | Order ID | Order ID, lines, totals, delivery address reference | Per order ID | Fulfilment, notification, analytics |
| OrderCancelled *(Example)* | Cancel accepted | Order ID | Order ID, reason code | Per order ID | Fulfilment, notification, analytics |
| [Event] | [Trigger] | [Key] | [Payload] | [Ordering] | [Consumers] |

### 5.3 Consumed interfaces

| Dependency | Operation | Timeout | Retries | Fallback | Contract |
|---|---|---|---|---|---|
| Payment orchestrator, IF-005 *(Example)* | Create payment intent | 450 ms (at measured p99; p95 budget 350 ms) | None on the request path | Return 503 with `Retry-After`; order stays PENDING_PAYMENT and the client retries with the same key | `payments-internal-v1` OpenAPI file |
| Price cache *(Example)* | Get price by SKU | 20 ms | 1 | Read from database | Cache key spec |
| PaymentAuthorised and PaymentFailed events, IF-006 *(Example)* | Consume | Not applicable | Redelivery by broker, 5 attempts then dead letter | Alert and manual replay; reconciliation job queries the payment orchestrator for orders pending over 10 min | `payment-events` AsyncAPI file |
| [Dependency] | [Operation] | [ms] | [Policy] | [Fallback] | [File] |

Versioning: additive changes are allowed within a major version; removing
or renaming a field needs a new major version and the deprecation period
in API Deprecation Policy (`architecture/api-deprecation-policy`).

## 6. Data design and mappings

<!-- Guidance: Give the physical schema owned by this component, the indexes that serve its access patterns and how fields map between API, domain and storage. Pitfall: a schema designed for the API shape rather than for the queries the component actually runs. -->

```mermaid
erDiagram
  ORDERS ||--|{ ORDER_LINES : contains
  ORDERS ||--o{ ORDER_STATUS_HISTORY : records
  ORDERS ||--o{ OUTBOX : emits
  IDEMPOTENCY_KEYS }o--|| ORDERS : identifies
```

| Table | Key columns | Purpose | Indexes | Retention |
|---|---|---|---|---|
| orders *(Example)* | id (UUID), customer_id, status, total_minor, currency, version, created_at | Order header | (customer_id, created_at desc), (status) partial for open states | 7 years, then anonymise personal fields |
| order_lines *(Example)* | order_id, line_no, sku, quantity, unit_price_minor | Lines | Primary key (order_id, line_no) | With order |
| order_status_history *(Example)* | order_id, from_status, to_status, actor, reason, at | Audit trail | (order_id, at) | With order |
| outbox *(Example)* | id, aggregate_id, type, payload, created_at, published_at | Pending events | (published_at) where null | Delete 7 days after publish |
| idempotency_keys *(Example)* | key, caller_id, request_hash, order_id, expires_at | Duplicate submit detection | Unique (caller_id, key) | 24 hours |
| [Table] | [Columns] | [Purpose] | [Indexes] | [Retention] |

### 6.1 Access patterns

| ID | Query | Frequency | Served by |
|---|---|---|---|
| AP-01 *(Example)* | Get order with lines by ID | High | Primary key lookups |
| AP-02 *(Example)* | Customer's latest orders, newest first | High | (customer_id, created_at desc) |
| AP-03 *(Example)* | Unpublished outbox rows in order | Every 200 ms | Partial index on published_at |
| [AP-NN] | [Query] | [Frequency] | [Index] |

### 6.2 Field mapping

| API field | Domain | Column | Rule |
|---|---|---|---|
| `total.amount` (string decimal) *(Example)* | Money.amount | total_minor (bigint) | Convert with currency minor units; reject more decimals than the currency allows |
| `deliveryAddress` *(Example)* | Address value object | orders.delivery_address (JSON, encrypted column) | Personal data, masked in logs |
| [Field] | [Domain] | [Column] | [Rule] |

Schema changes: forward-only migration scripts in `[path]`, applied by the
pipeline before the new version starts; every change must be compatible
with the running previous version (expand, migrate, contract).

## 7. Behaviour: sequences and states

<!-- Guidance: Draw the flows that involve more than one class or any external call, and the state machine of the main entity. Every transition needs a trigger and a guard. -->

The sequence below shows the submit path inside the component, including
the idempotency check and the transaction boundary.

```mermaid
sequenceDiagram
  participant C as OrdersController
  participant H as SubmitOrderHandler
  participant R as OrderRepository
  participant P as PaymentClient
  C->>H: SubmitOrder(command, idempotencyKey)
  H->>R: findByIdempotencyKey(caller, key)
  alt key seen with same request hash
    R-->>H: existing order ID
    H-->>C: return existing result (201)
  else new key
    H->>R: save(order PENDING_PAYMENT, key) in one transaction
    H->>P: createIntent(orderId, total) timeout 450 ms
    P-->>H: intentId, redirectUrl
    H-->>C: 201 with redirect
  end
```

```mermaid
stateDiagram-v2
  [*] --> PENDING_PAYMENT: submit
  PENDING_PAYMENT --> CONFIRMED: payment authorised
  PENDING_PAYMENT --> EXPIRED: no payment in 30 min
  PENDING_PAYMENT --> CANCELLED: customer cancels
  CONFIRMED --> SHIPPED: shipment event
  CONFIRMED --> CANCELLED: cancel before pick
  SHIPPED --> COMPLETED: delivered
  CANCELLED --> [*]
  EXPIRED --> [*]
  COMPLETED --> [*]
```

## 8. Business rules and algorithms

<!-- Guidance: List each rule with an ID, where it is enforced and the test that proves it. Pitfall: rules only described in user stories, so nobody can tell which version is implemented. -->

| ID | Rule | Enforced in | Source | Test |
|---|---|---|---|---|
| BR-001 *(Example)* | An order has between 1 and 100 lines | Order aggregate | Product backlog item [ID] | UT-ORD-001 |
| BR-002 *(Example)* | Unit price is snapshotted at submit; later price changes do not alter the order | SubmitOrderHandler | Pricing policy | UT-ORD-004 |
| BR-003 *(Example)* | Cancellation is refused once the warehouse reports picking started | Order.cancel | Logistics agreement | UT-ORD-009 |
| BR-004 *(Example)* | Unpaid orders expire after 30 minutes | Expiry job | Payments policy | IT-ORD-003 |
| [BR-NNN] | [Rule] | [Class or module] | [Source] | [Test ID] |

## 9. Error handling, retries and idempotency

<!-- Guidance: Define the error catalogue callers will see, the retry policy for each dependency and how duplicates are made harmless. Questions: which errors are safe to retry? What happens to a message that always fails? Pitfall: retrying non-idempotent calls, which creates duplicate payments or orders. -->

### 9.1 Error catalogue

Errors are returned as problem details (RFC 9457) with a stable `type`
URI per error.

| Type URI suffix | HTTP status | When | Retry by caller |
|---|---|---|---|
| `/problems/validation` *(Example)* | 400 or 422 | Schema or business rule failure; `errors` lists fields | No |
| `/problems/idempotency-conflict` *(Example)* | 409 | Same key reused with a different request body | No |
| `/problems/invalid-state` *(Example)* | 409 | Transition not allowed from current status | No |
| `/problems/rate-limited` *(Example)* | 429 | Caller quota exceeded | Yes, after `Retry-After` |
| `/problems/dependency-unavailable` *(Example)* | 503 | Payment orchestrator timed out or circuit open | Yes, same idempotency key |
| [Suffix] | [Status] | [When] | [Yes / No] |

### 9.2 Retry and resilience policy

| Call | Policy | Circuit breaker | Notes |
|---|---|---|---|
| Payment intent *(Example)* | No inline retry: one retry would take the worst case to 50 + 300 + 450 + 450 = 1,250 ms, above the 800 ms minimum | Opens at 50 % failures over 20 calls, half-open after 30 s | Caller retries with same key; expect about 1 % timeouts at the p99 setting |
| Outbox publish *(Example)* | Exponential backoff from 200 ms to 30 s with jitter, unlimited | Not applicable | Alert when oldest unpublished row is older than 2 min |
| Event consumption *(Example)* | Broker redelivery, 5 attempts | Not applicable | Then dead-letter topic, alert, replay tool |
| [Call] | [Policy] | [Settings] | [Notes] |

Consumers must be idempotent: each consumer records processed event IDs
for [7 days] and ignores repeats.

## 10. Concurrency, transactions and consistency

<!-- Guidance: State the transaction boundaries, the locking strategy and the consistency each reader can expect. Pitfall: assuming exactly-once delivery from the broker. -->

- Transaction boundary: *Example:* one database transaction per command,
  covering the order rows, status history, idempotency record and outbox
  row. No remote call is made inside a transaction.
- Concurrency control: *Example:* optimistic locking on `orders.version`;
  a conflicting update returns `/problems/invalid-state` after one
  internal re-read.
- Ordering: *Example:* events for one order are published in commit
  order; there is no ordering guarantee across orders.
- Read consistency: *Example:* the API reads from the primary; analytics
  consumers are eventually consistent with a typical lag under [5 s].
- Scheduled jobs: *Example:* the expiry job runs on one replica at a time
  using a database advisory lock.

## 11. Configuration and feature flags

<!-- Guidance: List every setting with its default, range and whether a change needs a restart. Secrets are referenced, never written here. -->

| Key | Purpose | Default | Allowed range | Source | Restart needed |
|---|---|---|---|---|---|
| `payment.timeoutMs` *(Example)* | Payment intent timeout (IF-005); keep at or above the measured p99 and at or below 450 so the timeout path fits NFR-PE-001 | 450 | 300 to 450 | Config map | No (reloaded) |
| `orders.maxLines` *(Example)* | BR-001 limit | 100 | 1 to 500 | Config map | No |
| `outbox.pollIntervalMs` *(Example)* | Relay polling | 200 | 50 to 5000 | Config map | Yes |
| `db.pool.max` *(Example)* | Connection pool size per replica | 20 | 5 to 50 | Config map | Yes |
| `flag.partnerChannel` *(Example)* | Enable partner channel | Off | On or off | Feature flag service | No |
| `vault:db-credentials` *(Example)* | Database login | Not applicable | Not applicable | Secrets vault, rotated | No (dynamic) |
| [Key] | [Purpose] | [Default] | [Range] | [Source] | [Yes / No] |

## 12. Security implementation

<!-- Guidance: Show how the component implements the controls the HLD assigned to it. Map to the verification level your organisation uses. Pitfall: relying on the gateway for authorisation, so any internal caller can read any order. -->

| Control | Implementation | HLD or NFR ref | Verification |
|---|---|---|---|
| Authentication *(Example)* | Validate JWT signature, issuer, audience and expiry using the identity provider key set, cached 10 min | HLD 10.1 | Unit and integration tests with forged tokens |
| Object-level authorisation *(Example)* | Every read and write checks that the caller owns the order or holds the agent role | NFR-SE-002 | Negative tests per endpoint |
| Input validation *(Example)* | Schema validation at the edge of the api module; size limits; reject unknown fields | NFR-SE-001 | Contract tests, fuzzing in CI |
| Data protection *(Example)* | Delivery address in an encrypted column; personal fields masked in logs | NFR-SE-003 | Log scan in test |
| Secrets *(Example)* | Read from the vault at start and on rotation events; never logged | NFR-SE-004 | Pipeline secret scan |
| Audit *(Example)* | Status history row with actor and reason for each agent change | NFR-SE-006 | Test IT-ORD-007 |
| [Control] | [Implementation] | [Ref] | [Verification] |

Target verification level: [OWASP ASVS 5.0.0 level 2, or the internal
standard]. Secure coding rules: Secure Coding Standard
(`security/secure-coding-standard`).

## 13. Logging, metrics and tracing

<!-- Guidance: Name the signals the component emits so that the SLOs in the HLD can be measured and alerts can be written. Use the organisation's semantic conventions for names. -->

| Signal | Name | Type and unit | Labels | Use |
|---|---|---|---|---|
| Request duration *(Example)* | `http.server.request.duration` | Histogram, seconds | route, method, status | Latency SLO |
| Orders submitted *(Example)* | `orders.submitted` | Counter | channel, result | Business health |
| Outbox lag *(Example)* | `orders.outbox.oldest_age` | Gauge, seconds | none | Alert above 120 s |
| Payment dependency errors *(Example)* | `orders.payment.errors` | Counter | reason | Circuit breaker tuning |
| [Signal] | [Name] | [Type] | [Labels] | [Use] |

Logging: structured JSON, one line per event, with trace ID, order ID and
caller ID; personal fields masked; levels as in the coding standard.
Tracing: OpenTelemetry instrumentation for inbound HTTP, database, outbound
HTTP and broker spans; trace context is carried in event headers. Names
follow OpenTelemetry semantic conventions (version [1.44.0 or the version
pinned by the platform]).

## 14. Performance and capacity

<!-- Guidance: Break the component's latency budget into steps and size the runtime for peak. Recompute every number when an input changes. -->

| Step in POST /v1/orders | Budget p95 |
|---|---|
| Validation and mapping *(Example)* | 20 ms |
| Idempotency check and insert transaction *(Example)* | 60 ms |
| Price lookups from cache *(Example)* | 20 ms |
| Payment intent call (counted in the payment orchestrator budget) | 0 ms here |
| Serialisation, framework and network overhead *(Example)* | 50 ms |
| Headroom *(Example)* | 150 ms |
| **Total inside component** | **300 ms** |

Check *(Example)*: 20 + 60 + 20 + 0 + 50 + 150 = 300 ms. With the
gateway (50 ms) and the payment intent (350 ms p95) the request budget is
700 ms p95; on the timeout path it is 50 + 300 + 450 = 800 ms, the
NFR-PE-001 minimum.

Sizing *(Example)*: peak 50 requests per second for order submission
(NFR-PE-002 minimum; target 60); measured capacity [40] requests per
second per replica at the p95 target; three replicas give 120 requests
per second, so one zone can be lost and the remaining two replicas (80
per second) still cover the 60 per second target. Database pool: 3
replicas x 20 connections = 60 at minimum scale, and 9 x 20 = 180 at the
maximum of 9 replicas in section 16. Confirm 180 plus [other clients]
fits the database limit of [200], or cap `db.pool.max` at [n]. The outbox
relay publishes up to 60 events per second (IF-003), one per confirmed
order.

## 15. Test approach

<!-- Guidance: Say what is tested at each level and which tests prove the NFR budgets. Link the test strategy instead of repeating it. -->

| Level | Scope | Tooling | Key cases | Gate |
|---|---|---|---|---|
| Unit *(Example)* | Domain and use cases with fake ports | [Test framework] | All BR rows, every state transition including refused ones | Pull request, coverage of domain at [90 %] |
| Contract *(Example)* | Provided OpenAPI and AsyncAPI, consumer-driven contracts | [Contract tool] | Each operation and event version | Pull request |
| Integration *(Example)* | Real database and broker in containers | [Tool] | Outbox order, idempotency, optimistic locking | Main branch |
| Performance *(Example)* | Component under load in pre-production | [Load tool] | 2x peak for 30 min, p95 at or below 300 ms inside the component; payment stub at p99 latency to prove the timeout path | Release |
| Resilience *(Example)* | Broker stopped during load in pre-production | [Fault injection tool] | Submissions keep succeeding, pods stay ready, outbox drains after restart | Release |
| Security *(Example)* | Authorisation negatives, dependency and image scans | [Scanner] | Cross-customer access refused | Release |
| [Level] | [Scope] | [Tool] | [Cases] | [Gate] |

Test strategy: Test Strategy (`quality/test-strategy`); performance test
plan: Performance Test Plan (`quality/performance-test-plan`).

## 16. Build, deployment and runtime

<!-- Guidance: Give what operations needs to deploy, scale and roll back this component safely. -->

| Item | Value |
|---|---|
| Artefact | *Example:* Container image `[registry]/order-service:[semver]`, signed, with SBOM |
| Pipeline | *Example:* Build, unit, contract, image scan, deploy to test, integration, deploy to pre-production, performance, approval, production |
| Deployment strategy | *Example:* Rolling update, max surge 1, max unavailable 0; database migration job runs first |
| Resources per replica | *Example:* Requests 0.5 CPU and 512 MiB; limit 1 GiB memory |
| Scaling | *Example:* Minimum 3, maximum 9 replicas, scale on CPU 60 % and request rate |
| Health checks | *Example:* Liveness on process health; readiness on database connectivity only. Broker health is surfaced through the `orders.outbox.oldest_age` alert, never through readiness, so a broker outage does not stop order capture (HLD section 11, ADR-003) |
| Rollback | *Example:* Redeploy previous image; migrations are backward compatible, so no schema rollback |
| Runbooks | *Example:* Links to the runbook for each alert in section 13 (`dev/runbook`) |

## 17. Traceability and open issues

<!-- Guidance: Show that each input from section 2 reaches a design element and a test, and list what is still undecided with a due date. -->

| Input | Design element | Test |
|---|---|---|
| NFR-PE-001 *(Example)* | Section 14 budget, cache use, pool size | PT-ORD-01 |
| NFR-SE-002 *(Example)* | Ownership check in application module | ST-ORD-03 |
| ADR-003 *(Example)* | Outbox table and relay, readiness without broker | IT-ORD-002, RT-ORD-01 |
| ADR-004 *(Example)* | `payment.timeoutMs` 450, no inline retry, circuit breaker | PT-ORD-01 |
| BR-003 *(Example)* | Order.cancel guard | UT-ORD-009 |
| [ID] | [Element] | [Test ID] |

| ID | Open issue | Owner | Due |
|---|---|---|---|
| ISS-LLD-001 *(Example)* | Confirm currency list for launch markets | @product-owner | YYYY-MM-DD |
| [ID] | [Issue] | [@owner] | YYYY-MM-DD |

Review checklist before approval:

- [ ] Every provided operation and event is in a contract file and in section 5.
- [ ] Every dependency call has a timeout, a retry rule and a fallback.
- [ ] Every business rule has an ID, an enforcement point and a test.
- [ ] No remote call happens inside a database transaction.
- [ ] Personal data fields are identified, protected and masked in logs.
- [ ] The latency budget and sizing arithmetic have been recomputed.
- [ ] Nothing here contradicts the HLD inputs in section 2.

## 18. Glossary and references

<!-- Guidance: Define terms and list the standards and internal documents used, with versions. -->

### 18.1 Glossary

| Term | Meaning |
|---|---|
| Aggregate | Cluster of domain objects changed together in one transaction, with one root |
| Idempotency key | Caller-supplied value that makes repeated submissions return the same result |
| Outbox | Table of events written with the state change and published by a relay |
| Problem details | Standard JSON body for HTTP API errors, defined in RFC 9457 |
| Dead-letter topic | Destination for messages that failed all delivery attempts |
| [Term] | [Meaning] |

### 18.2 References

| ID | Reference | Version or date | Use |
|---|---|---|---|
| REF-01 | IEEE 1016-2009, Standard for Information Technology: Systems Design: Software Design Descriptions | 2009; IEEE status Inactive-Reserved since 2020-03-05 | Concepts of design concerns, viewpoints and views |
| REF-02 | OpenAPI Specification, spec.openapis.org | 3.2 line (3.2.0 released September 2025) | REST contract |
| REF-03 | AsyncAPI Specification, asyncapi.com | 3.1.0 | Event contract |
| REF-04 | RFC 9457, Problem Details for HTTP APIs (IETF) | July 2023, obsoletes RFC 7807 | Error format |
| REF-05 | OWASP Application Security Verification Standard | 5.0.0, May 2025 | Security verification level |
| REF-06 | OpenTelemetry semantic conventions, opentelemetry.io | 1.44.0 at time of writing | Telemetry names |
| REF-07 | [Parent HLD] | [Version] | Fixed inputs |

## Standard and licence note

Structure aligned with IEEE 1016-2009 (design stakeholders, concerns,
viewpoints and views for a software design description); IEEE lists this
standard as Inactive-Reserved, and it remains a common reference for
detailed design. Contract and error formats are based on the OpenAPI
Specification, the AsyncAPI Specification and IETF RFC 9457; the
security level refers to the OWASP ASVS by name and level only, and no
OWASP text is adapted. No text, definitions or tables are reproduced from
IEEE standards. This template is original text written for this
collection. Check the current versions at standards.ieee.org,
spec.openapis.org, asyncapi.com, rfc-editor.org and owasp.org before use.
Template licence: MIT.
