Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions PRD.md
Original file line number Diff line number Diff line change
Expand Up @@ -233,7 +233,7 @@ end to end. Specifically:
The reference ships ONE runnable example tree at
`examples/lgtm-datamesh/`. Principal entry points:

- **`scripts/bootstrap-capstone.sh`** — brings the whole system up on a
- **`scripts/bootstrap-datamesh.sh`** — brings the whole system up on a
fresh minikube profile in ten steps. Each step has a success criterion
printed; the script stops on first failure with a fix hint.
- **`demos/walkthrough.sh`** — the five-act presenter walkthrough that
Expand Down Expand Up @@ -325,7 +325,7 @@ schema registry, OpenMetadata, Prometheus, Tempo, Grafana, Kiali.
Version pins live in the bootstrap and setup scripts; the reconciliation
file tracks any deferred upgrades.

The historical decision log at `_plans/archive/capstone-decisions.md`
The historical decision log at `_plans/archive/final-project-decisions.md`
captures every version choice with rationale.

---
Expand All @@ -345,7 +345,7 @@ captures every version choice with rationale.
## 11. Timeline and milestones

This reference was extracted from
`patterncatalyst/minikube-on-fedora`'s §17 capstone as of that repo's r28
`patterncatalyst/minikube-on-fedora`'s §17 final project as of that repo's r28
(June 2026). The original build's milestones (CAP-001 through CAP-047)
are preserved in `_plans/archive/`. Forward milestones for this repo are
tracked in `_plans/decisions.md` starting from DRA-001 (the extraction
Expand Down Expand Up @@ -375,8 +375,8 @@ itself).
## 13. Decision log pointer

Active decisions live in `_plans/decisions.md`, numbered from DRA-001.
Historical decisions from the capstone era live in
`_plans/archive/capstone-decisions.md`, numbered CAP-001 through CAP-047.
Historical decisions from the final-project era live in
`_plans/archive/final-project-decisions.md`, numbered CAP-001 through CAP-047.
The two series are deliberately distinct to keep the audit trail
unambiguous.

Expand Down
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ end to end, verified order-independent (see
conceptual deck) and *Data Mesh on OpenShift* (the implementation deck).
- **`assets/diagrams/`** — paired SVG + Excalidraw sources for every figure.
- **`_plans/`** — active decisions and reconciliation tracking.
Historical material from the capstone era is preserved under
Historical material from the final-project era is preserved under
`_plans/archive/`.
- **`scripts/`** — top-level helper scripts: cross-reference and Liquid
collision linters, a Fedora prerequisites audit, setup helpers for
Expand All @@ -126,7 +126,7 @@ for a read-only host check), then:

```bash
cd examples/lgtm-datamesh
./scripts/bootstrap-capstone.sh # the whole stack, ten gated tiers
./scripts/bootstrap-datamesh.sh # the whole stack, ten gated tiers
./demos/walkthrough.sh # the five-act presenter walkthrough
```

Expand All @@ -139,7 +139,7 @@ An optional OpenShift Local (CRC) appendix uses podman. <!-- forbidden-ok -->

## Origin

This repo is a fork of the §17 capstone from
This repo is a fork of the §17 final project from
[`patterncatalyst/minikube-on-fedora`](https://github.com/patterncatalyst/minikube-on-fedora).
The parent repo's tutorial (§1–§16) remains the canonical Minikube-on-Fedora
guide; this repo is the data-mesh reference broken out for its own audience.
Expand Down
4 changes: 2 additions & 2 deletions _config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ permalink: pretty
# Collections — _docs and _plans are not Jekyll defaults; this enables them.
# Unlike the upstream minikube-tutorial repo, this site's _docs IS the
# data-mesh content (it's not nested under a parent tutorial). There's no
# separate _capstone collection.
# separate _datamesh collection.
collections:
docs:
output: true
Expand Down Expand Up @@ -68,7 +68,7 @@ exclude:
- scripts/
- README.md
- LICENSE
- _plans/archive/ # archived capstone plans contain inline {% %} prose Liquid can't parse
- _plans/archive/ # archived final-project plans contain inline {% %} prose Liquid can't parse
- PRD.md
- LESSONS-LEARNED.md
- GETTING-STARTED.md
Expand Down
2 changes: 1 addition & 1 deletion _docs/00-index.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ the value it delivers and the implementation pieces that realize it.
catalog is a mesh requirement rather than an add-on.
- [**6 · The data planes**]({{ '/docs/05-data-planes/' | relative_url }}) —
The synchronous read layer (REST, gRPC, a GraphQL gateway) and the asynchronous event
backbone — and why the capstone uses all of them.
backbone — and why the final project uses all of them.
- [**7 · Progressive delivery & mTLS**]({{ '/docs/06-progressive-delivery-mtls/' | relative_url }}) —
Evolving a contract in the open with a v1→v2 canary, mTLS with no application changes, and the decision to
mesh selectively rather than namespace-wide.
Expand Down
6 changes: 3 additions & 3 deletions _docs/01-concepts.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ rather than leaving analytical data as a stale downstream copy.

![Operational vs. analytical data planes]({{ '/assets/diagrams/17-operational-vs-analytical.svg' | relative_url }})

This capstone models the **operational** side concretely — services that own their
This final project models the **operational** side concretely — services that own their
data and emit events — and shows how analytical consumers attach to that operational
flow through the event backbone, rather than through a nightly extract. That's the
loop the mesh is meant to keep closed.
Expand All @@ -81,7 +81,7 @@ loop the mesh is meant to keep closed.

Dehghani's data mesh rests on four interlocking principles. They depend on each other —
implement one without the others and you get a distributed mess rather than a mesh —
and each shows up explicitly in this capstone:
and each shows up explicitly in this final project:

![The four principles and how they map to Kubernetes primitives]({{ '/assets/diagrams/17-data-mesh-four-principles.svg' | relative_url }})

Expand Down Expand Up @@ -122,7 +122,7 @@ re-centralized into an approval bottleneck.
## The pattern is not the tools

A data mesh isn't a product, a tool, or a vendor offering — it's an organizational and
architectural pattern. The tools this capstone uses are *expressions* of the pattern,
architectural pattern. The tools this final project uses are *expressions* of the pattern,
chosen because each one makes a principle concrete and runnable, not because any of
them *is* the mesh. That distinction matters enough that it's the first
[anti-pattern]({{ '/docs/09-anti-patterns/' | relative_url }}): the most
Expand Down
24 changes: 12 additions & 12 deletions _docs/02-kubernetes-substrate.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,19 +11,19 @@ By the end of this chapter you can:

- Map each of Dehghani's four principles to the specific Kubernetes primitive that
realizes it (namespaces/RBAC, Deployments/CRDs, operators, admission control).
- Describe the three horizontal tiers of this capstone's architecture and which
- Describe the three horizontal tiers of this final project's architecture and which
protocol crosses each boundary.
- Explain why this capstone runs on minikube with the docker driver and containerd
- Explain why this final project runs on minikube with the docker driver and containerd
runtime, and what trade-off a single-node cluster makes.
- State how the capstone exposes host access (NodePorts published on `127.0.0.1` at
- State how the final project exposes host access (NodePorts published on `127.0.0.1` at
profile creation) and what command recreates the profile if the published ports
need to change.

## Why this matters

The [previous page]({{ '/docs/01-concepts/' | relative_url }}) ended on a
claim: a data mesh is a pattern, not a tool, and the tools are
expressions of it. So why build the capstone on Kubernetes at all? Because the four
expressions of it. So why build the final project on Kubernetes at all? Because the four
principles map onto Kubernetes primitives unusually cleanly — cleanly enough that
"implement a data mesh on Kubernetes" stops feeling like a translation exercise and
starts feeling like the primitives were waiting for it. This page makes that mapping
Expand Down Expand Up @@ -84,12 +84,12 @@ the system, which is exactly what "computational governance" means.

## The shape of the system

With the mapping in hand, here's the system this capstone builds. It has three
With the mapping in hand, here's the system this final project builds. It has three
horizontal tiers: **external clients** at the top, the **service mesh** running the
domain services in the middle, and the **self-serve platform** providing shared
infrastructure underneath.

![Capstone architecture — data mesh on minikube]({{ '/assets/diagrams/17-capstone-data-mesh.svg' | relative_url }})
![The final project's architecture — data mesh on minikube]({{ '/assets/diagrams/17-final-project-data-mesh.svg' | relative_url }})

The diagram doubles as a map of the protocol decisions, which the
[data planes page]({{ '/docs/05-data-planes/' | relative_url }}) develops
Expand All @@ -109,7 +109,7 @@ return to as the rest of the set works through the parts.

## A note on minikube specifically

This capstone runs on minikube — a single-node Kubernetes cluster — which is the right
This final project runs on minikube — a single-node Kubernetes cluster — which is the right
choice for *learning* the pattern and wrong for running it in production. A single node
means every tier shares one machine's resources, which keeps the whole mesh runnable on
a laptop but also concentrates failure modes that a real multi-node cluster would
Expand All @@ -125,13 +125,13 @@ the runc runtime, sized by `MINIKUBE_MEMORY`, `MINIKUBE_CPUS` and `MINIKUBE_DISK

### Host access

Host access follows the same single-node logic. The capstone's UIs and service
Host access follows the same single-node logic. The final project's UIs and service
endpoints are NodePorts published on `127.0.0.1` when the minikube profile is created
(`--ports=127.0.0.1:<hostPort>:<nodePort>`), never SSH tunnels or port-forwarding. <!-- forbidden-ok -->

The ports are fixed at creation, so changing the published set means recreating the
profile with `./scripts/setup-capstone-profile.sh --replace`. `--replace` deletes and
recreates the cluster, so run `./scripts/bootstrap-capstone.sh` again afterwards.
profile with `./scripts/setup-datamesh-profile.sh --replace`. `--replace` deletes and
recreates the cluster, so run `./scripts/bootstrap-datamesh.sh` again afterwards.

Run the workshop in isolation: shut down CRC, other minikube profiles, and other
workloads first, so the host ports are free. `./scripts/show-endpoints.sh` prints what
Expand All @@ -144,11 +144,11 @@ is published and reachable.
- *Platform Engineering on Kubernetes* by Mauricio Salatino — the self-serve platform
principle built out as an operating model, not just a set of operators.
- *Cloud Native Patterns* by Cornelia Davis — the broader cloud-native platform
patterns this capstone's shared infrastructure tier follows.
patterns this final project's shared infrastructure tier follows.
- [Kubernetes documentation](https://kubernetes.io/docs/) — namespaces, RBAC, CRDs,
and admission control, straight from the source.
- [minikube documentation](https://minikube.sigs.k8s.io/docs/) — driver and runtime
options, including the docker driver and containerd runtime this capstone uses.
options, including the docker driver and containerd runtime this final project uses.

Next, the services themselves: what a data product looks like in this build, the
order-service template the others follow, and how each one is packaged and shipped.
18 changes: 9 additions & 9 deletions _docs/03-services-and-data-products.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ By the end of this chapter you can:

With the [principles]({{ '/docs/01-concepts/' | relative_url }}) and the
[Kubernetes mapping]({{ '/docs/02-kubernetes-substrate/' | relative_url }})
in place, this page gets concrete: what the data products in this capstone actually
in place, this page gets concrete: what the data products in this final project actually
are, the one service we build end-to-end as a template for the rest, and how a data
product is packaged and shipped as a container image. This is the first
implementation-heavy page — the conceptual scaffolding is behind us.
Expand All @@ -34,7 +34,7 @@ make it discoverable and governed.

![Anatomy of a data product — ports in, ports out, transformation and governance inside]({{ '/assets/diagrams/17-data-product-anatomy.svg' | relative_url }})

In this capstone, that abstraction is concrete: **each domain service *is* a data
In this final project, that abstraction is concrete: **each domain service *is* a data
product.** It owns a slice of the database (its input/internal state), it serves data
through its APIs (output ports), it emits events as other domains' input, and it
publishes a contract and metadata so it can be discovered and depended on. The service
Expand Down Expand Up @@ -72,7 +72,7 @@ real without running five separate databases on a single learning node.

## Build one service end to end first

Rather than build all six services a layer at a time, the capstone takes a single
Rather than build all six services a layer at a time, the final project takes a single
service all the way through first — a *walking skeleton*. The point is to prove the
entire spine works before widening: build the image, get it to the cluster, deploy via
helm, have the operator-managed Postgres come up, the service connect, and data
Expand All @@ -92,12 +92,12 @@ rather than carrying the dependency manager into the runtime.

The image itself is multi-stage: a builder stage resolves dependencies, and a slim
runtime stage copies only the resolved environment and the application code, runs as a
non-root user, and serves the app. Standard production hygiene — the capstone just
non-root user, and serves the app. Standard production hygiene — the final project just
applies it from the start rather than retrofitting it.

## The one part that fights back: getting images to the kubelet

This is worth its own section because it's the single part of the capstone that
This is worth its own section because it's the single part of the final project that
reliably trips people up. The profile is a Docker container (the docker driver) running
containerd, and that container has its own image store, separate from the Docker Engine
on your host. An image you build locally is invisible to the kubelet until you put it
Expand All @@ -106,7 +106,7 @@ cluster.

### Build with Docker Engine, then load the image

The capstone's answer is simple: **build with Docker Engine, then load the
The final project's answer is simple: **build with Docker Engine, then load the
image into the profile with `minikube image load`**. There is no registry to run and no
addresses to reconcile, and it works the same on a native engine and on a VM-based one
(Docker Desktop, for example). That portability is the reason for the choice:
Expand All @@ -115,15 +115,15 @@ the host's network, which a VM-based engine's does not.

### Tags and pull policy

Images are tagged `capstone/<svc>:v1` and the charts set `imagePullPolicy: Never`. The
Images are tagged `datamesh/<svc>:v1` and the charts set `imagePullPolicy: Never`. The
policy matters in both directions. If an image was never loaded, the pod fails at once
with `ErrImageNeverPull` instead of quietly trying Docker Hub for a name that exists
nowhere. And because `:v1` is a mutable tag, a rebuild does not change what running pods
use, so `scripts/build-image.sh` restarts the Deployments that use the image it just
loaded.

None of this is unique to the capstone — any local cluster has some version of the gap between the host's images and the node's — but
the capstone is where it bites, because it's the first place you build and deploy your
None of this is unique to the final project — any local cluster has some version of the gap between the host's images and the node's — but
the final project is where it bites, because it's the first place you build and deploy your
*own* images at scale. Get the image workflow right once here, and every service
afterward is the same three commands. (The deeper operational sharp edges of running
all this on a single node are collected as gotchas, separate from this build-level
Expand Down
8 changes: 4 additions & 4 deletions _docs/04-contracts-and-catalog.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,9 @@ a field gets renamed, a type changes, and a consumer three domains away breaks w
warning. The mesh's answer is to give every contract a versioned home in a single
registry, regardless of its format.

![Each service's contract surface — OpenAPI, Protobuf, schema, SDL]({{ '/assets/diagrams/17-capstone-contracts.svg' | relative_url }})
![Each service's contract surface — OpenAPI, Protobuf, schema, SDL]({{ '/assets/diagrams/17-final-project-contracts.svg' | relative_url }})

This capstone uses **Apicurio** as that registry, holding all four contract types as
This final project uses **Apicurio** as that registry, holding all four contract types as
their native artifacts: the event schemas, the gRPC Protobuf definitions, the REST
OpenAPI documents, and the gateway's GraphQL SDL. One registry, every contract — so
there's a single place to ask "what is this product's shape, and what version am I
Expand All @@ -47,7 +47,7 @@ depending on?"
Here's the distinction that makes the registry more than a documentation folder, and
it's worth getting clear because the two jobs have very different coupling.

![How a contract flows — the runtime path versus the discovery path]({{ '/assets/diagrams/17-capstone-contract-flow.svg' | relative_url }})
![How a contract flows — the runtime path versus the discovery path]({{ '/assets/diagrams/17-final-project-contract-flow.svg' | relative_url }})

### Runtime contracts

Expand Down Expand Up @@ -81,7 +81,7 @@ ultimately came from. That's the catalog's job.

![Contracts in the registry, discovery and lineage in the catalog]({{ '/assets/diagrams/17-contracts-registry-catalog.svg' | relative_url }})

This capstone layers **OpenMetadata** on top as the catalog. It ingests from the
This final project layers **OpenMetadata** on top as the catalog. It ingests from the
registry (the contracts), from the database (the schemas each domain owns), and from
the event backbone (the topics), and assembles the picture a consumer actually needs:
the products that exist, their schemas, their owners, and — crucially — their
Expand Down
Loading
Loading