From 5ce83a20174b21e1cc86c678fab8f29d13ed7c61 Mon Sep 17 00:00:00 2001 From: PatternCatalyst Date: Fri, 23 Oct 2026 08:33:46 -0400 Subject: [PATCH] docs(site): replace verification footers with Build, run, observe Across all 34 chapters, remove the reader-facing *Verification status:* footer and bank it verbatim in _plans/verification-log.md (plans collection is output: false, not published). On each hands-on chapter, convert the existing "### Cross-check it yourself" section to "## Build, run, observe": keep its run/observe prose and add the example's verify command (start one language's stack, run the language-agnostic verify.sh) plus what that script asserts, drawn from the real script. Conceptual chapters keep their existing section and just lose the footer. The minikube and OpenShift appendices use their real k8s/oc command sequences. CLAUDE.md convention note updated to match. No content lost (net +167 lines, additive). validate_site.py clean; voice-scan 0 ban-tier; Jekyll build green. --- CLAUDE.md | 10 +- _docs/00-setting-up.md | 5 - _docs/01-cloud-native-principles.md | 19 ++- _docs/02-communications.md | 20 +++- _docs/03-composition.md | 19 ++- _docs/04-data.md | 19 ++- _docs/05-event-driven.md | 21 +++- _docs/06-stream-processing.md | 21 +++- _docs/07-workflows-and-jobs.md | 5 - _docs/08-api-management.md | 5 - _docs/09-api-registry.md | 20 +++- _docs/10-api-metadata.md | 5 - _docs/11-observability.md | 22 +++- _docs/12-security.md | 26 ++++- _docs/13-anti-patterns.md | 4 - _docs/14-appendix-a-protocols.md | 7 -- _docs/15-appendix-b-versioning.md | 5 - _docs/16-appendix-c-websockets.md | 18 ++- _docs/17-appendix-d-sagas.md | 19 ++- _docs/18-appendix-e-errors.md | 18 ++- _docs/19-appendix-f-ddd-hexagonal.md | 19 ++- _docs/20-appendix-g-coupling.md | 7 -- _docs/21-appendix-h-shutdown.md | 19 ++- _docs/22-appendix-i-l7-routing.md | 18 ++- _docs/23-appendix-j-dotnet-toolkit.md | 11 -- ...24-appendix-k-monolith-to-microservices.md | 19 ++- _docs/25-appendix-l-caching.md | 19 ++- _docs/26-appendix-m-failure-modes.md | 19 ++- _docs/27-appendix-n-feature-flags.md | 17 ++- _docs/28-appendix-o-newman.md | 18 ++- _docs/29-appendix-p-dotnet-on-linux.md | 7 -- _docs/29-glossary.md | 5 - _docs/30-appendix-q-blazor-signalr.md | 17 ++- _docs/31-appendix-r-minikube.md | 20 +++- _docs/32-appendix-s-openshift-local.md | 22 +++- _plans/verification-log.md | 109 ++++++++++++++++++ 36 files changed, 458 insertions(+), 176 deletions(-) create mode 100644 _plans/verification-log.md diff --git a/CLAUDE.md b/CLAUDE.md index 267b2ff9..4fe7f9e8 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -228,8 +228,14 @@ k6, Locust. `float("5.10") == 5.1` is a known trap. - **No positional language** anywhere (slides or site): no "above / below / next slide / previous section / see slide N". Cross-reference the **concept**, not a number. -- Chapters end with `### Cross-check it yourself` + an italic `*Verification status: …*` - footer. **Flip the footer to verified once `examples/NN-*/` runs green.** +- Hands-on chapters end with a `## Build, run, observe` section: the example's + `verify.sh` command (start one language's stack, run the language-agnostic + checks) and what it asserts, plus the observe guidance. **No reader-facing + `*Verification status: …*` footer** on the page: the per-chapter run records + live in `_plans/verification-log.md` (the `plans` collection is + `output: false`). Conceptual chapters have no Build, run, observe section. + When an example's `verify.sh` runs green, update `_plans/verification-log.md`, + not a chapter footer. - **Validate before commit:** front-matter parses; raw-guard balance; **0 unguarded `{{`**; each codetab block-count == its `langs` count; referenced figures exist (`.svg` + `.excalidraw`); **0 positional terms**. diff --git a/_docs/00-setting-up.md b/_docs/00-setting-up.md index e661a229..e708a4a5 100644 --- a/_docs/00-setting-up.md +++ b/_docs/00-setting-up.md @@ -97,8 +97,3 @@ concerns (workflows and the platform planes); and finally anti-patterns as a reflective close. The deep-dive appendices at the end go deeper on the questions that always come up (protocols, versioning, WebSockets, sagas, and more) and can be read on demand. - ---- -*Verification status: conceptual chapter; no runnable code. The patterns named -here are defined and applied in their own chapters, where the code ships with a -runnable example per language.* diff --git a/_docs/01-cloud-native-principles.md b/_docs/01-cloud-native-principles.md index 9274b899..08abd22f 100644 --- a/_docs/01-cloud-native-principles.md +++ b/_docs/01-cloud-native-principles.md @@ -290,7 +290,20 @@ The same two ideas appear in every tab: Notice what *isn't* here: no retry logic, no scaling code, no port-binding plumbing. Those are the right column of Figure 1.3: the platform's job. -### Cross-check it yourself +## Build, run, observe + +```bash +cd examples/01-cloud-native-principles/python # or any of the six language stacks +docker compose up -d --build --wait +cd .. +./verify.sh +``` + +`verify.sh` runs the language-agnostic end-to-end checks against the running stack (13/13 in +each of the six languages), asserting the service reports its name, version and config +source from the environment (Factor III), liveness stays `200` while readiness flips from +`200` to `503` and back as Postgres is stopped and restarted, and a CRUD round trip +(create an order, list it back) succeeds while the database is up. With the service running, the probes are just HTTP. Hit liveness and readiness with `curl` and confirm liveness returns healthy while the database is down but @@ -300,7 +313,3 @@ database and back as you start it, without the process ever restarting. The code is in [`examples/01-cloud-native-principles/`](https://github.com/patterncatalyst/cloud-native-design-patterns/tree/main/examples/01-cloud-native-principles/). The run script there builds and runs it; its `README.md` covers what it does and how to drive it. - ---- -*Verification status: verified; [`examples/01-cloud-native-principles/`](https://github.com/patterncatalyst/cloud-native-design-patterns/tree/main/examples/01-cloud-native-principles/) passes 13/13 checks in each of the six languages on Docker Engine (2026-10-13) -(health probes, config from environment, CRUD, readiness flip on dependency failure).* diff --git a/_docs/02-communications.md b/_docs/02-communications.md index a6365741..c8f1cc89 100644 --- a/_docs/02-communications.md +++ b/_docs/02-communications.md @@ -402,7 +402,21 @@ piles onto the struggling service in a retry storm. Putting the policy in the me means one bounded, observable rule for every caller, instead of each team re-inventing (and mis-tuning) retries in code. -### Cross-check it yourself +## Build, run, observe + +```bash +cd examples/02-communications/python # or any of the six language stacks +docker compose up -d --build --wait +cd .. +./verify.sh +``` + +`verify.sh` runs the language-agnostic end-to-end checks against the running stack (10/10 in +each of the six languages), asserting a create returns `201` with an id and status, a bad +body returns `422` before business logic runs, cursor pagination returns a page with a +`next_cursor`, an order with available stock comes back confirmed while one exceeding stock +is rejected (the gRPC stock reservation), a GraphQL orders query returns data, and the +`order.placed` Kafka topic exists after a create. Use plain tools. `curl` and Postman exercise the REST contract: confirm a create returns **201** and that a bad body returns **400** before your logic runs. @@ -411,7 +425,3 @@ bounded retries behave under pressure rather than trusting they do. The code is in [`examples/02-communications/`](https://github.com/patterncatalyst/cloud-native-design-patterns/tree/main/examples/02-communications/). The run script there builds and runs it; its `README.md` covers what it does and how to drive it. - ---- -*Verification status: verified: [`examples/02-communications/`](https://github.com/patterncatalyst/cloud-native-design-patterns/tree/main/examples/02-communications/) passes 10/10 checks in each of the six languages on Docker Engine (2026-10-13) -(REST create and validation (201, 422), cursor pagination, order confirmation and rejection driven by the gRPC stock reservation, a GraphQL orders query, and the order.placed Kafka topic).* diff --git a/_docs/03-composition.md b/_docs/03-composition.md index 839a68c9..a96ea923 100644 --- a/_docs/03-composition.md +++ b/_docs/03-composition.md @@ -262,7 +262,20 @@ distance), and it couples only to their contracts (low strength). A call chain violates it: services that are far apart couple at the functional level, which is exactly why it becomes a distributed monolith. -### Cross-check it yourself +## Build, run, observe + +```bash +cd examples/03-composition/python # or any of the six language stacks +docker compose up -d --build --wait +cd .. +./verify.sh +``` + +`verify.sh` runs the language-agnostic end-to-end checks against the running stack (8/8 in +each of the six languages), asserting the gateway's health endpoint and the backing order +API respond, a GraphQL query without the `stock` field omits it (no inventory call), a +query with `stock` resolves it from the gRPC inventory service, the gateway logs a single +batched DataLoader call across the backend skus, and a single-order lookup by id resolves. The gateway is just HTTP. Send the same query with `curl` or Postman and request `stock` in one call, then omit it in another. Confirm the gateway only calls @@ -272,7 +285,3 @@ list size; with a DataLoader it stays flat. That flat line is the N+1 fix workin The code is in [`examples/03-composition/`](https://github.com/patterncatalyst/cloud-native-design-patterns/tree/main/examples/03-composition/). The run script there builds and runs it; its `README.md` covers what it does and how to drive it. - ---- -*Verification status: verified; [`examples/03-composition/`](https://github.com/patterncatalyst/cloud-native-design-patterns/tree/main/examples/03-composition/) passes 8/8 checks in each of the six languages on Docker Engine (2026-10-13) -(GraphQL gateway fan-out to the REST order API and the gRPC stock service, selective stock-field resolution, DataLoader batching of stock lookups into one gRPC call, single-order lookup).* diff --git a/_docs/04-data.md b/_docs/04-data.md index 21b38b66..33abecb3 100644 --- a/_docs/04-data.md +++ b/_docs/04-data.md @@ -317,7 +317,20 @@ pattern; the choreography-versus-orchestration trade-off, how a saga holds its s and how a later step gets an earlier step's data are taken up in depth in **Appendix D · Sagas**. -### Cross-check it yourself +## Build, run, observe + +```bash +cd examples/04-data/python # or any of the six language stacks +docker compose up -d --build --wait +cd .. +./verify.sh +``` + +`verify.sh` runs the language-agnostic end-to-end checks against the running stack (8/8 in +each of the six languages), asserting Kafka Connect comes up and the Debezium outbox +connector reaches `RUNNING`, a create writes both the order row and its outbox row in one +transaction, and the Debezium relay publishes the outbox row to the `order.placed` Kafka +topic with the order's payload intact. You do not have to trust that the commit and the event stay in step: you can watch it. With the example running, stop the Kafka broker, place an order, and @@ -329,7 +342,3 @@ without a re-request, is the property the outbox buys you. The code is in [`examples/04-data/`](https://github.com/patterncatalyst/cloud-native-design-patterns/tree/main/examples/04-data/). The run script there builds and runs it; its `README.md` covers what it does and how to drive it. - ---- -*Verification status: verified; [`examples/04-data/`](https://github.com/patterncatalyst/cloud-native-design-patterns/tree/main/examples/04-data/) passes 8/8 checks in each of the six languages on Docker Engine (2026-10-13) -(transactional write of order and outbox row, Debezium outbox connector running, relay of the outbox event to the order.placed Kafka topic with its payload).* diff --git a/_docs/05-event-driven.md b/_docs/05-event-driven.md index c2e30f2d..2eb1381f 100644 --- a/_docs/05-event-driven.md +++ b/_docs/05-event-driven.md @@ -1284,7 +1284,22 @@ The throughline of the whole chapter: an event-driven system trades immediate co and a single in-the-moment truth for decoupling, autonomy, scale, and a complete, replayable history. Spend that trade where it pays, and avoid it in the few places it doesn't. -### Cross-check it yourself +## Build, run, observe + +```bash +cd examples/05-event-driven/python # or any of the six language stacks +docker compose up -d --build --wait +cd .. +./verify.sh +``` + +`verify.sh` runs the language-agnostic end-to-end checks against the running stack (10/10 in +each of the six languages), asserting a placed order publishes to `order.placed`, both the +`shipping-group` and `notification-group` consumer groups exist and each independently +writes its own row for the order (fan-out, not round-robin), and replaying the same event +through the topic does not create a second shipment or notification row (idempotent +consumers under a redelivered duplicate). The same script also drives the Kubernetes +variant: Appendix R runs it on minikube and Appendix S on OpenShift Local. Produce one `order.placed` and watch two independent consumer groups receive it: the fan-out is real, not round-robin. Then stop a consumer mid-handler (before it @@ -1295,7 +1310,3 @@ redelivery is the at-least-once guarantee, and the reason your handler dedupes. The code is in [`examples/05-event-driven/`](https://github.com/patterncatalyst/cloud-native-design-patterns/tree/main/examples/05-event-driven/). The run script there builds and runs it; its `README.md` covers what it does and how to drive it. The same example, with Kafka managed by the Strimzi operator, also runs on a Kubernetes cluster; Appendix R covers that deployment on minikube and Appendix S covers OpenShift Local. - ---- -*Verification status: verified; [`examples/05-event-driven/`](https://github.com/patterncatalyst/cloud-native-design-patterns/tree/main/examples/05-event-driven/) passes 10/10 checks in each of the six languages on Docker Engine (2026-10-13) and on minikube (2026-10-11, Appendix R), and 10/10 in five languages on OpenShift Local 4.22.14 (2026-10-11, Appendix S) -(event publication to order.placed, fan-out to two independent consumer groups (shipping and notification), and idempotent consumers under a redelivered duplicate event).* diff --git a/_docs/06-stream-processing.md b/_docs/06-stream-processing.md index bd7b84ce..4f4cc6df 100644 --- a/_docs/06-stream-processing.md +++ b/_docs/06-stream-processing.md @@ -327,7 +327,22 @@ lever: true scale-to-zero when the topic is quiet, springing back the moment lag builds. Picking the signal (lag for events, request rate for HTTP) is the whole point; scaling a backlog-bound consumer on CPU would miss the load entirely. -### Cross-check it yourself +## Build, run, observe + +```bash +cd examples/06-stream-processing/python # or any of the six language stacks +docker compose up -d --build --wait +cd .. +./verify.sh +``` + +`verify.sh` runs the language-agnostic end-to-end checks against the running stack (11/11 in +each of the six languages), asserting orders for two merchants land in the derived +`revenue.by-merchant` topic with a per-merchant aggregation, the `stream-processor` consumer +group exists, pausing that consumer and sending a burst of orders makes its lag on +`order.placed` rise above zero, resuming it drains the lag back down, and the burst's +merchant eventually shows up in the revenue stream (the windowed aggregation catching up +end to end). Make lag visible, then make it move. Pause the `payment-consumer`, publish a burst of `order.placed` with `hey` driving `order-service`, and watch the consumer-group @@ -337,7 +352,3 @@ behaviour to confirm. The code is in [`examples/06-stream-processing/`](https://github.com/patterncatalyst/cloud-native-design-patterns/tree/main/examples/06-stream-processing/). The run script there builds and runs it; its `README.md` covers what it does and how to drive it. - ---- -*Verification status: verified: [`examples/06-stream-processing/`](https://github.com/patterncatalyst/cloud-native-design-patterns/tree/main/examples/06-stream-processing/) passes 11/11 checks in each of the six languages on Docker Engine (2026-10-13) -(per-merchant revenue aggregation onto a derived Kafka topic, the stream-processor consumer group, consumer lag that builds while the processor is paused and drains on resume, and the burst appearing in the revenue stream).* diff --git a/_docs/07-workflows-and-jobs.md b/_docs/07-workflows-and-jobs.md index ca94e8fb..d9318853 100644 --- a/_docs/07-workflows-and-jobs.md +++ b/_docs/07-workflows-and-jobs.md @@ -247,8 +247,3 @@ the pod enter `Completed`, not `Running`. Separately, push a heavy task through HTTP handler and watch the client time out. Then move the same task behind a queue worker and watch the request return immediately while the work drains in the background. That contrast is the whole rule. - ---- -*Verification status: conceptual chapter with an illustrative Kubernetes manifest; -no per-language runnable example. The coordination patterns it names are made -concrete in the saga appendix.* diff --git a/_docs/08-api-management.md b/_docs/08-api-management.md index aa501f3b..d84b4fe2 100644 --- a/_docs/08-api-management.md +++ b/_docs/08-api-management.md @@ -141,8 +141,3 @@ limit with `hey` and confirm the gateway returns `429` with a `Retry-After` rath than letting the load through: that response is the runtime plane doing its job as configuration. The contract-plane enforcement is demonstrated in the registry chapter, where a breaking change is rejected at publish time. - ---- -*Verification status: conceptual chapter: no per-language runnable code. The -runtime-plane and contract-plane behaviours it describes are exercised in the -security, registry, and Appendix B chapters.* diff --git a/_docs/09-api-registry.md b/_docs/09-api-registry.md index 8cdfcf51..65413888 100644 --- a/_docs/09-api-registry.md +++ b/_docs/09-api-registry.md @@ -330,7 +330,21 @@ func isCompatible(group, artifactID string, schema []byte) (bool, error) { } ``` -### Cross-check it yourself +## Build, run, observe + +```bash +cd examples/09-api-registry +docker compose up -d --build --wait +./verify.sh +``` + +This example is language-agnostic: Apicurio Registry is the only service under +test, so there is one stack, not six. `verify.sh` runs the end-to-end checks +against the running registry (8/8), asserting schema registration, the +`BACKWARD` compatibility rule, rejection of a breaking change (renamed field) +with `400` and a `RuleViolationException`, acceptance of an additive change +(new optional field) with `200`, and versioned retrieval of the latest schema +content. Prove the gate bites. Register `order-placed.avsc`, then make a breaking change (rename a required field) and `curl` the new version: you should get a @@ -338,7 +352,3 @@ change (rename a required field) and `curl` the new version: you should get a *optional* field) and confirm it returns `200` and publishes a new version. The breaking change failing the build and the additive change passing it is the entire point of the registry. - ---- -*Verification status: verified; [`examples/09-api-registry/`](https://github.com/patterncatalyst/cloud-native-design-patterns/tree/main/examples/09-api-registry/) passes 8/8 checks on Docker Engine (2026-10-13) -(schema registration, BACKWARD compatibility rule, rejection of a breaking change with 400, acceptance of an additive change, versioned retrieval).* diff --git a/_docs/10-api-metadata.md b/_docs/10-api-metadata.md index 9cb21c67..4134bbd8 100644 --- a/_docs/10-api-metadata.md +++ b/_docs/10-api-metadata.md @@ -228,8 +228,3 @@ follow its lineage downstream: confirm you can see, without asking anyone, which tables and APIs consume it and which team owns each. Then check that an asset carries its owner and PII tags. Getting that impact list from the catalog rather than from a post-incident retro is the whole point. - ---- -*Verification status: conceptual chapter: no per-language runnable code. The -discovery, lineage, and trust capabilities it describes are exercised against a -running OpenMetadata catalog in the example stack.* diff --git a/_docs/11-observability.md b/_docs/11-observability.md index 4a584164..a52633dd 100644 --- a/_docs/11-observability.md +++ b/_docs/11-observability.md @@ -432,7 +432,23 @@ The shape (keep errors, keep slow, sample the rest) is the one most teams want: you never drop the traces you'd investigate, and you pay full storage only for the boring majority you sample down. -### Cross-check it yourself +## Build, run, observe + +```bash +cd examples/11-observability/python # or any of the six language stacks +docker compose up -d --build --wait +cd .. +./verify.sh +``` + +`verify.sh` runs the language-agnostic end-to-end checks against the running +stack (10/10 in each of the six languages), asserting: the order-service health +check; an order placed over REST returns `201` and reaches `confirmed` status +(stock reserved via gRPC); the `trace_id` logged by order-service is correlated +with the request; that trace is found in Tempo with order-service spans; the +inventory-service span (propagated over gRPC) is part of that same trace, not a +different one; and the `orders.placed` and `stock.reservations` counters show up +in Prometheus. Prove the correlation with the house tools; no heavy framework needed. Drive a request through `order-service` with `curl`, grab the `trace_id`, and find that @@ -441,7 +457,3 @@ five services. Then push load with `hey` (HTTP) and `ghz` (gRPC) and watch the metrics move and the tail sampler keep the slow and errored traces while dropping the rest. Seeing one request as one waterfall is the moment observability stops being three disconnected tools. - ---- -*Verification status: verified: [`examples/11-observability/`](https://github.com/patterncatalyst/cloud-native-design-patterns/tree/main/examples/11-observability/) passes 10/10 checks in each of the six languages on Docker Engine (2026-10-13) -(trace propagation, metric export, structured log correlation, LGTM stack integration).* diff --git a/_docs/12-security.md b/_docs/12-security.md index 4649288f..0814fab8 100644 --- a/_docs/12-security.md +++ b/_docs/12-security.md @@ -743,7 +743,27 @@ Every pattern is a trade, so the cost line matters as much as the benefit: - **Claim-check** when payloads are large or sensitive. Cost: a second fetch and a store to manage, in exchange for keeping bulk and PII off the broker. -### Cross-check it yourself +## Build, run, observe + +```bash +cd examples/12-security/python # or any of the six language stacks +docker compose up -d --build --wait +cd .. +./verify.sh +``` + +`verify.sh` runs the language-agnostic end-to-end checks against the running +stack (18/18 in each of the six languages), asserting: `/healthz` bypasses the +identity check; a request with no `X-Forwarded-Client-Cert` is refused with +`403` and a "no validated identity" body, while a request carrying the header +succeeds with `201` and echoes back the sidecar identity and the JWT subject +claim; a valet key is minted, verifies successfully, and is rejected with `403` +when tampered or used for the wrong operation; the per-tenant bulkhead reports +independent pools per tenant, and filling one tenant's pool with eight +concurrent slow requests gets some of them refused with `429` while another +tenant is still served `201`; and conftest's Rego policy accepts a +compliant manifest and rejects one with an untrusted image. JWT signature +validation, RBAC enforcement, and rate limiting are not covered. Test the deny path, which is the one that matters. Call `order-service` *directly*, bypassing the mesh, with no `X-Forwarded-Client-Cert`, and confirm you get `403`: @@ -751,7 +771,3 @@ the app trusts only the sidecar. Then apply a Deployment with an unsigned image confirm Gatekeeper rejects it at admission, and that the same policy fails the CI step pre-merge. A request without identity being refused, and an unsigned image being blocked twice, is the layered model working. - ---- -*Verification status: verified: [`examples/12-security/`](https://github.com/patterncatalyst/cloud-native-design-patterns/tree/main/examples/12-security/) passes 18/18 checks in each of the six languages on Docker Engine (2026-10-13) -(sidecar identity trust (403 without X-Forwarded-Client-Cert, health-check bypass), propagation of the JWT subject claim, valet-key mint, verify and tamper rejection, per-tenant bulkhead state, a full tenant pool refusing the overflow with 429 while another tenant is still served, and conftest Rego policy evaluation of a good and a bad manifest. JWT signature validation, RBAC enforcement and rate limiting are not covered).* diff --git a/_docs/13-anti-patterns.md b/_docs/13-anti-patterns.md index 0fd354a7..6739aa96 100644 --- a/_docs/13-anti-patterns.md +++ b/_docs/13-anti-patterns.md @@ -84,7 +84,3 @@ That closes the thirteen core sections. The appendices that follow go deeper on questions that always come up (protocols, versioning, WebSockets, sagas, errors, DDD, coupling, shutdown, routing, decomposition, caching, and failure modes) and can be read on demand. - ---- -*Verification status: conceptual chapter; no runnable code. Its heuristics are the -review-time application of the patterns proven in the earlier chapters.* diff --git a/_docs/14-appendix-a-protocols.md b/_docs/14-appendix-a-protocols.md index cd35847c..5c6ddd18 100644 --- a/_docs/14-appendix-a-protocols.md +++ b/_docs/14-appendix-a-protocols.md @@ -298,10 +298,3 @@ Drawn from real review comments: The throughline: each protocol is a tool with a shape. Match the shape to the interaction, and most of these mistakes never arise. - ---- -*Verification status: the gRPC stub call and server-streaming handler are shown in -all six languages and mirror the patterns compiled and run in the Communications -chapter; the comparison axes, streaming modes, and performance levers are conceptual. -The protocol choices this appendix argues are exercised in Communications and -Composition.* diff --git a/_docs/15-appendix-b-versioning.md b/_docs/15-appendix-b-versioning.md index 5a96b835..ce85dda2 100644 --- a/_docs/15-appendix-b-versioning.md +++ b/_docs/15-appendix-b-versioning.md @@ -333,8 +333,3 @@ optional field to a schema and confirm the `BACKWARD` check passes; change a fie type or reuse a protobuf tag and confirm it fails with `400`. The mechanical check catching the breaking change, instead of a reviewer's memory, is the principle made real. - ---- -*Verification status: unverified: examples transcribed from the source decks; the -REST, gRPC, and GraphQL compatibility behaviour and the Kafka `schema.version` header -are exercised against the registry in the `examples/09-api-registry/` runner.* diff --git a/_docs/16-appendix-c-websockets.md b/_docs/16-appendix-c-websockets.md index 59fa0263..6760f2ae 100644 --- a/_docs/16-appendix-c-websockets.md +++ b/_docs/16-appendix-c-websockets.md @@ -546,7 +546,7 @@ SignalR**. payload), not JSON text, and bound each connection's send buffer so a slow client applies backpressure instead of exhausting the pod. -### Cross-check it yourself +## Build, run, observe Prove the backplane carries messages between pods. Connect a client to `ws-pod-1`, then send it a message *from* `ws-pod-2`: with the backplane wired up it arrives; rip the backplane @@ -554,6 +554,16 @@ out and it vanishes, because no other pod can reach that socket. Then scale the Deployment down by one and confirm the dropped clients reconnect with backoff and resume from their last sequence rather than losing the stream. ---- -*Verification status: verified; [`examples/16-websockets/`](https://github.com/patterncatalyst/cloud-native-design-patterns/tree/main/examples/16-websockets/) passes 6/6 checks in each of the six languages on Docker Engine (2026-10-13) -(WebSocket connect, cross-pod delivery and broadcast over the Redis pub/sub backplane, monotonic sequence numbers).* +```bash +cd examples/16-websockets/python # or any of the six language stacks +docker compose up -d --build --wait +cd .. +./verify.sh +``` + +`verify.sh` runs the language-agnostic end-to-end checks against the running stack +(6/6 in each of the six languages), asserting both pods report healthy, a client can +open a WebSocket connection to `ws-pod-1`, a message sent through `ws-pod-2`'s REST +API arrives on that client over the Redis pub/sub backplane, a broadcast from +`ws-pod-2` reaches a client connected to `ws-pod-1`, and the `seq` field on a burst of +messages is monotonic and gap-free. diff --git a/_docs/17-appendix-d-sagas.md b/_docs/17-appendix-d-sagas.md index b7c3aaa0..6f729b95 100644 --- a/_docs/17-appendix-d-sagas.md +++ b/_docs/17-appendix-d-sagas.md @@ -316,7 +316,7 @@ free. alt="The orchestrator owns the saga context. It calls service A, which returns a reservation_id and warehouse region; that output is persisted into the saga context (reservation_id r-77, region eu-1) written after A and read before B; the orchestrator then calls service B with the context, which supplies the reservation_id B needs. If the orchestrator crashes between persisting A's output and calling B, on restart it loads the saga context, sees A is done, and resumes at B without re-calling A." caption="Figure D.3: A's output flows to B through the orchestrator's persisted context; B never calls A, and a crash resumes cleanly" %} -### Cross-check it yourself +## Build, run, observe Force the unhappy path. Run the saga with `book_shipping` rigged to fail, and confirm the database shows `release_stock` then `refund_payment` executing in that @@ -326,6 +326,17 @@ resume from its persisted `step_index` and `context`, not start over. Reverse-or inverses over only the completed steps, plus a clean resume, shows the saga works. ---- -*Verification status: verified: [`examples/17-sagas/`](https://github.com/patterncatalyst/cloud-native-design-patterns/tree/main/examples/17-sagas/) passes 11/11 checks in each of the six languages on Docker Engine (2026-10-13) -(orchestrated saga happy path, compensation in reverse order on failure, skipped compensation for steps that never completed, resume of a RUNNING saga after an orchestrator restart).* +```bash +cd examples/17-sagas/python # or any of the six language stacks +docker compose up -d --build --wait +cd .. +./verify.sh +``` + +`verify.sh` runs the language-agnostic end-to-end checks against the running stack +(11/11 in each of the six languages), asserting the happy-path saga runs +`charge_payment`, `reserve_stock`, and `book_shipping` in order with no compensation, +a saga with `book_shipping` rigged to fail ends `COMPENSATED` and compensates in +reverse order (`release_stock` then `refund_payment`), `cancel_shipping` never runs +because shipping never completed, and a saga force-set to `RUNNING` in Postgres +resumes to `COMPLETED` after the orchestrator container is stopped and restarted. diff --git a/_docs/18-appendix-e-errors.md b/_docs/18-appendix-e-errors.md index 2b96f3d4..75fe4a50 100644 --- a/_docs/18-appendix-e-errors.md +++ b/_docs/18-appendix-e-errors.md @@ -837,7 +837,7 @@ maximum, backoff plus jitter, circuit breaker, retry budget) while keeping every handler idempotent. Get those right and the failure paths become as boring as the happy ones. -### Cross-check it yourself +## Build, run, observe Prove the contract holds on the wire, in two protocols. For REST, trigger the stock conflict and inspect the response with curl: `curl -i` should show `HTTP/1.1 409`, a @@ -852,6 +852,16 @@ breaker trip: total downstream calls should *plateau* rather than climb with off load. A flat downstream-call line under rising pressure is the retry budget and breaker doing their job; a climbing one means a control is missing. ---- -*Verification status: verified. [`examples/18-errors/`](https://github.com/patterncatalyst/cloud-native-design-patterns/tree/main/examples/18-errors/) passes 13/13 checks in each of the six languages on Docker Engine (2026-10-13) -(RFC 9457 problem details over REST for validation (422), stock conflict (409) and upstream unavailable (503), with error codes, traceId, retryable and retryAfter fields).* +```bash +cd examples/18-errors/python # or any of the six language stacks +docker compose up -d --build --wait +cd .. +./verify.sh +``` + +`verify.sh` runs the language-agnostic end-to-end checks against the running stack +(13/13 in each of the six languages), asserting a happy-path order returns `201`, an +invalid order returns `422` with `code: VALIDATION_ERROR` and a `traceId`, exhausting +stock returns `409` with `code: STOCK_UNAVAILABLE` and `retryable: false`, and +stopping the inventory container turns the next order into a `503` with +`retryable: true`, a `retryAfter` field, and a `Retry-After` HTTP header. diff --git a/_docs/19-appendix-f-ddd-hexagonal.md b/_docs/19-appendix-f-ddd-hexagonal.md index 14178b1e..d36129df 100644 --- a/_docs/19-appendix-f-ddd-hexagonal.md +++ b/_docs/19-appendix-f-ddd-hexagonal.md @@ -340,7 +340,7 @@ DDD is not a separate methodology bolted onto the architecture; it is the naming the reasoning behind the boundaries the whole system already relies on. The full treatment is Vlad Khononov's *Learning Domain-Driven Design* (O'Reilly, 2021). -### Cross-check it yourself +## Build, run, observe Test the isolation claim directly, because it is the one that pays the rent. In the domain package (`domain/` in Python or C++, the application service and its port @@ -353,6 +353,17 @@ a second driving adapter (a gRPC endpoint, or a Kafka consumer) that calls the * new adapter file appears. A clean domain import list plus a no-domain-change diff when you bolt on a protocol is hexagonal architecture holding. ---- -*Verification status: verified: [`examples/19-ddd-hexagonal/`](https://github.com/patterncatalyst/cloud-native-design-patterns/tree/main/examples/19-ddd-hexagonal/) passes 17/17 checks in each of the six languages on Docker Engine (2026-10-13) -(source scans proving the domain layer imports no framework or persistence types, REST and CLI adapters driving the same use case, input validation, OrderPlaced event emission).* +```bash +cd examples/19-ddd-hexagonal/python # or any of the six language stacks +docker compose up -d --build --wait +cd .. +./verify.sh +``` + +`verify.sh` runs the language-agnostic end-to-end checks against the running stack +(17/17 in each of the six languages), asserting the domain directory imports no web +framework, no JPA/Hibernate, no database driver, and no CDI/DI annotations; that the +REST driving adapter can place and read back an order and rejects invalid input with +`422`; that the CLI driving adapter (`cli-place-order`) creates an order through the +same `PlaceOrder` use case and that order is then visible through the REST adapter; +and that an `OrderPlaced` domain event is logged for the REST-created order. diff --git a/_docs/20-appendix-g-coupling.md b/_docs/20-appendix-g-coupling.md index 3871684c..352c1d36 100644 --- a/_docs/20-appendix-g-coupling.md +++ b/_docs/20-appendix-g-coupling.md @@ -234,10 +234,3 @@ cheapest to pull: usually drive strength down to a contract, since distance and volatility are often fixed by the org chart and the domain. If your worst-scoring integration is already contract-coupled and sits on stable code, the model says to leave it alone, which is the appendix's real lesson: looser is not automatically better. - ---- -*Verification status: unverified. This is a conceptual appendix with no runnable -example; its claims are model-level (Khononov's *Balancing Coupling*) rather than code -that executes. There is no `examples/` runner for it; verification here means a design -review confirms the three-dimension scoring matches how changes propagate in a -real system.* diff --git a/_docs/21-appendix-h-shutdown.md b/_docs/21-appendix-h-shutdown.md index d7814b17..a88bfede 100644 --- a/_docs/21-appendix-h-shutdown.md +++ b/_docs/21-appendix-h-shutdown.md @@ -316,7 +316,20 @@ Read it as an ordered runbook: References: the Quarkus lifecycle guide and Markus Eisele's "Quarkus Graceful Shutdown" (2026); uvicorn's `--timeout-graceful-shutdown`. -### Cross-check it yourself +## Build, run, observe + +```bash +cd examples/21-graceful-shutdown/python # or any of the six language stacks +docker compose up -d --build --wait +cd .. +./verify.sh +``` + +`verify.sh` runs the language-agnostic end-to-end checks against the running stack (17/17 in +each of the six languages), asserting one SIGTERM produces the readiness flip to 503, the drain +log lines, and an exit code of 0 inside the grace period, that a `docker stop` times out cleanly +rather than falling back to `SIGKILL`, and that orders placed before a restart are still present +after it. The one test teams skip is the only one that proves the recipe: a real `SIGTERM` mid-request during a rollout. Put steady traffic on the service with `hey` (a modest concurrency for a @@ -329,7 +342,3 @@ For the Kafka path, restart a consumer pod mid-stream and confirm from the broke group rebalanced quickly and offsets advanced: no long stall, no large redelivery spike. A clean rollout under live load, not a green integration test, is evidence that graceful shutdown works. - ---- -*Verification status: verified: [`examples/21-graceful-shutdown/`](https://github.com/patterncatalyst/cloud-native-design-patterns/tree/main/examples/21-graceful-shutdown/) passes 17/17 checks in each of the six languages on Docker Engine (2026-10-13) -(one SIGTERM, readiness flip to 503, the drain log lines, exit code 0 inside the grace period, a `docker stop` timing check, restart recovery, and orders persisting across the restart. In-flight request draining is not checked by the script).* diff --git a/_docs/22-appendix-i-l7-routing.md b/_docs/22-appendix-i-l7-routing.md index 5104cf58..f4014bed 100644 --- a/_docs/22-appendix-i-l7-routing.md +++ b/_docs/22-appendix-i-l7-routing.md @@ -665,7 +665,19 @@ organisational failure to avoid is the cluster that ends up running three ingres because three teams each picked one: choose one ingress, one gateway, one mesh, manage routing through GitOps, watch certificate rotation, and load-test the failure paths. -### Cross-check it yourself +## Build, run, observe + +```bash +cd examples/22-l7-routing/python # or any of the six language stacks +docker compose up -d --build --wait +cd .. +./verify.sh +``` + +`verify.sh` runs the language-agnostic end-to-end checks against the running stack (15/15 in +each of the six languages), asserting Envoy's weighted traffic split and header-based routing +override, the in-app router sending VIP orders to `orders.priority` and ordinary orders to +`orders.default`, and that changing the routing ruleset takes effect without a redeploy. Prove a routing decision twice: once at the mesh, once in the app. For the mesh: apply the canary `VirtualService`/`DestinationRule`, drive a few hundred requests with `hey`, and confirm @@ -679,7 +691,3 @@ HTTP call) while the ordinary one fell through to `orders.default`; then change ruleset, not the dispatcher, and confirm the routing changes without a redeploy. Weights that track the config and a ruleset that can move routing without a code change are the two halves of this appendix working. - ---- -*Verification status: verified; [`examples/22-l7-routing/`](https://github.com/patterncatalyst/cloud-native-design-patterns/tree/main/examples/22-l7-routing/) passes 15/15 checks in each of the six languages on Docker Engine (2026-10-13) -(Envoy weighted traffic split, header-based routing override, in-app rule-driven routing, and changing routing rules without a redeploy. Canary promotion and circuit breaking are not covered).* diff --git a/_docs/23-appendix-j-dotnet-toolkit.md b/_docs/23-appendix-j-dotnet-toolkit.md index 94ad2cfc..27da336f 100644 --- a/_docs/23-appendix-j-dotnet-toolkit.md +++ b/_docs/23-appendix-j-dotnet-toolkit.md @@ -215,14 +215,3 @@ log line) into Grafana and verify the span carries the service name and the `ActivitySource`-based attributes. A new service that answers both probes and emits a correlated trace on its first request has the spine of every pattern in this book working before you have written any business logic. - ---- -*Verification status: package versions and licenses were checked against nuget.org on -2026-10-10 (Confluent.Kafka 2.16.0, Apache-2.0; Streamiz.Kafka.Net 1.8.2, MIT; Temporalio, -Asp.Versioning and Microsoft.Extensions.Resilience, MIT; Polly 8, BSD-3-Clause). MassTransit -v9 and MediatR are commercially licensed, which is why the examples use Confluent.Kafka -directly and Wolverine is the OSS alternative. The three tables -were transcribed from the source deck, so the rows match the deck; the .NET 10 LTS support -window (through Nov 2028) follows Microsoft's published policy. There is no per-language -`examples/` runner for this appendix; the recipe's cross-check is the closest thing to a -runnable proof.* diff --git a/_docs/24-appendix-k-monolith-to-microservices.md b/_docs/24-appendix-k-monolith-to-microservices.md index b7c6344b..c82d3817 100644 --- a/_docs/24-appendix-k-monolith-to-microservices.md +++ b/_docs/24-appendix-k-monolith-to-microservices.md @@ -572,7 +572,20 @@ architectural take-home is the property named at the start: design every step to config change, because migrations fail when they reach an irreversible step too early. The book-length treatment is Sam Newman's *Monolith to Microservices* (O'Reilly). -### Cross-check it yourself +## Build, run, observe + +```bash +cd examples/24-monolith-to-microservices/python # or any of the six language stacks +docker compose up -d --build --wait +cd .. +./verify.sh +``` + +`verify.sh` runs the language-agnostic end-to-end checks against the running stack (19/19 in +each of the six languages), asserting the strangler fig router's per-tenant content-based +routing, a reversible cutover (flip and flip back), the decorating collaborator adding caching +and Kafka `order.placed` event emission, and that the legacy service remains untouched +throughout. Prove the two properties this appendix rests on: reversibility and equivalence. Stand the content router in front of a stub "monolith" and a stub "new service" that tag their responses, @@ -584,7 +597,3 @@ twice and confirm the second response is served from cache (the legacy backend s two: check its access log), then `POST` once and confirm both that the legacy service recorded the write *and* that an `order.placed` event landed on Kafka. Same client-visible API, new behaviour underneath, legacy untouched: that is the decorator doing its job. - ---- -*Verification status: verified. [`examples/24-monolith-to-microservices/`](https://github.com/patterncatalyst/cloud-native-design-patterns/tree/main/examples/24-monolith-to-microservices/) passes 19/19 checks in each of the six languages on Docker Engine (2026-10-13) -(strangler fig router with per-tenant routing, reversible cutover, a decorating collaborator adding caching and Kafka event emission, and an untouched legacy service).* diff --git a/_docs/25-appendix-l-caching.md b/_docs/25-appendix-l-caching.md index d6f517b4..ba25a02e 100644 --- a/_docs/25-appendix-l-caching.md +++ b/_docs/25-appendix-l-caching.md @@ -1211,7 +1211,20 @@ a small identified hot set at the cost of extra DB load on it. Most production s for the bulk of traffic, layer refresh-ahead on the hot keys they have identified, and use write-through only where stale reads are forbidden: a mix, not a single rule. -### Cross-check it yourself +## Build, run, observe + +```bash +cd examples/25-caching/python # or any of the six language stacks +docker compose up -d --build --wait +cd .. +./verify.sh +``` + +`verify.sh` runs the language-agnostic end-to-end checks against the running stack (29/29 in +each of the six languages), asserting cache-aside with invalidation on update, read-through, +write-through, write-around, write-back with a 202 and a flush retry across a Postgres outage, +refresh-ahead with hot-set tracking, and degradation (not failure) when Redis is stopped and +restarted. Prove the two properties that matter: the cache offloads the database, and a cache outage degrades rather than breaks. Put read load on the cache-aside endpoint with `hey` and watch the @@ -1223,7 +1236,3 @@ cache is wired as a source of truth rather than an optimisation, which is the on cannot ship. Finally, for write-through, write a key and immediately read it back: the read must return the new value with no DB round trip; for write-around, write then read and confirm the first read is a miss (the DB log shows the `SELECT`) while the second is a hit. - ---- -*Verification status: verified; [`examples/25-caching/`](https://github.com/patterncatalyst/cloud-native-design-patterns/tree/main/examples/25-caching/) passes 29/29 checks in each of the six languages on Docker Engine (2026-10-13) -(cache-aside with invalidation on update, read-through, write-through, write-around, write-back with 202 and a flush retry across a Postgres outage, refresh-ahead, hot-set tracking, and degradation with Redis stopped and restarted).* diff --git a/_docs/26-appendix-m-failure-modes.md b/_docs/26-appendix-m-failure-modes.md index 8f1dcb1c..efbc0d6b 100644 --- a/_docs/26-appendix-m-failure-modes.md +++ b/_docs/26-appendix-m-failure-modes.md @@ -756,7 +756,20 @@ and bulkhead were named for software), the gRPC retry design, gRFC A6 (retry pol throttling), the Istio traffic-management docs, and the metastable-failures research (Bronson et al., 2021). -### Cross-check it yourself +## Build, run, observe + +```bash +cd examples/26-failure-modes/python # or any of the six language stacks +docker compose up -d --build --wait +cd .. +./verify.sh +``` + +`verify.sh` runs the language-agnostic end-to-end checks against the running stack (22/22 in +each of the six languages), asserting timeout behaviour, retry exhaustion, the circuit breaker +moving closed to open to recovered with a fallback, deadline-budget propagation across a +two-hop chain, the bulkhead's concurrency limit, and a full bulkhead refusing overflow with 429 +and admitting again once calls finish. Reproduce the two dynamics that this appendix turns on. **Cascading and the breaker:** put steady load on a service with `hey` (or `ghz` for gRPC) while you force its downstream dependency to be @@ -770,7 +783,3 @@ deadline that stops doomed work early, are the two behaviours everything here de This appendix touches difficult operational territory; if you are diagnosing a live incident, the Google SRE cascading-failures chapter is the fastest way to a shared vocabulary with your team. - ---- -*Verification status: verified; [`examples/26-failure-modes/`](https://github.com/patterncatalyst/cloud-native-design-patterns/tree/main/examples/26-failure-modes/) passes 22/22 checks in each of the six languages on Docker Engine (2026-10-13) -(timeout, retry exhaustion, circuit breaker closed, open and recovered with fallback, deadline-budget propagation, bulkhead limit, a full bulkhead refusing the overflow with 429 and admitting again).* diff --git a/_docs/27-appendix-n-feature-flags.md b/_docs/27-appendix-n-feature-flags.md index b77099fb..7c0cb4e8 100644 --- a/_docs/27-appendix-n-feature-flags.md +++ b/_docs/27-appendix-n-feature-flags.md @@ -404,7 +404,18 @@ flag types), and the *Accelerate* / DORA research (Forsgren, Humble, Kim) for th distinction and progressive delivery; Unleash is a capable open-source alternative backend behind the same OpenFeature SDK. -### Cross-check it yourself +## Build, run, observe + +```bash +cd examples/27-feature-flags/python # or any available language stack +docker compose up -d --build --wait +cd .. +./verify.sh +``` + +`verify.sh` runs 14 checks against the running stack: plan-based targeting rules, sticky +assignment and percentage rollout for `new-checkout`, the `recommendations-enabled` kill +switch, the `dark-mode` boolean toggle, flag listing, and safe defaults while flagd is down. Prove the three properties this appendix rests on, against a running flagd. **Decoupling:** with the service running, edit the `new-checkout` flag in flagd's source from `off` to a 25% fractional @@ -415,7 +426,3 @@ confirm the split lands near 25%. **Fail-safe:** stop flagd entirely and confirm returns the coded default and the service keeps serving `200`s. If it instead errors, a flag lookup is on a critical path without a default, which is the one feature-flag mistake you cannot ship. - ---- -*Verification status: verified: [`examples/27-feature-flags/`](https://github.com/patterncatalyst/cloud-native-design-patterns/tree/main/examples/27-feature-flags/) passes 14/14 checks in each of the six languages on Docker Engine (2026-10-13) -(plan-based targeting rules, sticky assignment and percentage rollout, kill switch, simple boolean flag, flag listing, and safe defaults while flagd is down).* diff --git a/_docs/28-appendix-o-newman.md b/_docs/28-appendix-o-newman.md index 524e27d1..fbc81616 100644 --- a/_docs/28-appendix-o-newman.md +++ b/_docs/28-appendix-o-newman.md @@ -264,7 +264,19 @@ the Newman command-line documentation (install, reporters, and the `--iteration- `--bail` flags), and the Postman Learning Center's guides on writing tests with the `pm` API and on JSON-schema validation. -### Cross-check it yourself +## Build, run, observe + +```bash +cd examples/28-newman +docker compose up -d --build --wait +./verify.sh +``` + +`verify.sh` checks health, runs the Postman collection with Newman, confirms the assertion +count comes back with zero failures, checks the CLI output names the 201, 422, 404 and 204 +status assertions and the cancel-status assertion, then re-runs the collection with the JUnit +reporter and confirms the exported `newman.xml` contains `testsuites` and `testcase` elements. +It is the single stack, not six languages. Prove the gate end to end against the running stack. Bring the services up with `docker compose up -d --build --wait`, then `newman run` the orders collection with `--reporters cli,junit` and confirm a green run and a @@ -274,7 +286,3 @@ the failing assertion names the exact expectation. Finally, add a negative row t `--iteration-data` (a zero-quantity order expecting `422`) and confirm it passes against a correct service and fails against one that wrongly accepts it. A suite that goes red on a real contract break, and only then, is the whole value of this appendix. - ---- -*Verification status: verified; [`examples/28-newman/`](https://github.com/patterncatalyst/cloud-native-design-patterns/tree/main/examples/28-newman/) passes 11/11 checks on Docker Engine (2026-10-13) -(a Newman collection run with status-code and state assertions (201, 422, 404, 204, cancel) and JUnit XML export).* diff --git a/_docs/29-appendix-p-dotnet-on-linux.md b/_docs/29-appendix-p-dotnet-on-linux.md index b342ad00..1b128005 100644 --- a/_docs/29-appendix-p-dotnet-on-linux.md +++ b/_docs/29-appendix-p-dotnet-on-linux.md @@ -258,10 +258,3 @@ Then build a container image and inspect its layers. Run `docker image inspect | jq '.[0].RootFS.Layers | length'`: you should see the UBI base layer, the runtime layer, and your application layer. Compare the image size to the SDK image: the runtime image should be roughly 4x smaller. - ---- -*Verification status: unverified. The three diagrams (P.1 compilation pipeline, P.2 -Kestrel pipeline, P.3 container layers) are structurally correct based on the .NET 10 -architecture, and the Containerfile pattern matches the verified examples in this book. The -UBI image sizes (~780 MB SDK, ~170 MB runtime) are approximate and will vary by .NET -version. The NativeAOT section describes general availability behavior as of .NET 8+.* diff --git a/_docs/29-glossary.md b/_docs/29-glossary.md index f620dc35..e794085d 100644 --- a/_docs/29-glossary.md +++ b/_docs/29-glossary.md @@ -142,8 +142,3 @@ might expect. | RFC | Request for Comments | An IETF standard, e.g. RFC 9457 Problem Details, RFC 9745 Deprecation | | IETF | Internet Engineering Task Force | Publisher of the HTTP, Problem Details, and `Idempotency-Key` specs | | MIT, BSD, Apache 2.0, AGPL | open-source software licenses | The licenses on the pinned toolkit and platform components | - ---- -*Verification status: compiled from the book's own usage: every entry appears in the -chapters or appendices. Expansions follow each technology's canonical naming; where a term -is overloaded (`ACL`), the meaning used in this book is called out explicitly.* diff --git a/_docs/30-appendix-q-blazor-signalr.md b/_docs/30-appendix-q-blazor-signalr.md index 6b9cc369..11acc2c3 100644 --- a/_docs/30-appendix-q-blazor-signalr.md +++ b/_docs/30-appendix-q-blazor-signalr.md @@ -167,7 +167,19 @@ over the wire format. **SignalR** when the server is .NET and you want connectio management handled for you. **Blazor Server** when you also want the UI rendered server-side, avoiding a separate frontend framework entirely. -### Cross-check it yourself +## Build, run, observe + +```bash +cd examples/30-blazor-signalr/dotnet +docker compose up -d --build --wait +cd .. +./verify.sh +``` + +`verify.sh` checks health, order placement and listing, the dashboard page, and the SignalR +negotiate endpoint, then drives a minimal Python SignalR client to confirm Redis backplane +delivery in both directions between the two dashboard instances (8080 and 8081) and again +after restarting one instance. It is .NET only, not six languages. Run the Blazor Server example, open the dashboard in two browser tabs, and place an order via `curl`. Both tabs should update simultaneously, confirming the SignalR hub @@ -177,6 +189,3 @@ state. The code is in [`examples/30-blazor-signalr/`](https://github.com/patterncatalyst/cloud-native-design-patterns/tree/main/examples/30-blazor-signalr/). The run script there builds and runs it; its `README.md` covers what it does and how to drive it. - ---- -*Verification status: verified: [`examples/30-blazor-signalr/`](https://github.com/patterncatalyst/cloud-native-design-patterns/tree/main/examples/30-blazor-signalr/) passes 10/10 checks on Docker Engine (2026-10-13): health, order placement and listing, the dashboard page, the SignalR negotiate endpoint, a second dashboard instance, Redis backplane delivery in both directions between the two instances (a SignalR client on one receives an order posted to the other), orders listed across instances, and backplane delivery to a restarted instance (the script asserts the container's start time changed and it returned healthy). Blazor circuit reconnection is the framework default and is not covered by the script.* diff --git a/_docs/31-appendix-r-minikube.md b/_docs/31-appendix-r-minikube.md index dd735200..fe2eb239 100644 --- a/_docs/31-appendix-r-minikube.md +++ b/_docs/31-appendix-r-minikube.md @@ -262,7 +262,22 @@ minikube profile: the scripts refuse to start while anything else is running. | `kubectl` talks to the wrong cluster | Commands without `--context` | Pass `--context cndp-k8s`; the scripts do | | order-service restarts during startup | The startup probe window is shorter than the JVM start on a loaded host | Raise `failureThreshold` on the startup probe in `base/` | -### Cross-check it yourself +## Build, run, observe + +```bash +cd examples/05-event-driven +k8s/setup-profile.sh # create the profile cndp-k8s +k8s/build-images.sh go # build and load a language's images +k8s/deploy.sh go # Strimzi, infrastructure, the overlay +RUNTIME=kubernetes BASE=http://127.0.0.1:30080 ./verify.sh +k8s/teardown.sh # remove the cndp-05 namespace +``` + +With `RUNTIME=kubernetes`, `verify.sh` maps the container names it uses on compose to +`kubectl exec` against the Kafka and Postgres pods, and confirms the order-service health +endpoint, an order visible in `GET /orders`, the `order.placed` topic and both consumer +groups in Kafka, the shipment and notification side effects, and that redelivering the same +event stays idempotent. Place an order through `127.0.0.1:30080`, then run `kubectl --context cndp-k8s -n cndp-05 exec pod/cndp-postgres-0 -- psql -U appuser -d appdb -c "SELECT * FROM shipments"` @@ -272,6 +287,3 @@ and query again: the count is 0. Scale it back to one replica and the count beco consumer group resumes from its last committed offset. The manifests and scripts are in [`examples/05-event-driven/k8s/`](https://github.com/patterncatalyst/cloud-native-design-patterns/tree/main/examples/05-event-driven/k8s/). - ---- -*Verification status: verified: [`examples/05-event-driven/k8s/`](https://github.com/patterncatalyst/cloud-native-design-patterns/tree/main/examples/05-event-driven/k8s/) passes 10/10 checks in each of the six languages on minikube v1.39.0 (Docker driver, containerd, Kubernetes v1.36.5, Strimzi 1.2.0, 2026-10-13, on the code after the S13 and S14 refactors), using the same `verify.sh` as compose with `RUNTIME=kubernetes`. No pod restarted or was OOM-killed under the manifests' memory limits. The consumer scale-down and scale-up in the cross-check was run by hand and is not part of the script.* diff --git a/_docs/32-appendix-s-openshift-local.md b/_docs/32-appendix-s-openshift-local.md index b657be2e..dc962ecb 100644 --- a/_docs/32-appendix-s-openshift-local.md +++ b/_docs/32-appendix-s-openshift-local.md @@ -475,7 +475,24 @@ and exits 0. | Kafka never becomes Ready | The operator is not running, or the Kafka version is not in the bundle | `oc --context crc-admin -n cndp-amq-streams get pods`; `oc --context crc-admin -n cndp-05 describe kafka cndp` | | Namespace stuck terminating | A KafkaTopic finalizer outlived the operator | `teardown.sh` deletes the topics while the operator runs; do not remove the operator first | -### Cross-check it yourself +## Build, run, observe + +```bash +cd examples/05-event-driven +openshift/setup-crc.sh # crc start, ingress CA, registry route +openshift/build-push-images.sh go # docker build, skopeo copy +openshift/deploy.sh go # operator, infrastructure, the overlay +RUNTIME=kubernetes KUBECTL=oc KUBE_CONTEXT=crc-admin KUBE_NAMESPACE=cndp-05 \ + BASE=https://order-service-cndp-05.apps-crc.testing \ + CURL_OPTS="--cacert $HOME/.local/state/cndp-05-crc/ingress-ca.crt" ./verify.sh +openshift/teardown.sh # remove everything, then crc stop +``` + +The same `verify.sh` as compose and minikube, with `KUBECTL=oc` and the CA-verified Route +URL: it confirms the order-service health endpoint, an order visible in `GET /orders`, the +`order.placed` topic and both consumer groups in Kafka, the shipment and notification side +effects, and that redelivering the same event stays idempotent, this time over HTTPS through +`oc exec` against the AMQ Streams and sclorg Postgres pods. Place an order through the Route, then run `oc --context crc-admin -n cndp-05 exec pod/cndp-postgres-0 -- psql -U appuser -d appdb -c "SELECT * FROM shipments"` @@ -487,6 +504,3 @@ assigned range (the `openshift.io/sa.scc.uid-range` annotation on the namespace) on the recorded run; read the ReplicaSet's events if you try it. The manifests and scripts are in [`examples/05-event-driven/openshift/`](https://github.com/patterncatalyst/cloud-native-design-patterns/tree/main/examples/05-event-driven/openshift/). - ---- -*Verification status: verified: [`examples/05-event-driven/openshift/`](https://github.com/patterncatalyst/cloud-native-design-patterns/tree/main/examples/05-event-driven/openshift/) passes 10/10 checks in each of the five languages on OpenShift Local 4.22.14 (crc 2.64.0, Kubernetes v1.35.6, AMQ Streams `amqstreams.v3.2.1-14`, Kafka 4.2.0, 2026-10-13, on the code after the S13 and S14 refactors), using the same `verify.sh` as compose with `RUNTIME=kubernetes` over HTTPS. Every pod ran under `restricted-v2`, Tempo held a trace with all three services for each language, and teardown left the cluster empty and stopped. The SCC rejection of a fixed root user ID was not exercised.* diff --git a/_plans/verification-log.md b/_plans/verification-log.md new file mode 100644 index 00000000..b4382313 --- /dev/null +++ b/_plans/verification-log.md @@ -0,0 +1,109 @@ +# Verification log + +Per-chapter verification-status footers moved here from the chapter bodies. The +reader-facing chapters now carry a `## Build, run, observe` section (the former +"Cross-check it yourself" section, with the example's verify command) instead; +the run records stay here. The `plans` collection is `output: false`, so this +file is not published. Each entry is the footer text verbatim as it last stood. + +## 00-setting-up.md +conceptual chapter; no runnable code. The patterns named here are defined and applied in their own chapters, where the code ships with a runnable example per language. + +## 01-cloud-native-principles.md +verified; examples/01-cloud-native-principles/ passes 13/13 checks in each of the six languages on Docker Engine (2026-10-13) (health probes, config from environment, CRUD, readiness flip on dependency failure). + +## 02-communications.md +verified; examples/02-communications/ passes 10/10 checks in each of the six languages on Docker Engine (2026-10-13) (REST create and validation (201, 422), cursor pagination, order confirmation and rejection driven by the gRPC stock reservation, a GraphQL orders query, and the order.placed Kafka topic). + +## 03-composition.md +verified; examples/03-composition/ passes 8/8 checks in each of the six languages on Docker Engine (2026-10-13) (GraphQL gateway fan-out to the REST order API and the gRPC stock service, selective stock-field resolution, DataLoader batching of stock lookups into one gRPC call, single-order lookup). + +## 04-data.md +verified; examples/04-data/ passes 8/8 checks in each of the six languages on Docker Engine (2026-10-13) (transactional write of order and outbox row, Debezium outbox connector running, relay of the outbox event to the order.placed Kafka topic with its payload). + +## 05-event-driven.md +verified; examples/05-event-driven/ passes 10/10 checks in each of the six languages on Docker Engine (2026-10-13) and on minikube (2026-10-11, Appendix R), and 10/10 in five languages on OpenShift Local 4.22.14 (2026-10-11, Appendix S) (event publication to order.placed, fan-out to two independent consumer groups (shipping and notification), and idempotent consumers under a redelivered duplicate event). + +## 06-stream-processing.md +verified; examples/06-stream-processing/ passes 11/11 checks in each of the six languages on Docker Engine (2026-10-13) (per-merchant revenue aggregation onto a derived Kafka topic, the stream-processor consumer group, consumer lag that builds while the processor is paused and drains on resume, and the burst appearing in the revenue stream). + +## 07-workflows-and-jobs.md +conceptual chapter with an illustrative Kubernetes manifest; no per-language runnable example. The coordination patterns it names are made concrete in the saga appendix. + +## 08-api-management.md +conceptual chapter: no per-language runnable code. The runtime-plane and contract-plane behaviours it describes are exercised in the security, registry, and Appendix B chapters. + +## 09-api-registry.md +verified; examples/09-api-registry/ passes 8/8 checks on Docker Engine (2026-10-13) (schema registration, BACKWARD compatibility rule, rejection of a breaking change with 400, acceptance of an additive change, versioned retrieval). Language-agnostic (single Apicurio stack). + +## 10-api-metadata.md +conceptual chapter: no per-language runnable code. The discovery, lineage, and trust capabilities it describes are exercised against a running OpenMetadata catalog in the example stack. + +## 11-observability.md +verified; examples/11-observability/ passes 10/10 checks in each of the six languages on Docker Engine (2026-10-13) (trace propagation, metric export, structured log correlation, LGTM stack integration). + +## 12-security.md +verified; examples/12-security/ passes 18/18 checks in each of the six languages on Docker Engine (2026-10-13) (sidecar identity trust (403 without X-Forwarded-Client-Cert, health-check bypass), propagation of the JWT subject claim, valet-key mint/verify/tamper rejection, per-tenant bulkhead state, a full tenant pool refusing the overflow with 429 while another tenant is still served, and conftest Rego policy evaluation of a good and a bad manifest. JWT signature validation, RBAC enforcement and rate limiting are not covered). + +## 13-anti-patterns.md +conceptual chapter; no runnable code. Its heuristics are the review-time application of the patterns proven in the earlier chapters. + +## 14-appendix-a-protocols.md +the gRPC stub call and server-streaming handler are shown in all six languages and mirror the patterns compiled and run in the Communications chapter; the comparison axes, streaming modes, and performance levers are conceptual. The protocol choices this appendix argues are exercised in Communications and Composition. + +## 15-appendix-b-versioning.md +unverified: examples transcribed from the source decks; the REST, gRPC, and GraphQL compatibility behaviour and the Kafka schema.version header are exercised against the registry in the examples/09-api-registry/ runner. + +## 16-appendix-c-websockets.md +verified; examples/16-websockets/ passes 6/6 checks in each of the six languages on Docker Engine (2026-10-13) (WebSocket connect, cross-pod delivery and broadcast over the Redis pub/sub backplane, monotonic sequence numbers). + +## 17-appendix-d-sagas.md +verified; examples/17-sagas/ passes 11/11 checks in each of the six languages on Docker Engine (2026-10-13) (orchestrated saga happy path, compensation in reverse order on failure, skipped compensation for steps that never completed, resume of a RUNNING saga after an orchestrator restart). + +## 18-appendix-e-errors.md +verified; examples/18-errors/ passes 13/13 checks in each of the six languages on Docker Engine (2026-10-13) (RFC 9457 problem details over REST for validation (422), stock conflict (409) and upstream unavailable (503), with error codes, traceId, retryable and retryAfter fields). + +## 19-appendix-f-ddd-hexagonal.md +verified; examples/19-ddd-hexagonal/ passes 17/17 checks in each of the six languages on Docker Engine (2026-10-13) (source scans proving the domain layer imports no framework or persistence types, REST and CLI adapters driving the same use case, input validation, OrderPlaced event emission). + +## 20-appendix-g-coupling.md +unverified. Conceptual appendix with no runnable example; its claims are model-level (Khononov's Balancing Coupling). Verification here means a design review confirms the three-dimension scoring matches how changes propagate in a real system. + +## 21-appendix-h-shutdown.md +verified; examples/21-graceful-shutdown/ passes 17/17 checks in each of the six languages on Docker Engine (2026-10-13) (one SIGTERM, readiness flip to 503, the drain log lines, exit code 0 inside the grace period, a docker stop timing check, restart recovery, and orders persisting across the restart. In-flight request draining is not checked by the script). + +## 22-appendix-i-l7-routing.md +verified; examples/22-l7-routing/ passes 15/15 checks in each of the six languages on Docker Engine (2026-10-13) (Envoy weighted traffic split, header-based routing override, in-app rule-driven routing, and changing routing rules without a redeploy. Canary promotion and circuit breaking are not covered). + +## 23-appendix-j-dotnet-toolkit.md +package versions and licenses checked against nuget.org on 2026-10-10 (Confluent.Kafka 2.16.0 Apache-2.0; Streamiz.Kafka.Net 1.8.2 MIT; Temporalio, Asp.Versioning, Microsoft.Extensions.Resilience MIT; Polly 8 BSD-3-Clause). MassTransit v9 and MediatR are commercially licensed, which is why the examples use Confluent.Kafka directly and Wolverine is the OSS alternative. Three tables transcribed from the source deck; .NET 10 LTS support window through Nov 2028 per Microsoft policy. No per-language examples/ runner for this appendix. + +## 24-appendix-k-monolith-to-microservices.md +verified; examples/24-monolith-to-microservices/ passes 19/19 checks in each of the six languages on Docker Engine (2026-10-13) (strangler fig router with per-tenant routing, reversible cutover, a decorating collaborator adding caching and Kafka event emission, and an untouched legacy service). + +## 25-appendix-l-caching.md +verified; examples/25-caching/ passes 29/29 checks in each of the six languages on Docker Engine (2026-10-13) (cache-aside with invalidation on update, read-through, write-through, write-around, write-back with 202 and a flush retry across a Postgres outage, refresh-ahead, hot-set tracking, and degradation with Redis stopped and restarted). + +## 26-appendix-m-failure-modes.md +verified; examples/26-failure-modes/ passes 22/22 checks in each of the six languages on Docker Engine (2026-10-13) (timeout, retry exhaustion, circuit breaker closed/open/recovered with fallback, deadline-budget propagation, bulkhead limit, a full bulkhead refusing the overflow with 429 and admitting again). + +## 27-appendix-n-feature-flags.md +verified; examples/27-feature-flags/ passes 14/14 checks in each of the six languages on Docker Engine (2026-10-13) (plan-based targeting rules, sticky assignment and percentage rollout, kill switch, simple boolean flag, flag listing, and safe defaults while flagd is down). + +## 28-appendix-o-newman.md +verified; examples/28-newman/ passes 11/11 checks on Docker Engine (2026-10-13) (a Newman collection run with status-code and state assertions (201, 422, 404, 204, cancel) and JUnit XML export). Language-agnostic (single stack). + +## 29-appendix-p-dotnet-on-linux.md +unverified. The three diagrams (P.1 compilation pipeline, P.2 Kestrel pipeline, P.3 container layers) are structurally correct for the .NET 10 architecture, and the Containerfile pattern matches the verified examples. UBI image sizes (~780 MB SDK, ~170 MB runtime) are approximate and vary by .NET version. The NativeAOT section describes general-availability behaviour as of .NET 8+. + +## 29-glossary.md +compiled from the book's own usage: every entry appears in the chapters or appendices. Expansions follow each technology's canonical naming; where a term is overloaded (ACL), the meaning used in this book is called out explicitly. + +## 30-appendix-q-blazor-signalr.md +verified; examples/30-blazor-signalr/ passes 10/10 checks on Docker Engine (2026-10-13): health, order placement and listing, the dashboard page, the SignalR negotiate endpoint, a second dashboard instance, Redis backplane delivery in both directions between the two instances, orders listed across instances, and backplane delivery to a restarted instance. Blazor circuit reconnection is the framework default and is not covered by the script. .NET-only. + +## 31-appendix-r-minikube.md +verified; examples/05-event-driven/k8s/ passes 10/10 checks in each of the six languages on minikube v1.39.0 (Docker driver, containerd, Kubernetes v1.36.5, Strimzi 1.2.0, 2026-10-13, on the code after the S13 and S14 refactors), using the same verify.sh as compose with RUNTIME=kubernetes. No pod restarted or was OOM-killed under the manifests' memory limits. The consumer scale-down and scale-up in the cross-check was run by hand and is not part of the script. + +## 32-appendix-s-openshift-local.md +verified; examples/05-event-driven/openshift/ passes 10/10 checks in each of the five languages on OpenShift Local 4.22.14 (crc 2.64.0, Kubernetes v1.35.6, AMQ Streams amqstreams.v3.2.1-14, Kafka 4.2.0, 2026-10-13, on the code after the S13 and S14 refactors), using the same verify.sh as compose with RUNTIME=kubernetes over HTTPS. Every pod ran under restricted-v2, Tempo held a trace with all three services for each language, and teardown left the cluster empty and stopped. The SCC rejection of a fixed root user ID was not exercised.