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.
Document control
| 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
1. Component identification and scope
| 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
| 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
| 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
| 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
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
| 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
The sequence below shows the submit path inside the component, including
the idempotency check and the transaction boundary.
8. Business rules and algorithms
| 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
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
- 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
| 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
| 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
| 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
| 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
| 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
| 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
| 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:
18. Glossary and references
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.