From c92d9ddc8fb2a208b9241d42f0204a53ecc42900 Mon Sep 17 00:00:00 2001 From: PatternCatalyst Date: Wed, 28 Oct 2026 09:29:18 -0400 Subject: [PATCH 1/2] docs(deck): professional-voice pass on the data-mesh decks Remove em dashes, chapter/section cross-references, and ban-tier verbal tics (deliberately, earns its place, load-bearing, worth noticing, honest, on purpose, not a toy, rest of this deck) from The Data Mesh 101 and Data Mesh on OpenShift pptxgenjs sources, then rebuild both decks. --- .../data-mesh-101/The_Data_Mesh_101.pptx | Bin 1943849 -> 1943669 bytes presentation/data-mesh-101/build-101.js | 84 ++--- .../Data_Mesh_on_OpenShift.pptx | Bin 8239981 -> 8239219 bytes .../data-mesh-openshift/build-deck.js | 298 +++++++++--------- presentation/data-mesh-openshift/deck-lib.js | 10 +- 5 files changed, 196 insertions(+), 196 deletions(-) diff --git a/presentation/data-mesh-101/The_Data_Mesh_101.pptx b/presentation/data-mesh-101/The_Data_Mesh_101.pptx index fd72e788cce613f4fdefe9a8d1d5497f6a6a9a0c..6b37da96d4e3df79cabca61072e186aa3758a5d4 100644 GIT binary patch delta 6122 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 I3VT`jFAI@_i~s-t delta 6356 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 diff --git a/presentation/data-mesh-101/build-101.js b/presentation/data-mesh-101/build-101.js index 5fa2864..f694e1e 100644 --- a/presentation/data-mesh-101/build-101.js +++ b/presentation/data-mesh-101/build-101.js @@ -1,4 +1,4 @@ -// build-101.js — Data Mesh 101 deck with the data-architecture disambiguation section. +// build-101.js: Data Mesh 101 deck with the data-architecture disambiguation section. // Run from presentation/data-mesh-101/: node build-101.js // Requires: pptxgenjs (global or local), dpng/ with rasterized diagrams + dims.json const pptxgen = require("pptxgenjs"); @@ -25,7 +25,7 @@ const pres = new pptxgen(); pres.defineLayout({ name: "W", width: 13.333, height: 7.5 }); pres.layout = "W"; pres.author = "Robert Sedor"; -pres.title = "The Data Mesh — A Primer"; +pres.title = "The Data Mesh: A Primer"; const PW = 13.333, PH = 7.5; let PAGENO = 0; @@ -116,116 +116,116 @@ function diagramSlide({ eyebrow, title, image, caption, notes }) { titleSlide({ eyebrow: "Data Mesh · Primer", title: "The Data Mesh", - subtitle: "From pipelines and warehouses to decentralized, domain-owned data products — the landscape, the principles, and why the mesh is a different kind of answer.", + subtitle: "From pipelines and warehouses to decentralized, domain-owned data products: the landscape, the principles, and why the mesh is a different kind of answer.", tagline: "A primer on data architectures", breadcrumb: "Pipelines · Warehouses · Lakes · Data Mesh · Dehghani's Four Principles", - notes: "Welcome. This deck makes the case for data mesh by first grounding the audience in the landscape of data architectures that came before it. We'll walk through four patterns — data pipelines, data warehouses, data lakes, and data mesh — and show what each solves, where each stalls, and why the mesh is a different kind of answer. Then we'll define data mesh precisely through Dehghani's four principles.", + notes: "Welcome. This deck makes the case for data mesh by first grounding the audience in the landscape of data architectures that came before it. We'll walk through four patterns (data pipelines, data warehouses, data lakes, and data mesh) and show what each solves, where each stalls, and why the mesh is a different kind of answer. Then we'll define data mesh precisely through Dehghani's four principles.", }); /* ============================ 01 · THE LANDSCAPE ============================ */ -divider({ num: "01", title: "The landscape", sub: "Four data architectures — what each pattern solves and the limitation it leaves behind.", - notes: "Section 1 covers the data architecture landscape. The goal is to build the audience's understanding of why data mesh exists by walking through the patterns that came before it. Each pattern solved a real problem; the mesh is not a replacement for all of them but a response to a specific organizational scaling challenge. We'll spend enough time on each to make the mesh's value proposition concrete rather than abstract." }); +divider({ num: "01", title: "The landscape", sub: "Four data architectures: what each pattern solves and the limitation it leaves behind.", + notes: "The data architecture landscape sets up why data mesh exists. The goal is to build the audience's understanding of why data mesh exists by walking through the patterns that came before it. Each pattern solved a real problem; the mesh is not a replacement for all of them but a response to a specific organizational scaling challenge. We'll spend enough time on each to make the mesh's value proposition concrete rather than abstract." }); /* ---- Data Pipelines ---- */ -contentSlide({ eyebrow: "Data architectures", title: "Data pipelines — the plumbing", +contentSlide({ eyebrow: "Data architectures", title: "Data pipelines: the plumbing", bullets: [ { text: "The oldest data architecture problem is movement: operational systems produce data, analytical systems consume it, and something has to get it from one to the other." }, - { text: "A data pipeline is that something — a sequence of steps that extracts data from a source, transforms it, and loads it into a target store.", lvl: 1 }, + { text: "A data pipeline is that something: a sequence of steps that extracts data from a source, transforms it, and loads it into a target store.", lvl: 1 }, { head: true, text: "ETL vs. ELT" }, { text: "ETL (extract-transform-load): transformation happens in transit, before the data lands. ELT (extract-load-transform): raw data lands first, transformation happens in the target." }, { text: "Both are pipelines: linear, bespoke, point-to-point flows from a known source to a known destination.", lvl: 1 }, { head: true, text: "The limitation: pipeline sprawl" }, - { text: "Each source-destination pair needs its own pipeline. The total grows as the product of sources and destinations, not the sum. Nobody 'owns' the data flowing through a pipeline — the pipeline is plumbing, not a product.", color: C.ink }, + { text: "Each source-destination pair needs its own pipeline. The total grows as the product of sources and destinations, not the sum. Nobody 'owns' the data flowing through a pipeline; the pipeline is plumbing, not a product.", color: C.ink }, ], - notes: "Start with the oldest pattern. Define pipelines concretely: extract-transform-load, or extract-load-transform. The distinction matters (where does the business logic live?) but both are the same shape: linear, bespoke, point-to-point. The limitation is pipeline sprawl — and it's combinatorial. 15 sources times 5 destinations isn't 20 pipelines, it's closer to 75, each with its own schedule, transformation logic, and failure modes. Nobody owns the data in a pipeline — the pipeline is plumbing, not a product. That distinction becomes central when we get to data mesh. The sprawl is what the warehouse was designed to tame.", + notes: "Start with the oldest pattern. Define pipelines concretely: extract-transform-load, or extract-load-transform. The distinction matters (where does the business logic live?) but both are the same shape: linear, bespoke, point-to-point. The limitation is pipeline sprawl, and it's combinatorial. 15 sources times 5 destinations isn't 20 pipelines, it's closer to 75, each with its own schedule, transformation logic, and failure modes. Nobody owns the data in a pipeline; the pipeline is plumbing, not a product. That distinction becomes central when we get to data mesh. The sprawl is what the warehouse was designed to tame.", }); -diagramSlide({ eyebrow: "Data architectures", title: "Pipeline architecture — linear, bespoke, multiplying", +diagramSlide({ eyebrow: "Data architectures", title: "Pipeline architecture: linear, bespoke, multiplying", image: "01-data-pipeline-architecture", caption: "Extract-transform-load: each source-destination pair requires its own pipeline, and the total grows as the product of sources and destinations.", - notes: "Walk the diagram left to right. Sources on the left — CRM, ERP, logs, events — each with its own characteristics and cadence. The ETL box in the center is one pipeline, with its own transformation logic, schedule, and failure modes. Two destinations on the right. Now look at the bottom callout: pipeline sprawl. Each new source or destination doesn't just add one pipeline — it adds as many as there are things on the other side. The dashed lines show the fan-out. At organizational scale, this becomes unmanageable: nobody can see the whole picture, nobody owns the data in transit, and when something breaks at 2 a.m., the question 'whose problem is this?' rarely has a clear answer." }); + notes: "Walk the diagram left to right. Sources on the left (CRM, ERP, logs, events), each with its own characteristics and cadence. The ETL box in the center is one pipeline, with its own transformation logic, schedule, and failure modes. Two destinations on the right. Now look at the bottom callout: pipeline sprawl. Each new source or destination doesn't just add one pipeline; it adds as many as there are things on the other side. The dashed lines show the fan-out. At organizational scale, this becomes unmanageable: nobody can see the whole picture, nobody owns the data in transit, and when something breaks at 2 a.m., the question 'whose problem is this?' rarely has a clear answer." }); /* ---- Data Warehouse ---- */ -contentSlide({ eyebrow: "Data architectures", title: "Data warehouses — the single source of truth", +contentSlide({ eyebrow: "Data architectures", title: "Data warehouses: the single source of truth", bullets: [ { text: "A data warehouse is a centralized analytical store, purpose-built for structured queries across the entire business. Where pipelines are plumbing, the warehouse is a destination." }, - { text: "Star schemas, fact tables, dimension tables — all optimized for the aggregations and joins that BI tools need.", lvl: 1 }, + { text: "Star schemas, fact tables, dimension tables: all optimized for the aggregations and joins that BI tools need.", lvl: 1 }, { head: true, text: "Schema-on-write" }, { text: "Data is modeled and validated before it's stored. The rigidity is a feature: it forces consistency and creates a governed single source of truth." }, { head: true, text: "The limitation: central-team bottleneck" }, { text: "The warehouse is owned by a central data team that owns all the data but understands none of the domains. Schema changes require central coordination. The analytical view is always hours or days behind the operational truth.", color: C.ink }, - { text: "As the organization grows, the central team processes more requests than it can understand — the queue becomes the bottleneck.", lvl: 1 }, + { text: "As the organization grows, the central team processes more requests than it can understand; the queue becomes the bottleneck.", lvl: 1 }, ], - notes: "The warehouse solved the truth problem that pipeline sprawl created. Instead of every consumer building its own understanding from raw pipeline output, everyone queries the same curated model. Schema-on-write is the defining characteristic — and it's a feature, not a bug: when two teams query 'total revenue last quarter,' they get the same number.\n\nThe limitation is organizational, not technical. The central data team — sometimes called the BI team, the analytics engineering team, the data platform team — becomes the bottleneck in direct proportion to the organization's growth. They own all the data but understand none of the domains. When the logistics domain needs a new dimension, the request goes to the central team, who must learn enough about logistics to model it correctly, prioritize it against every other domain's requests, and coordinate the schema change without breaking downstream consumers. The analytical view is always stale because the ingestion pipelines run on a schedule, and the central team's capacity to model new data is the real constraint.", + notes: "The warehouse solved the truth problem that pipeline sprawl created. Instead of every consumer building its own understanding from raw pipeline output, everyone queries the same curated model. Schema-on-write is the defining characteristic, and it's a feature, not a bug: when two teams query 'total revenue last quarter,' they get the same number.\n\nThe limitation is organizational, not technical. The central data team (sometimes called the BI team, the analytics engineering team, the data platform team) becomes the bottleneck in direct proportion to the organization's growth. They own all the data but understand none of the domains. When the logistics domain needs a new dimension, the request goes to the central team, who must learn enough about logistics to model it correctly, prioritize it against every other domain's requests, and coordinate the schema change without breaking downstream consumers. The analytical view is always stale because the ingestion pipelines run on a schedule, and the central team's capacity to model new data is the real constraint.", }); -diagramSlide({ eyebrow: "Data architectures", title: "Warehouse architecture — many sources, one team, one store", +diagramSlide({ eyebrow: "Data architectures", title: "Warehouse architecture: many sources, one team, one store", image: "01-data-warehouse-architecture", - caption: "Many sources, one warehouse, one team — the single source of truth, and the single point of contention.", - notes: "Walk the hub-and-spoke. Sources on the left — the same CRM, ERP, logs, events from before — all flowing into one centralized warehouse. The warehouse card in the center lists the defining characteristics: schema-on-write, star schemas, dimensional models, governed and structured. Consumers on the right: BI dashboards, reports, analysts — all querying the same governed store.\n\nNow look at the bottom: the Central Data Team card. This is the bottleneck. Every schema change, every new data source, every question funnels through this one team. The dashed arrows connecting them to the warehouse make the coupling visible. They own everything but understand none of the domains. The label says it plainly: 'the bottleneck a mesh exists to remove.' This team — not the technology — is what the mesh reorganizes.", + caption: "Many sources, one warehouse, one team: the single source of truth, and the single point of contention.", + notes: "Walk the hub-and-spoke. Sources on the left (the same CRM, ERP, logs, events from before), all flowing into one centralized warehouse. The warehouse card in the center lists the defining characteristics: schema-on-write, star schemas, dimensional models, governed and structured. Consumers on the right: BI dashboards, reports, analysts, all querying the same governed store.\n\nNow look at the bottom: the Central Data Team card. This is the bottleneck. Every schema change, every new data source, every question funnels through this one team. The dashed arrows connecting them to the warehouse make the coupling visible. They own everything but understand none of the domains. The label calls it out: 'the bottleneck a mesh exists to remove.' This team, not the technology, is what the mesh reorganizes.", }); /* ---- Data Lake ---- */ -contentSlide({ eyebrow: "Data architectures", title: "Data lakes — accept everything, sort it out later", +contentSlide({ eyebrow: "Data architectures", title: "Data lakes: accept everything, sort it out later", bullets: [ - { text: "A data lake accepts data in any format — structured, semi-structured, unstructured, streaming — and defers schema decisions to the point of consumption. This is schema-on-read." }, + { text: "A data lake accepts data in any format (structured, semi-structured, unstructured, streaming) and defers schema decisions to the point of consumption. This is schema-on-read." }, { head: true, text: "Zone-based organization" }, { text: "Raw zone: data exactly as it arrived. Curated zone: cleaned, validated, enriched. Refined zone: modeled and aggregated for specific consumption patterns.", lvl: 1 }, { head: true, text: "What it solved" }, { text: "The warehouse's format rigidity. A warehouse can't easily accommodate a Kafka topic of JSON events, a directory of Parquet files, or a bucket of images for computer vision. The lake can." }, { head: true, text: "The limitation: the swamp" }, - { text: "Without governance, the lake becomes a data swamp — undocumented, stale, duplicated datasets nobody trusts. The curation effort re-creates warehouse work, and a central team still owns the bucket.", color: C.ink }, + { text: "Without governance, the lake becomes a data swamp: undocumented, stale, duplicated datasets nobody trusts. The curation effort re-creates warehouse work, and a central team still owns the bucket.", color: C.ink }, ], - notes: "The lake solved the warehouse's most visible problem: format inflexibility. The warehouse can't handle Kafka events, Parquet files, images, sensor data. The lake can. At massive scale with cheap object storage, the lake makes it feasible to keep everything and decide later what's worth querying. That flexibility unlocked workloads — ML, unstructured analytics, exploratory data science — that warehouses were never built for.\n\nThe zone model is how well-run lakes organize the data: raw (as-is from sources), curated (cleaned, typed), refined (modeled for consumption). The progression from raw to refined represents increasing transformation and decreasing generality.\n\nThe limitation is governance. Without active curation, the lake becomes the 'data swamp' — undocumented datasets accumulate, nobody knows which version is authoritative, which datasets are stale, which duplicate each other. The curation effort needed to make a lake useful re-creates the warehouse's modeling work, just without the warehouse's enforcement mechanisms. And the organizational problem is unchanged: a central team still owns the lake.", + notes: "The lake solved the warehouse's most visible problem: format inflexibility. The warehouse can't handle Kafka events, Parquet files, images, sensor data. The lake can. At massive scale with cheap object storage, the lake makes it feasible to keep everything and decide later what's worth querying. That flexibility unlocked workloads (ML, unstructured analytics, exploratory data science) that warehouses were never built for.\n\nThe zone model is how well-run lakes organize the data: raw (as-is from sources), curated (cleaned, typed), refined (modeled for consumption). The progression from raw to refined represents increasing transformation and decreasing generality.\n\nThe limitation is governance. Without active curation, the lake becomes the 'data swamp': undocumented datasets accumulate, nobody knows which version is authoritative, which datasets are stale, which duplicate each other. The curation effort needed to make a lake useful re-creates the warehouse's modeling work, just without the warehouse's enforcement mechanisms. And the organizational problem is unchanged: a central team still owns the lake.", }); -diagramSlide({ eyebrow: "Data architectures", title: "Lake architecture — zones, flexibility, and the swamp risk", +diagramSlide({ eyebrow: "Data architectures", title: "Lake architecture: zones, flexibility, and the swamp risk", image: "01-data-lake-architecture", - caption: "Raw, curated, refined — flexible on the way in, but without governance the lake becomes a swamp, and the central-team bottleneck remains.", - notes: "Walk the architecture. Heterogeneous sources on the left — structured, semi-structured, unstructured, streaming — this is the format flexibility the lake provides. The center shows the lake with its three zones in graduated shading: the raw/landing zone (data as-is), the curated zone (cleaned and typed), the refined zone (modeled and ready for consumption). Consumers on the right: analytics, ML/AI, reporting, ad-hoc queries — the diverse workloads the lake enables.\n\nNow the red callout at the bottom: 'Without governance, the lake becomes a swamp.' Data lands in the raw zone and never moves. Nobody knows what's valid, what's stale, what duplicates what. Schema-on-read means consumers discover structure at query time — if they can find the data at all. And the final line: the organizational bottleneck is unchanged. A central team still owns the bucket.", + caption: "Raw, curated, refined: flexible on the way in, but without governance the lake becomes a swamp, and the central-team bottleneck remains.", + notes: "Walk the architecture. Heterogeneous sources on the left (structured, semi-structured, unstructured, streaming), this is the format flexibility the lake provides. The center shows the lake with its three zones in graduated shading: the raw/landing zone (data as-is), the curated zone (cleaned and typed), the refined zone (modeled and ready for consumption). Consumers on the right: analytics, ML/AI, reporting, ad-hoc queries, the diverse workloads the lake enables.\n\nNow the red callout at the bottom: 'Without governance, the lake becomes a swamp.' Data lands in the raw zone and never moves. Nobody knows what's valid, what's stale, what duplicates what. Schema-on-read means consumers discover structure at query time, if they can find the data at all. And the final line: the organizational bottleneck is unchanged. A central team still owns the bucket.", }); /* ---- How Data Mesh Differs ---- */ -contentSlide({ eyebrow: "Data architectures", title: "Data mesh — a different kind of answer", +contentSlide({ eyebrow: "Data architectures", title: "Data mesh: a different kind of answer", bullets: [ - { text: "All three patterns — pipelines, warehouses, lakes — centralize data and hand ownership to a single team. The technology improves; the organizational shape stays the same: one team at the center." }, + { text: "All three patterns (pipelines, warehouses, lakes) centralize data and hand ownership to a single team. The technology improves; the organizational shape stays the same: one team at the center." }, { head: true, text: "The mesh changes the organizational shape" }, - { text: "Instead of building a better center, decentralize ownership to the domain teams that produce the data. Each domain owns its data as a product — discoverable, addressable, trustworthy, self-describing." }, + { text: "Instead of building a better center, decentralize ownership to the domain teams that produce the data. Each domain owns its data as a product: discoverable, addressable, trustworthy, self-describing." }, { text: "A shared self-serve platform provides the infrastructure. Federated computational governance keeps the independently-owned products interoperable.", lvl: 1 }, { head: true, text: "Not a replacement" }, { text: "A domain may still use a warehouse or a lake internally. The reorganization is about who owns the data, not which storage technology to use.", color: C.ink }, - { text: "The mesh earns its complexity in organizations with many domains, many consumers, and the maturity to operate federated ownership.", lvl: 1 }, + { text: "The mesh's added complexity pays off in organizations with many domains, many consumers, and the maturity to operate federated ownership.", lvl: 1 }, ], - notes: "This is the pivot slide — from what came before to what the mesh proposes. The key insight: all three prior patterns centralize data and hand ownership to a single team. The technology improves generation by generation — from bespoke pipelines, to a governed warehouse, to a flexible lake — but the organizational shape stays the same. One team at the center, receiving requests from every domain, owning data it did not produce, modeling concepts it does not deeply understand, scaling linearly while demands grow combinatorially.\n\nThe mesh changes the axis: instead of a better center, decentralize ownership. Each domain owns its data as a product. A shared platform provides streaming, storage, registries, observability — so domains don't each build their own. Federated governance keeps the products interoperable — through platform-enforced standards, not review boards.\n\nCritical clarification: the mesh is not a replacement for warehouses or lakes. A domain may use a warehouse internally. The reorganization is about who owns the data — not which database to pick. An important caveat: the mesh earns its complexity in organizations with many domains, many consumers, and the maturity to operate federated ownership. For smaller teams, a well-run warehouse may be exactly right.", + notes: "This is the pivot slide, from what came before to what the mesh proposes. The key insight: all three prior patterns centralize data and hand ownership to a single team. The technology improves generation by generation, from bespoke pipelines, to a governed warehouse, to a flexible lake, but the organizational shape stays the same. One team at the center, receiving requests from every domain, owning data it did not produce, modeling concepts it does not deeply understand, scaling linearly while demands grow combinatorially.\n\nThe mesh changes the axis: instead of a better center, decentralize ownership. Each domain owns its data as a product. A shared platform provides streaming, storage, registries, observability, so domains don't each build their own. Federated governance keeps the products interoperable, through platform-enforced standards, not review boards.\n\nCritical clarification: the mesh is not a replacement for warehouses or lakes. A domain may use a warehouse internally. The reorganization is about who owns the data, not which database to pick. An important caveat: the mesh's added complexity pays off in organizations with many domains, many consumers, and the maturity to operate federated ownership. For smaller teams, a well-run warehouse may be exactly right.", }); -diagramSlide({ eyebrow: "Data architectures", title: "The mesh — decentralized, domain-owned data products", +diagramSlide({ eyebrow: "Data architectures", title: "The mesh: decentralized, domain-owned data products", image: "01-data-mesh-decentralized", - caption: "Each domain owns its data as a product, on a shared platform under federated governance — the organizational answer.", - notes: "Walk the diagram. Top band: federated computational governance — standards enforced by the platform, automatically. The 2x2 grid shows four domain data products — Orders, Inventory, Payment, Shipping — each a red card because this is the target pattern. Each card shows: the domain name, what it owns, 'owns its data,' three API port pills (REST, gRPC, Events), and 'team-owned' at the bottom. The bidirectional red arrows between domains show inter-domain communication — products talking to products.\n\nBottom band: the self-serve data platform — shared infrastructure every domain consumes: streaming, storage, mesh, observability. This is the organizational shape that's different: no central data team owning everything. Each domain owns its own data; the platform and governance make them interoperable. The contrast with the warehouse slide should be visceral: there's no 'Central Data Team' card here.", + caption: "Each domain owns its data as a product, on a shared platform under federated governance: the organizational answer.", + notes: "Walk the diagram. Top band: federated computational governance, with standards enforced by the platform automatically. The 2x2 grid shows four domain data products (Orders, Inventory, Payment, Shipping), each a red card because this is the target pattern. Each card shows: the domain name, what it owns, 'owns its data,' three API port pills (REST, gRPC, Events), and 'team-owned' at the bottom. The bidirectional red arrows between domains show inter-domain communication, products talking to products.\n\nBottom band: the self-serve data platform, shared infrastructure every domain consumes: streaming, storage, mesh, observability. This is the organizational shape that's different: no central data team owning everything. Each domain owns its own data; the platform and governance make them interoperable. The contrast with the warehouse slide should be visceral: there's no 'Central Data Team' card here.", }); /* ---- Evolution ---- */ -diagramSlide({ eyebrow: "Data architectures", title: "The evolution — from pipelines to mesh", +diagramSlide({ eyebrow: "Data architectures", title: "The evolution: from pipelines to mesh", image: "01-architecture-evolution", caption: "Each pattern solved the problem the previous one left; the mesh solves the organizational scaling problem they all share.", - notes: "The synthesis slide. Walk left to right through the four columns. Pipelines (blue): solved the movement problem, but the limitation is pipeline sprawl and no ownership model. Warehouses (orange): solved the truth problem — a single governed view — but the limitation is the central team bottleneck. Lakes (green): solved the flexibility problem — any format, any workload — but the limitation is swamp risk and the same organizational bottleneck. Mesh (red): solves the organizational scaling problem — decentralized ownership — but requires organizational maturity.\n\nThe bottom band makes the organizational dimension explicit: point-to-point → centralized team → centralized team (bigger) → decentralized ownership. The progression isn't 'each is better than the last' — it's 'each solves a different problem.' A pipeline is still right when movement is the problem. A warehouse is still right when truth is the problem. A lake is still right when flexibility is the problem. The mesh is right when the bottleneck is organizational.", + notes: "The synthesis slide. Walk left to right through the four columns. Pipelines (blue): solved the movement problem, but the limitation is pipeline sprawl and no ownership model. Warehouses (orange): solved the truth problem (a single governed view), but the limitation is the central team bottleneck. Lakes (green): solved the flexibility problem (any format, any workload), but the limitation is swamp risk and the same organizational bottleneck. Mesh (red): solves the organizational scaling problem (decentralized ownership), but requires organizational maturity.\n\nThe bottom band makes the organizational dimension explicit: point-to-point, then centralized team, then centralized team (bigger), then decentralized ownership. The progression isn't 'each is better than the last'; it's 'each solves a different problem.' A pipeline is still right when movement is the problem. A warehouse is still right when truth is the problem. A lake is still right when flexibility is the problem. The mesh is right when the bottleneck is organizational.", }); /* ---- When Each Fits ---- */ contentSlide({ eyebrow: "Data architectures", title: "When each pattern fits", bullets: [ - { text: "The choice depends on scale, data landscape, and where the bottleneck sits — not on which pattern is newest." }, + { text: "The choice depends on scale, data landscape, and where the bottleneck sits, not on which pattern is newest." }, { head: true, text: "Pipelines" }, { text: "Small number of well-understood integrations, stable data flows, no pressing need for centralized cross-domain analytics. The sprawl hasn't started.", lvl: 1 }, { head: true, text: "Warehouse" }, - { text: "The organization needs a governed analytical view — BI, reporting, structured decisions — and a central team has the capacity to curate it. The bottleneck is the absence of a single source of truth.", lvl: 1 }, + { text: "The organization needs a governed analytical view (BI, reporting, structured decisions) and a central team has the capacity to curate it. The bottleneck is the absence of a single source of truth.", lvl: 1 }, { head: true, text: "Lake" }, { text: "Heterogeneous data, ML/AI workloads, scale that exceeds what a warehouse handles. The bottleneck is format rigidity, not organizational ownership.", lvl: 1 }, { head: true, text: "Mesh" }, - { text: "Many domains, many consumers, and the central team has become the constraint. The bottleneck is organizational — who owns what — not technical.", color: C.ink }, + { text: "Many domains, many consumers, and the central team has become the constraint. The bottleneck is organizational (who owns what), not technical.", color: C.ink }, ], - notes: "This is the fitness slide. Not every organization needs a mesh. The question is whether the bottleneck is technical (better tools solve it) or organizational (who owns what). Walk each:\n\n• Pipelines fit when the plumbing is simple enough that sprawl hasn't started. A handful of integrations, stable flows, known sources and destinations. Don't add complexity before the problem demands it.\n\n• Warehouses fit when the organization needs a single source of truth for BI and reporting, and a central team has the capacity to curate the data. If the bottleneck is 'we don't know the truth,' the warehouse is the answer.\n\n• Lakes fit when the data is heterogeneous — logs, events, images, ML training sets — and the scale or workload patterns exceed what a warehouse handles. The bottleneck is format rigidity, not organizational ownership.\n\n• Mesh fits when the organization has outgrown centralized ownership. Many domains, many consumers, and the central team is the constraint. The bottleneck is organizational — not which tool to buy. The organization also needs the maturity to operate federated ownership: domain teams that can treat data as a product, a platform team that can provide self-serve infrastructure, and governance that works through automation.\n\nTelling people when NOT to use a mesh builds credibility for when you say they should.", + notes: "This is the fitness slide. Not every organization needs a mesh. The question is whether the bottleneck is technical (better tools solve it) or organizational (who owns what). Walk each:\n\n• Pipelines fit when the plumbing is simple enough that sprawl hasn't started. A handful of integrations, stable flows, known sources and destinations. Don't add complexity before the problem demands it.\n\n• Warehouses fit when the organization needs a single source of truth for BI and reporting, and a central team has the capacity to curate the data. If the bottleneck is 'we don't know the truth,' the warehouse is the answer.\n\n• Lakes fit when the data is heterogeneous (logs, events, images, ML training sets) and the scale or workload patterns exceed what a warehouse handles. The bottleneck is format rigidity, not organizational ownership.\n\n• Mesh fits when the organization has outgrown centralized ownership. Many domains, many consumers, and the central team is the constraint. The bottleneck is organizational, not which tool to buy. The organization also needs the maturity to operate federated ownership: domain teams that can treat data as a product, a platform team that can provide self-serve infrastructure, and governance that works through automation.\n\nNaming when not to use a mesh makes the recommendation to use one more credible.", }); /* ============================ CLOSING ============================ */ @@ -233,13 +233,13 @@ contentSlide({ eyebrow: "Data architectures", title: "When each pattern fits", const s = pres.addSlide(); s.addImage({ path: ILLUS, x: 0, y: 0, w: PW, h: PH, sizing: { type: "cover", w: PW, h: PH } }); const rx = PW * 0.42, rw = PW - rx - 0.7; - s.addText("The mesh is the network of products —", { x: rx, y: 2.5, w: rw, h: 0.9, fontSize: 28, color: "FFFFFF", fontFace: F.head, bold: true, valign: "top", margin: 0 }); + s.addText("The mesh is the network of products,", { x: rx, y: 2.5, w: rw, h: 0.9, fontSize: 28, color: "FFFFFF", fontFace: F.head, bold: true, valign: "top", margin: 0 }); s.addText("plus the platform and standards\nthat let them interoperate.", { x: rx, y: 3.35, w: rw, h: 1.2, fontSize: 28, color: "FFD9D9", fontFace: F.head, bold: true, valign: "top", margin: 0 }); s.addText("Each pattern solved a real problem; the mesh solves the organizational one they all left behind.", { x: rx, y: 4.85, w: rw, h: 0.6, fontSize: 15, color: "FFFFFF", fontFace: F.body, italic: true, valign: "top", margin: 0 }); const lw = 1.25, lh = lw / LOGO_AR; s.addImage({ path: LOGO_LIGHT, x: PW - 0.6 - lw, y: PH - 0.3 - lh, w: lw, h: lh }); PAGENO += 1; - s.addNotes("Closing slide. The one-sentence definition: a mesh is the network of products plus the platform and standards that let them interoperate. And the grounding from today: each pattern we walked through solved a real problem — the mesh solves the organizational one they all left behind. Open for questions."); + s.addNotes("Closing slide. The one-sentence definition: a mesh is the network of products plus the platform and standards that let them interoperate. And the grounding from today: each pattern we walked through solved a real problem; the mesh solves the organizational one they all left behind. Open for questions."); })(); /* ============================ WRITE ============================ */ diff --git a/presentation/data-mesh-openshift/Data_Mesh_on_OpenShift.pptx b/presentation/data-mesh-openshift/Data_Mesh_on_OpenShift.pptx index 3a7c8d9000a6c3bf306fcfc20122aa8c8eca8201..16b1c734909f2a5083979428bf0249af3114fafa 100644 GIT binary patch delta 23759 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 diff --git a/presentation/data-mesh-openshift/build-deck.js b/presentation/data-mesh-openshift/build-deck.js index c268016..66e22ed 100644 --- a/presentation/data-mesh-openshift/build-deck.js +++ b/presentation/data-mesh-openshift/build-deck.js @@ -1,4 +1,4 @@ -// Build the Data Mesh on OpenShift implementation deck — v2. +// Build the Data Mesh on OpenShift implementation deck, v2. // Diagrams-forward (YAML replaced by diagrams where it doesn't teach); Python API // examples kept; per-section value-closer diagrams; speaker notes on every slide. const L = require("./deck-lib.js"); @@ -9,10 +9,10 @@ const code = (s) => s.replace(/\t/g, " "); titleSlide({ eyebrow: "Data Mesh · Implementation", title: "Data Mesh on OpenShift", - subtitle: "From the four principles to a running platform — domains owning data as products, on the platform and governance that let them interoperate.", + subtitle: "From the four principles to a running platform: domains owning data as products, on the platform and governance that let them interoperate.", tagline: "A reference implementation", breadcrumb: "OpenShift · AMQ Streams · CloudNativePG · Service Mesh · KEDA · OpenMetadata", - notes: "Welcome. This is the implementation companion to the Data Mesh 101 deck. Where 101 made the conceptual case, today we build it — on OpenShift, principle by principle, with the real pieces and the code. Set expectations: a 1.5–3 hour walk through a reference architecture. For each principle I'll show the value it delivers and the OpenShift pieces that deliver it. Today is about the value of the whole — the principles and the pieces that realize them.", + notes: "Welcome. This is the implementation companion to the Data Mesh 101 deck. Where 101 made the conceptual case, today we build it on OpenShift, principle by principle, with the real pieces and the code. Set expectations: a 1.5-3 hour walk through a reference architecture. For each principle I'll show the value it delivers and the OpenShift pieces that deliver it. Today is about the value of the whole: the principles and the pieces that realize them.", }); /* ============================ AGENDA ============================ */ @@ -28,7 +28,7 @@ L.agendaSlide({ { text: "05 · The whole picture" }, { text: "06 · Anti-patterns" }, ], - notes: "The agenda. Seven sections. The spine is the four data-mesh principles (01–04), bookended by an orientation section (00), a synthesis (05), and the anti-patterns (06) that close out the talk. Point out the shape: each principle section follows the same rhythm — principle, pieces, code, value. Roughly 1.5–3 hours depending on how deep you go on the code and diagrams.", + notes: "The agenda. Seven sections. The spine is the four data-mesh principles (01-04), bookended by an orientation section (00), a synthesis (05), and the anti-patterns (06) that close out the talk. Point out the shape: each principle section follows the same rhythm (principle, pieces, code, value). Roughly 1.5-3 hours depending on how deep you go on the code and diagrams.", }); (() => { @@ -36,47 +36,47 @@ L.agendaSlide({ s.background = { color: C.white }; L.head(s, "Where the 101 deck ended", "From the idea to the running platform"); L.addBullets(s, [ - { text: "The Data Mesh 101 deck made the case: centralized data platforms stall, and the answer is to decentralize ownership — domains own their data as products on a shared, self-serve platform under federated, automated governance.", }, + { text: "The Data Mesh 101 deck made the case: centralized data platforms stall, and the answer is to decentralize ownership, with domains owning their data as products on a shared, self-serve platform under federated, automated governance.", }, { text: "That deck ended by pointing at implementation. This deck is the implementation.", color: C.ink }, { head: true, text: "What this deck does" }, - { text: "Takes each of the four principles and shows the concrete OpenShift pieces that realize it — with the architecture and the code, not just the diagram." }, - { text: "Frames every piece by the value it delivers: why it earns its place in a production data mesh." }, - { text: "Stays focused on value and the conceptual shape — the through-line is the four principles and how they're realized." }, + { text: "Takes each of the four principles and shows the concrete OpenShift pieces that realize it, with the architecture and the code, not just the diagram." }, + { text: "Frames every piece by the value it delivers and why it belongs in a production data mesh." }, + { text: "Stays focused on value and the conceptual shape: the through-line is the four principles and how they're realized." }, ], { x: 0.7, y: 1.95, w: PW - 1.4 }); L.footer(s); - s.addNotes("Bridge from 101. If your audience saw the 101 deck, this is a quick recap; if not, it's the whole premise in four sentences. The key move: we are not going to argue for data mesh again — we're going to build one. Emphasize the last bullet: the talk stays on value and the conceptual shape, closing with the anti-patterns that most often derail real efforts."); + s.addNotes("Bridge from 101. If your audience saw the 101 deck, this is a quick recap; if not, it's the whole premise in four sentences. The key move: this deck does not argue for data mesh again, it builds one. Emphasize the last bullet: the talk stays on value and the conceptual shape, closing with the anti-patterns that most often derail real efforts."); })(); /* ====================== 00 · FROM PRINCIPLES TO PLATFORM ====================== */ divider({ num: "00", title: "From principles to platform", sub: "The four principles, the reference architecture, and how to read what follows.", - notes: "Section 0 is orientation: the four principles in one diagram, the reference architecture we build toward, the domain we model, and the rhythm each section follows. Keep it brisk — the map, not the territory." }); + notes: "Orientation: the four principles in one diagram, the reference architecture we build toward, the domain we model, and the rhythm each section follows. Keep it brisk: the map, not the territory." }); -diagramSlide({ eyebrow: "The four principles", title: "Four principles — and how they map in practice", +diagramSlide({ eyebrow: "The four principles", title: "Four principles, and how they map in practice", image: "four-principles", caption: "The tools are expressions of the pattern, not the pattern itself. Each principle below maps to concrete platform pieces.", - notes: "Straight from the 101 deck, deliberately, so the decks connect. Walk left to right: domain ownership, data as a product, self-serve platform, federated governance. The bottom row of each card already names the Kubernetes/OpenShift primitive — that's the whole structure of today's talk in one slide. The four interlock: implement one without the others and you get a distributed mess, not a mesh." }); + notes: "Straight from the 101 deck, so the decks connect. Walk left to right: domain ownership, data as a product, self-serve platform, federated governance. The bottom row of each card already names the Kubernetes/OpenShift primitive, which is the whole structure of today's talk in one slide. The four interlock: implement one without the others and you get a distributed mess, not a mesh." }); (() => { const s = pres.addSlide(); s.background = { color: C.white }; - L.head(s, "Why a platform — and why OpenShift", "The value thesis"); + L.head(s, "Why a platform, and why OpenShift", "The value thesis"); L.addBullets(s, [ - { text: "A data mesh is a socio-technical pattern. The technical half needs a platform that makes domain ownership, product packaging, self-serve infrastructure, and computational governance the path of least resistance — not a fight.", }, + { text: "A data mesh is a socio-technical pattern. The technical half needs a platform that makes domain ownership, product packaging, self-serve infrastructure, and computational governance the path of least resistance, not a fight.", }, { head: true, text: "OpenShift gives each principle a home" }, { text: "Domain ownership → Projects, project-scoped RBAC, SCCs, quotas.", lvl: 1 }, { text: "Data as a product → Deployments, Services, Routes, CRDs for domain types.", lvl: 1 }, { text: "Self-serve platform → OperatorHub + OLM, GitOps, the integrated registry, AMQ Streams, Service Mesh.", lvl: 1 }, { text: "Federated governance → Service Mesh mTLS, admission policy, schema enforcement, built-in observability.", lvl: 1 }, - { text: "The rest of this deck is that mapping, principle by principle, with the code that makes it real.", color: C.ink }, + { text: "What follows is that mapping, principle by principle, with the code that makes it real.", color: C.ink }, ], { x: 0.7, y: 1.95, w: PW - 1.4 }); L.footer(s); - s.addNotes("The thesis slide. A data mesh fails for organizational reasons far more than technical ones, so the platform's job is to make the right thing the easy thing. OpenShift earns its place because each principle has a home in primitives the audience already knows. Don't dwell on each bullet — they're a preview; we spend a section on each. Payoff line: this whole deck is that mapping.\n\nDefinitions:\n• OperatorHub — OpenShift's catalog of installable operators; the platform team curates what's available.\n• OLM (Operator Lifecycle Manager) — installs, updates, and manages the lifecycle of operators on the cluster.\n• GitOps — desired cluster state lives in Git and is continuously reconciled onto the cluster (OpenShift GitOps is based on Argo CD).\n• RBAC (Role-Based Access Control) — Kubernetes' permission model; Roles grant verbs on resources, RoleBindings bind them to users/groups, enforced by the API server.\n• SCC (Security Context Constraint) — OpenShift policy controlling what a pod may do on the node (run as root, mount host paths, privileged, etc.).\n• mTLS (mutual TLS) — both client and server present certificates; the mesh establishes it automatically between sidecars, with no app code.\n• Admission control — the API-server gate that validates or rejects resources as they're created; where computational governance runs.\n• Service Mesh (OpenShift Service Mesh / Istio) — a layer of sidecar proxies that secures, routes, and observes service-to-service traffic.\n• AMQ Streams — Red Hat's supported distribution of Strimzi: Apache Kafka run on Kubernetes via operators."); + s.addNotes("The thesis slide. A data mesh fails for organizational reasons far more than technical ones, so the platform's job is to make the right thing the easy thing. OpenShift fits because each principle has a home in primitives the audience already knows. Don't dwell on each bullet; they're a preview, and we spend a section on each. Payoff line: this whole deck is that mapping.\n\nDefinitions:\n• OperatorHub: OpenShift's catalog of installable operators; the platform team curates what's available.\n• OLM (Operator Lifecycle Manager): installs, updates, and manages the lifecycle of operators on the cluster.\n• GitOps: desired cluster state lives in Git and is continuously reconciled onto the cluster (OpenShift GitOps is based on Argo CD).\n• RBAC (Role-Based Access Control): Kubernetes' permission model; Roles grant verbs on resources, RoleBindings bind them to users/groups, enforced by the API server.\n• SCC (Security Context Constraint): OpenShift policy controlling what a pod may do on the node (run as root, mount host paths, privileged, etc.).\n• mTLS (mutual TLS): both client and server present certificates; the mesh establishes it automatically between sidecars, with no app code.\n• Admission control: the API-server gate that validates or rejects resources as they're created; where computational governance runs.\n• Service Mesh (OpenShift Service Mesh / Istio): a layer of sidecar proxies that secures, routes, and observes service-to-service traffic.\n• AMQ Streams: Red Hat's supported distribution of Strimzi: Apache Kafka run on Kubernetes via operators."); })(); diagramSlide({ eyebrow: "Reference architecture", title: "The data mesh, at a glance", image: "reference-data-mesh-architecture", caption: "The centerpiece: domain data products, the platform planes beneath them, and the governance and observability that span them. We assemble this piece by piece.", - notes: "The centerpiece — we return to it assembled at the end. Now, just orient: top is external clients, the middle band is the meshed domain services, the bottom is the self-serve platform, and observability spans the whole. Don't explain every box yet — promise that by the end every piece here will have been built and they'll recognize all of it." }); + notes: "The centerpiece; we return to it assembled at the end. Now, just orient: top is external clients, the middle band is the meshed domain services, the bottom is the self-serve platform, and observability spans the whole. Don't explain every box yet; promise that by the end every piece here will have been built and they'll recognize all of it." }); (() => { const s = pres.addSlide(); @@ -84,72 +84,72 @@ diagramSlide({ eyebrow: "Reference architecture", title: "The data mesh, at a gl L.head(s, "The domain we model", "Five data products and a read gateway"); L.addBullets(s, [ { text: "Order-placement-through-fulfillment, modeled small enough to stay legible and complete enough to exercise every principle.", }, - { text: "order · inventory · payment · shipping · notification — five domain services, each a data product owning its schema, its contract, and its lifecycle.", lvl: 1 }, - { text: "A GraphQL gateway composes reads across them — a read-layer convenience, not a domain data product.", lvl: 1 }, + { text: "order · inventory · payment · shipping · notification: five domain services, each a data product owning its schema, its contract, and its lifecycle.", lvl: 1 }, + { text: "A GraphQL gateway composes reads across them, a read-layer convenience, not a domain data product.", lvl: 1 }, { head: true, text: "Production framing" }, - { text: "On OpenShift this is five domain Projects plus a platform Project — not one namespace on a laptop. Each domain team owns its Project end to end.", }, + { text: "On OpenShift this is five domain Projects plus a platform Project, not one namespace on a laptop. Each domain team owns its Project end to end.", }, { text: "The protocols vary by fitness: REST at the edge, gRPC between services, GraphQL for composition, events for everything asynchronous.", }, ], { x: 0.7, y: 1.95, w: PW - 1.4 }); L.footer(s); - s.addNotes("Introduce the worked example. Order fulfillment is deliberately mundane so the architecture stays the focus. Five domain products plus a gateway = six deployable things. Stress the distinction: five DOMAIN products (each owns data) plus one gateway (composes reads, owns nothing). The production-framing line matters: five Projects across teams, not a toy.\n\nDefinitions:\n• Project — OpenShift's unit of tenancy: a Kubernetes namespace with added governance (annotations, RBAC, lifecycle). One per domain here.\n• REST — resource-oriented HTTP APIs; used at the edge where external clients cross the trust boundary.\n• gRPC — a fast, strongly-typed RPC protocol over HTTP/2; used for synchronous service-to-service calls.\n• GraphQL — a query language that lets a client request exactly the shape it needs; the gateway uses it to compose reads across products."); + s.addNotes("Introduce the worked example. Order fulfillment is mundane so the architecture stays the focus. Five domain products plus a gateway equals six deployable things. Stress the distinction: five domain products (each owns data) plus one gateway (composes reads, owns nothing). The production-framing line matters: five Projects across teams, a real deployment shape rather than a single laptop namespace.\n\nDefinitions:\n• Project: OpenShift's unit of tenancy: a Kubernetes namespace with added governance (annotations, RBAC, lifecycle). One per domain here.\n• REST: resource-oriented HTTP APIs; used at the edge where external clients cross the trust boundary.\n• gRPC: a fast, strongly-typed RPC protocol over HTTP/2; used for synchronous service-to-service calls.\n• GraphQL: a query language that lets a client request exactly the shape it needs; the gateway uses it to compose reads across products."); })(); contentSlide({ eyebrow: "How to read this deck", title: "Principle → pieces → code → value", bullets: [ { text: "Each of the next four sections takes one principle and follows the same rhythm:", }, - { text: "The principle, recapped in a sentence — the 101 deck has the full argument.", lvl: 1 }, + { text: "The principle, recapped in a sentence; the 101 deck has the full argument.", lvl: 1 }, { text: "The pieces that realize it on OpenShift, shown as architecture diagrams.", lvl: 1 }, - { text: "The code where code is the lesson — Python for the APIs, the few manifests worth seeing.", lvl: 1 }, + { text: "The code where code is the lesson: Python for the APIs, the few manifests that matter.", lvl: 1 }, { text: "A closing value picture: the principle's payoff and the pieces that deliver it.", lvl: 1 }, { head: true, text: "A note on scope" }, - { text: "The main narrative stays on value and leans on diagrams over YAML. We close with the anti-patterns — the conceptual and organizational ways data-mesh efforts go wrong." }, + { text: "The main narrative stays on value and leans on diagrams over YAML. We close with the anti-patterns: the conceptual and organizational ways data-mesh efforts go wrong." }, ], - notes: "Set the per-section rhythm: principle, pieces as diagrams, code only where it teaches, value-closer picture. Call out explicitly that we favor diagrams over YAML — most people don't want to read manifests off a slide, and the architecture is clearer as a picture. The Python API code we DO show, because seeing the implementation is valuable. The deck closes with the anti-patterns section." }); + notes: "Set the per-section rhythm: principle, pieces as diagrams, code only where it teaches, value-closer picture. Call out explicitly that we favor diagrams over YAML: most people don't want to read manifests off a slide, and the architecture is clearer as a picture. The Python API code we do show, because seeing the implementation is valuable. The deck closes with the anti-patterns section." }); /* ====================== 01 · DOMAIN OWNERSHIP ====================== */ -divider({ num: "01", title: "Domain ownership", sub: "Each domain owns its data end to end — and the platform makes that boundary real.", - notes: "Principle 1 of 4. Throughline: ownership is only real if the boundary is enforced by the platform, not just agreed in a meeting. We see the bottleneck it removes, the monolith-to-mesh shift, then the OpenShift pieces — Projects, RBAC, quotas, a database per domain — and close with the value picture." }); +divider({ num: "01", title: "Domain ownership", sub: "Each domain owns its data end to end, and the platform makes that boundary real.", + notes: "Principle 1 of 4. Throughline: ownership is only real if the boundary is enforced by the platform, not just agreed in a meeting. We see the bottleneck it removes, the monolith-to-mesh shift, then the OpenShift pieces (Projects, RBAC, quotas, a database per domain), and close with the value picture." }); contentSlide({ eyebrow: "Domain ownership", title: "The principle, in one slide", bullets: [ { text: "Data is owned, end to end, by the domain team that produces it. There is no central team that owns the warehouse.", }, - { text: "The domain owns its schema, its data's lifecycle, and its evolution — and is accountable for its quality.", lvl: 1 }, + { text: "The domain owns its schema, its data's lifecycle, and its evolution, and is accountable for its quality.", lvl: 1 }, { head: true, text: "Why it matters" }, { text: "The central data team that owns all the data but understands none of the domains is the bottleneck a mesh exists to remove.", }, - { text: "Ownership without an enforced boundary is just a wish. The platform's job is to make the boundary structural — so ownership is the default, not a convention people remember to honor.", }, + { text: "Ownership without an enforced boundary is just a wish. The platform's job is to make the boundary structural, so ownership is the default, not a convention people remember to honor.", }, ], - notes: "One-slide principle recap — don't re-teach the 101 argument, just land it. The line to hammer: 'ownership without an enforced boundary is just a wish.' That's the bridge to platform primitives. If someone owns data but anyone can reach in and change it, they don't really own it." }); + notes: "One-slide principle recap; don't re-teach the 101 argument, just land it. The line to hammer: 'ownership without an enforced boundary is just a wish.' That's the bridge to platform primitives. If someone owns data but anyone can reach in and change it, they don't really own it." }); diagramSlide({ eyebrow: "Domain ownership", title: "The bottleneck it removes", image: "central-team-bottleneck", caption: "When every domain's data flows through one central team, that team becomes the constraint on the entire organization's data velocity.", - notes: "The problem diagram. Every domain routing through one central data team makes that team the constraint, no matter how good they are — they own all the data but understand none of the domains. Ask: how long does a schema change take in your org today? That lead time is the bottleneck this principle removes." }); + notes: "The problem diagram. Every domain routing through one central data team makes that team the constraint, no matter how good they are; they own all the data but understand none of the domains. Ask: how long does a schema change take in your org today? That lead time is the bottleneck this principle removes." }); diagramSlide({ eyebrow: "Domain ownership", title: "From monolith to a mesh of products", image: "monolith-to-mesh", caption: "The same refactor microservices applied to applications, applied to data: bounded contexts, each owned by the domain team that knows it best.", - notes: "The solution shape, by analogy. Everyone lived the monolith-to-microservices refactor for applications. Data mesh is that same move for data: break the monolithic data platform into bounded data products, each owned by the domain team that knows it. The analogy does a lot of work — lean on it." }); + notes: "The solution shape, by analogy. Everyone lived the monolith-to-microservices refactor for applications. Data mesh is that same move for data: break the monolithic data platform into bounded data products, each owned by the domain team that knows it. The analogy does a lot of work; lean on it." }); (() => { const s = pres.addSlide(); s.background = { color: C.white }; L.head(s, "Domain ownership", "Ownership = a Project per domain"); L.addBullets(s, [ - { text: "On OpenShift, the unit of tenancy is the Project (a namespace with added governance). Each domain gets its own Project — its own identities, permissions, and resource budget.", }, + { text: "On OpenShift, the unit of tenancy is the Project (a namespace with added governance). Each domain gets its own Project: its own identities, permissions, and resource budget.", }, { text: "A domain team operates inside its Project the way a microservice team operates inside its repository: it owns what's there, and nothing outside reaches in to mutate it.", }, { head: true, text: "What the Project carries" }, - { text: "ServiceAccounts — the domain's workload identities.", lvl: 1 }, - { text: "Project-scoped RBAC — who in the domain can do what, enforced by the API server.", lvl: 1 }, - { text: "SCCs — what the domain's pods are allowed to do on the node.", lvl: 1 }, - { text: "ResourceQuota and LimitRange — the budget the domain lives within.", lvl: 1 }, + { text: "ServiceAccounts: the domain's workload identities.", lvl: 1 }, + { text: "Project-scoped RBAC: who in the domain can do what, enforced by the API server.", lvl: 1 }, + { text: "SCCs: what the domain's pods are allowed to do on the node.", lvl: 1 }, + { text: "ResourceQuota and LimitRange: the budget the domain lives within.", lvl: 1 }, ], { x: 0.7, y: 1.95, w: PW - 1.4 }); L.footer(s); - s.addNotes("The OpenShift mapping. The Project IS the ownership boundary. The repository analogy lands: a domain team owns its Project like a microservice team owns its repo. Walk the four things a Project carries — identities, permissions, security context, budget — each enforces ownership in a different dimension. Next slides show the actual code.\n\nDefinitions:\n• Project — OpenShift's unit of tenancy: a Kubernetes namespace with added governance (annotations, RBAC, lifecycle). One per domain here.\n• RBAC (Role-Based Access Control) — Kubernetes' permission model; Roles grant verbs on resources, RoleBindings bind them to users/groups, enforced by the API server.\n• SCC (Security Context Constraint) — OpenShift policy controlling what a pod may do on the node (run as root, mount host paths, privileged, etc.).\n• ResourceQuota — a namespace-level cap on aggregate resource use (CPU, memory, object counts) so one domain can't starve others.\n• LimitRange — per-container default and max resource limits applied within a namespace when a pod omits its own.\n• ServiceAccount — a workload identity inside a namespace; pods run as one, and mesh mTLS and authorization policies key off it."); + s.addNotes("The OpenShift mapping. The Project is the ownership boundary. The repository analogy lands: a domain team owns its Project like a microservice team owns its repo. Walk the four things a Project carries (identities, permissions, security context, budget); each enforces ownership in a different dimension. Next slides show the actual code.\n\nDefinitions:\n• Project: OpenShift's unit of tenancy: a Kubernetes namespace with added governance (annotations, RBAC, lifecycle). One per domain here.\n• RBAC (Role-Based Access Control): Kubernetes' permission model; Roles grant verbs on resources, RoleBindings bind them to users/groups, enforced by the API server.\n• SCC (Security Context Constraint): OpenShift policy controlling what a pod may do on the node (run as root, mount host paths, privileged, etc.).\n• ResourceQuota: a namespace-level cap on aggregate resource use (CPU, memory, object counts) so one domain can't starve others.\n• LimitRange: per-container default and max resource limits applied within a namespace when a pod omits its own.\n• ServiceAccount: a workload identity inside a namespace; pods run as one, and mesh mTLS and authorization policies key off it."); })(); codeSlide({ eyebrow: "Domain ownership", title: "The domain's Project and identity", lang: "YAML · OpenShift", - note: "One Project per domain — the tenancy boundary. The istio-injection label opts this domain into the mesh explicitly (selective, not cluster-wide). The ServiceAccount is the domain's workload identity, which mesh mTLS and authorization policy key off.", + note: "One Project per domain: the tenancy boundary. The istio-injection label opts this domain into the mesh explicitly (selective, not cluster-wide). The ServiceAccount is the domain's workload identity, which mesh mTLS and authorization policy key off.", code: code(`apiVersion: project.openshift.io/v1 kind: Project metadata: @@ -163,11 +163,11 @@ kind: ServiceAccount metadata: name: order-service namespace: order`), - notes: "One of the few manifests worth showing — the labels carry meaning. Point at istio-injection: enabled — we OPT this domain INTO the mesh explicitly. That's a deliberate choice justified in section 04: selective meshing, not cluster-wide. The ServiceAccount is the workload identity that mesh mTLS and authorization policies key off — remember it for section 04.\n\nDefinitions:\n• Project — OpenShift's unit of tenancy: a Kubernetes namespace with added governance (annotations, RBAC, lifecycle). One per domain here.\n• ServiceAccount — a workload identity inside a namespace; pods run as one, and mesh mTLS and authorization policies key off it.\n• mTLS (mutual TLS) — both client and server present certificates; the mesh establishes it automatically between sidecars, with no app code." }); + notes: "One of the few manifests worth showing: the labels carry meaning. Point at istio-injection: enabled, which opts this domain into the mesh explicitly. That choice is about selective meshing, not cluster-wide injection, and pays off when the mesh and canary come up later. The ServiceAccount is the workload identity that mesh mTLS and authorization policies key off, worth remembering once the mesh is in play.\n\nDefinitions:\n• Project: OpenShift's unit of tenancy: a Kubernetes namespace with added governance (annotations, RBAC, lifecycle). One per domain here.\n• ServiceAccount: a workload identity inside a namespace; pods run as one, and mesh mTLS and authorization policies key off it.\n• mTLS (mutual TLS): both client and server present certificates; the mesh establishes it automatically between sidecars, with no app code." }); codeSlide({ eyebrow: "Domain ownership", title: "The domain owns its access", lang: "YAML · OpenShift", - note: "Project-scoped RBAC, enforced by the API server: the domain team holds full rights inside its own Project and none outside it. Ownership is not a convention people remember — it's a rule the platform applies on every request.", + note: "Project-scoped RBAC, enforced by the API server: the domain team holds full rights inside its own Project and none outside it. Ownership is not a convention people remember; it's a rule the platform applies on every request.", code: code(`apiVersion: rbac.authorization.k8s.io/v1 kind: Role metadata: @@ -190,40 +190,40 @@ roleRef: kind: Role name: order-owner apiGroup: rbac.authorization.k8s.io`), - notes: "The enforcement. RBAC is namespace-scoped — the order team has full rights inside the order Project and none outside it, enforced on every request by the API server. This is 'structural, not a convention' made concrete: ownership isn't remembered, it's a rule that runs. Don't read line by line — point at the Role verbs and the Group binding and move on.\n\nDefinitions:\n• RBAC (Role-Based Access Control) — Kubernetes' permission model; Roles grant verbs on resources, RoleBindings bind them to users/groups, enforced by the API server." }); + notes: "The enforcement. RBAC is namespace-scoped: the order team has full rights inside the order Project and none outside it, enforced on every request by the API server. This is 'structural, not a convention' made concrete: ownership isn't remembered, it's a rule that runs. Don't read line by line; point at the Role verbs and the Group binding and move on.\n\nDefinitions:\n• RBAC (Role-Based Access Control): Kubernetes' permission model; Roles grant verbs on resources, RoleBindings bind them to users/groups, enforced by the API server." }); diagramSlide({ eyebrow: "Domain ownership", title: "The domain owns its data, end to end", image: "17-value-domain-ownership", - caption: "Each domain ships its data product on its own — clear owner, enforced boundary, no central team in the path.", - notes: "SECTION VALUE-CLOSER for domain ownership — the slide that should stick. Value banner up top is the payoff; the callout pills are the reference-implementation pieces (Project, RBAC, SCCs, ResourceQuota); the red band is what breaks without it — fuzzy boundaries and ownership vacuums. Recap the whole section in 60 seconds against this picture, then move to data as a product." }); + caption: "Each domain ships its data product on its own: clear owner, enforced boundary, no central team in the path.", + notes: "Value closer for domain ownership, the slide that should stick. Value banner up top is the payoff; the callout pills are the reference-implementation pieces (Project, RBAC, SCCs, ResourceQuota); the red band is what breaks without it: fuzzy boundaries and ownership vacuums. Recap the whole section in 60 seconds against this picture, then move to data as a product." }); /* ====================== 02 · DATA AS A PRODUCT ====================== */ -divider({ num: "02", title: "Data as a product", sub: "Discoverable, addressable, trustworthy, self-describing — held to the standard of any software product.", - notes: "Principle 2 — the big section: contracts, catalog, protocols, APIs. Throughline: a data product is an autonomous unit, not a renamed table, and autonomy makes the rest of the mesh possible. We see the anatomy, the four protocol APIs and their Python implementations, the contracts (runtime vs discovery), the catalog — then the value-closer." }); +divider({ num: "02", title: "Data as a product", sub: "Discoverable, addressable, trustworthy, self-describing: held to the standard of any software product.", + notes: "Principle 2, the big section: contracts, catalog, protocols, APIs. Throughline: a data product is an autonomous unit, not a renamed table, and autonomy makes the rest of the mesh possible. We see the anatomy, the four protocol APIs and their Python implementations, the contracts (runtime vs discovery), the catalog, then the value closer." }); contentSlide({ eyebrow: "Data as a product", title: "The principle, in one slide", bullets: [ { text: "A data product is held to the same standard as any software product: discoverable, addressable, trustworthy, self-describing, interoperable, and secure.", }, - { text: "It is the architectural quantum of a mesh — the smallest unit you independently deploy and operate, carrying everything it needs to do its job.", lvl: 1 }, + { text: "It is the architectural quantum of a mesh: the smallest unit you independently deploy and operate, carrying everything it needs to do its job.", lvl: 1 }, { head: true, text: "Why it matters" }, - { text: "A 'data product' that is really just a renamed table can't serve itself, enforce its own policies, or describe itself — and federated governance has nowhere to live in it.", }, + { text: "A 'data product' that is really just a renamed table can't serve itself, enforce its own policies, or describe itself, and federated governance has nowhere to live in it.", }, { text: "Autonomy is what makes the rest of the mesh possible. Lose it, and you lose the governance model with it.", }, ], - notes: "Principle recap. Land the phrase 'architectural quantum' — the smallest independently deployable unit that carries everything it needs. The warning is the most-cited failure in the literature: calling a renamed table a 'data product.' If it can't serve, govern, and describe itself, it's not a product, and you've lost the governance model too." }); + notes: "Principle recap. Land the phrase 'architectural quantum': the smallest independently deployable unit that carries everything it needs. The warning is the most-cited failure in the literature: calling a renamed table a 'data product.' If it can't serve, govern, and describe itself, it's not a product, and you've lost the governance model too." }); diagramSlide({ eyebrow: "Data as a product", title: "Anatomy of a data product", image: "data-product-anatomy", - caption: "Input ports, output ports, the transformation between them, and the metadata and policies that make it discoverable and governed. Not a dataset — a product.", - notes: "The anatomy. Input ports, output ports, transformation logic, metadata/policies wrapped around it — a real data product is self-contained. Contrast again with the renamed table, which has none of this. In our build, each domain service IS this picture: owns data, serves APIs, emits events, publishes a contract." }); + caption: "Input ports, output ports, the transformation between them, and the metadata and policies that make it discoverable and governed. Not a dataset: a product.", + notes: "The anatomy. Input ports, output ports, transformation logic, metadata/policies wrapped around it: a real data product is self-contained. Contrast again with the renamed table, which has none of this. In our build, each domain service is this picture: owns data, serves APIs, emits events, publishes a contract." }); -diagramSlide({ eyebrow: "Data as a product", title: "Four protocols, four contracts — each by fitness", +diagramSlide({ eyebrow: "Data as a product", title: "Four protocols, four contracts, each by fitness", image: "17-api-implementations", - caption: "REST at the edge, gRPC between services, GraphQL to compose, events to decouple — and the Python that implements each.", - notes: "NEW diagram — the APIs and their implementations. Walk the four cards: REST (FastAPI, OpenAPI) at the edge; gRPC (grpcio, Protobuf) between services; GraphQL (Strawberry, SDL) to compose reads; events (aiokafka, Avro) to decouple. The middle band names the Python library for each — this audience wants to know what actually implements it. Bottom band: the gateway composing one query across REST + gRPC. Key message: not one protocol everywhere — each earns its place by fitness. Next slide shows the gateway code.\n\nDefinitions:\n• REST — resource-oriented HTTP APIs; used at the edge where external clients cross the trust boundary.\n• gRPC — a fast, strongly-typed RPC protocol over HTTP/2; used for synchronous service-to-service calls.\n• GraphQL — a query language that lets a client request exactly the shape it needs; the gateway uses it to compose reads across products.\n• OpenAPI — the contract format describing a REST API's endpoints, parameters, and responses.\n• Protobuf (Protocol Buffers) — the strongly-typed contract format for gRPC interfaces (.proto files).\n• SDL (Schema Definition Language) — GraphQL's contract format describing the available types and queries.\n• Avro — a binary serialization format with a schema; the runtime event contract, enforced by the registry at publish time." }); + caption: "REST at the edge, gRPC between services, GraphQL to compose, events to decouple, and the Python that implements each.", + notes: "The APIs and their implementations. Walk the four cards: REST (FastAPI, OpenAPI) at the edge; gRPC (grpcio, Protobuf) between services; GraphQL (Strawberry, SDL) to compose reads; events (aiokafka, Avro) to decouple. The middle band names the Python library for each; this audience wants to know what actually implements it. Bottom band: the gateway composing one query across REST + gRPC. Key message: not one protocol everywhere, each fits the job by fitness. Next slide shows the gateway code.\n\nDefinitions:\n• REST: resource-oriented HTTP APIs; used at the edge where external clients cross the trust boundary.\n• gRPC: a fast, strongly-typed RPC protocol over HTTP/2; used for synchronous service-to-service calls.\n• GraphQL: a query language that lets a client request exactly the shape it needs; the gateway uses it to compose reads across products.\n• OpenAPI: the contract format describing a REST API's endpoints, parameters, and responses.\n• Protobuf (Protocol Buffers): the strongly-typed contract format for gRPC interfaces (.proto files).\n• SDL (Schema Definition Language): GraphQL's contract format describing the available types and queries.\n• Avro: a binary serialization format with a schema; the runtime event contract, enforced by the registry at publish time." }); -codeSlide({ eyebrow: "Data as a product", title: "The gateway composes — in Python", +codeSlide({ eyebrow: "Data as a product", title: "The gateway composes, in Python", lang: "Python · Strawberry + clients", - note: "The gateway exposes one GraphQL graph; its resolvers fetch each piece from the owning product over that product's existing interface — order over REST, stock over gRPC — and GraphQL assembles the shaped response. One query, multiple backends, no GraphQL added to the domain services.", + note: "The gateway exposes one GraphQL graph; its resolvers fetch each piece from the owning product over that product's existing interface (order over REST, stock over gRPC) and GraphQL assembles the shaped response. One query, multiple backends, no GraphQL added to the domain services.", code: code(`import strawberry @strawberry.type @@ -245,16 +245,16 @@ class Query: # REST call to order-service data = await rest.get(f"/orders/{id}") return Order(**data)`), - notes: "Real Python — the kind of code the audience wants. Strawberry defines the GraphQL types; resolvers call out to the owning products. Point at the two resolver bodies: order() makes a REST call to order-service; the nested stock field makes a gRPC call to inventory-service. One query, two backends, two protocols, composed by the gateway — and the domain services were NOT changed; they keep their REST and gRPC interfaces. This is gateway orchestration; true subgraph federation is the production alternative worth a mention.\n\nDefinitions:\n• GraphQL — a query language that lets a client request exactly the shape it needs; the gateway uses it to compose reads across products.\n• REST — resource-oriented HTTP APIs; used at the edge where external clients cross the trust boundary.\n• gRPC — a fast, strongly-typed RPC protocol over HTTP/2; used for synchronous service-to-service calls.\n• Strawberry — the Python GraphQL library used to implement the gateway's resolvers." }); + notes: "Real Python, the kind of code the audience wants. Strawberry defines the GraphQL types; resolvers call out to the owning products. Point at the two resolver bodies: order() makes a REST call to order-service; the nested stock field makes a gRPC call to inventory-service. One query, two backends, two protocols, composed by the gateway, and the domain services were not changed; they keep their REST and gRPC interfaces. This is gateway orchestration; true subgraph federation is the production alternative worth a mention.\n\nDefinitions:\n• GraphQL: a query language that lets a client request exactly the shape it needs; the gateway uses it to compose reads across products.\n• REST: resource-oriented HTTP APIs; used at the edge where external clients cross the trust boundary.\n• gRPC: a fast, strongly-typed RPC protocol over HTTP/2; used for synchronous service-to-service calls.\n• Strawberry: the Python GraphQL library used to implement the gateway's resolvers." }); diagramSlide({ eyebrow: "Data as a product", title: "Two jobs the registry does", image: "contract-flow", - caption: "A runtime contract is load-bearing on the hot path — the event won't serialize without it. A discovery contract describes the product for humans, CI, and the catalog.", - notes: "The conceptual heart of contracts. Two jobs, very different coupling. RUNTIME contract: the event schema — producer/consumer serialize against it, so the event won't encode/decode without it; the registry is in the hot path, which is why it can REJECT a breaking change at publish time. DISCOVERY contract: OpenAPI and SDL — source of truth for humans, CI, and the catalog, but nothing fails at runtime if absent. Don't conflate them. Most useful idea in the contracts story." }); + caption: "A runtime contract sits on the hot path: the event won't serialize without it. A discovery contract describes the product for humans, CI, and the catalog.", + notes: "The conceptual heart of contracts. Two jobs, very different coupling. Runtime contract: the event schema; producer and consumer serialize against it, so the event won't encode/decode without it, and the registry is in the hot path, which is why it can reject a breaking change at publish time. Discovery contract: OpenAPI and SDL, the source of truth for humans, CI, and the catalog, but nothing fails at runtime if absent. Don't conflate them. Most useful idea in the contracts story." }); codeSlide({ eyebrow: "Data as a product", title: "The runtime contract: a registered schema", lang: "Avro · schema registry", - note: "The event schema is load-bearing: producer and consumer serialize against it, so the registry can reject an incompatible change at publish time — governance enforced computationally, before a single consumer breaks. v2 adds currency additively, with a default that keeps v1 consumers working.", + note: "The event schema sits on the hot path: producer and consumer serialize against it, so the registry can reject an incompatible change at publish time, governance enforced computationally, before a single consumer breaks. v2 adds currency additively, with a default that keeps v1 consumers working.", code: code(`{ "type": "record", "namespace": "com.acme.order", @@ -272,110 +272,110 @@ codeSlide({ eyebrow: "Data as a product", title: "The runtime contract: a regist ] } # registry compatibility: BACKWARD`), - notes: "Show the contract because the contract IS the lesson. This is the v2 evolution we canary in section 04: currency added with a default, so backward-compatible — old consumers keep working. The registry is set to BACKWARD compatibility, so it rejects any change that would break consumers, at publish time. Federated governance made computational: the rule runs automatically. Tie forward to the canary.\n\nDefinitions:\n• Avro — a binary serialization format with a schema; the runtime event contract, enforced by the registry at publish time.\n• Apicurio Registry — the schema/contract registry; stores Avro/Protobuf/OpenAPI/SDL and enforces compatibility." }); + notes: "Show the contract because the contract is the lesson. This is the v2 evolution the canary carries later: currency added with a default, so it's backward-compatible and old consumers keep working. The registry is set to backward compatibility, so it rejects any change that would break consumers, at publish time. Federated governance made computational: the rule runs automatically. Tie forward to the canary.\n\nDefinitions:\n• Avro: a binary serialization format with a schema; the runtime event contract, enforced by the registry at publish time.\n• Apicurio Registry: the schema/contract registry; stores Avro/Protobuf/OpenAPI/SDL and enforces compatibility." }); -diagramSlide({ eyebrow: "Data as a product", title: "Discovery and lineage — the catalog", +diagramSlide({ eyebrow: "Data as a product", title: "Discovery and lineage: the catalog", image: "contracts-registry-catalog", - caption: "Contracts live in one registry; the catalog ingests them — plus schemas and topics — to make products discoverable and their lineage visible.", - notes: "The catalog story. The registry holds contract SHAPES; the catalog (OpenMetadata) answers which products exist, who owns them, who consumes whom, and lineage. The load-bearing argument: a mesh's premise is consumers finding and trusting products WITHOUT a central team. Without a usable catalog, they fall back to asking the central team — and you've rebuilt the bottleneck. So the catalog is a requirement, not an add-on. Ties back to ownership and forward to the anti-patterns.\n\nDefinitions:\n• OpenMetadata — the data catalog: which products exist, their schemas, owners, and the lineage graph across domains.\n• Lineage — the graph of which products produce and which consume each dataset/event; answers 'if this changes, what's downstream?'\n• Apicurio Registry — the schema/contract registry; stores Avro/Protobuf/OpenAPI/SDL and enforces compatibility." }); + caption: "Contracts live in one registry; the catalog ingests them, plus schemas and topics, to make products discoverable and their lineage visible.", + notes: "The catalog story. The registry holds contract shapes; the catalog (OpenMetadata) answers which products exist, who owns them, who consumes whom, and lineage. The argument that matters: a mesh's premise is consumers finding and trusting products without a central team. Without a usable catalog, they fall back to asking the central team, and the bottleneck is rebuilt. So the catalog is a requirement, not an add-on. Ties back to ownership and forward to the anti-patterns.\n\nDefinitions:\n• OpenMetadata: the data catalog: which products exist, their schemas, owners, and the lineage graph across domains.\n• Lineage: the graph of which products produce and which consume each dataset/event; answers 'if this changes, what's downstream?'\n• Apicurio Registry: the schema/contract registry; stores Avro/Protobuf/OpenAPI/SDL and enforces compatibility." }); diagramSlide({ eyebrow: "Data as a product", title: "Products others can find and trust", image: "17-value-data-product", - caption: "Discoverable, addressable, trustworthy, self-describing — consumers depend on products without a broker in the middle.", - notes: "SECTION VALUE-CLOSER for data as a product. Payoff: products others can find and depend on without a broker. Pills: Deployment+Service, schema registry, OpenMetadata, CRDs. Failure mode in the red band: 'dumb' data products — renamed tables that can't serve or govern themselves. Recap against this picture, then move to the platform that provides the shared infrastructure." }); + caption: "Discoverable, addressable, trustworthy, self-describing: consumers depend on products without a broker in the middle.", + notes: "Value closer for data as a product. Payoff: products others can find and depend on without a broker. Pills: Deployment+Service, schema registry, OpenMetadata, CRDs. Failure mode in the red band: 'dumb' data products, renamed tables that can't serve or govern themselves. Recap against this picture, then move to the platform that provides the shared infrastructure." }); /* ====================== 03 · SELF-SERVE DATA PLATFORM ====================== */ -divider({ num: "03", title: "Self-serve data platform", sub: "Shared infrastructure domains consume — so they don't each build Kafka, a registry, a catalog, or observability.", - notes: "Principle 3. Throughline: the self-serve platform is what lets domain ownership SCALE — many domains, one platform, no central team in the critical path. Hero concept: the operator. We cover operators/OperatorHub, elasticity with KEDA (as diagrams), recoverability, then the value-closer with OpenShift specifics." }); +divider({ num: "03", title: "Self-serve data platform", sub: "Shared infrastructure domains consume, so they don't each build Kafka, a registry, a catalog, or observability.", + notes: "Principle 3. Throughline: the self-serve platform is what lets domain ownership scale, with many domains on one platform and no central team in the critical path. Hero concept: the operator. We cover operators/OperatorHub, elasticity with KEDA (as diagrams), recoverability, then the value closer with OpenShift specifics." }); contentSlide({ eyebrow: "Self-serve platform", title: "The principle, in one slide", bullets: [ { text: "Domain teams should not each build their own streaming, database operations, registry, catalog, or observability. The platform provides these as shared infrastructure they consume by declaration.", }, - { text: "A domain that needs Kafka asks for a topic — it does not learn to operate Kafka.", lvl: 1 }, + { text: "A domain that needs Kafka asks for a topic; it does not learn to operate Kafka.", lvl: 1 }, { head: true, text: "Why it matters" }, { text: "Without a self-serve platform, every domain reinvents the same infrastructure, badly, and ownership fragments into shadow platform teams.", }, { text: "The self-serve layer is what lets domain ownership scale: many domains, one platform, no central team in the critical path.", }, ], - notes: "Principle recap. The one-liner that lands: 'a domain that needs Kafka asks for a topic — it does not learn to operate Kafka.' Without this layer, every domain reinvents infrastructure badly, and you get shadow platform teams — re-fragmented ownership. The self-serve platform lets ownership actually scale." }); + notes: "Principle recap. The one-liner that lands: 'a domain that needs Kafka asks for a topic; it does not learn to operate Kafka.' Without this layer, every domain reinvents infrastructure badly, and you get shadow platform teams: re-fragmented ownership. The self-serve platform lets ownership actually scale." }); diagramSlide({ eyebrow: "Self-serve platform", title: "The three planes of the platform", image: "platform-planes", - caption: "The infrastructure plane, the data-product developer-experience plane, and the mesh-experience plane — what a domain consumes to ship a product without operating the substrate.", - notes: "The platform-planes model. Three planes: raw infrastructure at the bottom, the developer-experience plane in the middle (how a domain declares a product), the mesh-experience plane (discovery, governance across products). The audience needn't memorize the names — the point is 'platform' is layered, and domains interact with the higher planes by declaration, not raw infrastructure.\n\nDefinitions:\n• Kafka — the distributed event log / streaming platform; domains publish and consume domain events on topics.\n• OpenMetadata — the data catalog: which products exist, their schemas, owners, and the lineage graph across domains." }); + caption: "The infrastructure plane, the data-product developer-experience plane, and the mesh-experience plane: what a domain consumes to ship a product without operating the substrate.", + notes: "The platform-planes model. Three planes: raw infrastructure at the bottom, the developer-experience plane in the middle (how a domain declares a product), the mesh-experience plane (discovery, governance across products). The audience needn't memorize the names; the point is 'platform' is layered, and domains interact with the higher planes by declaration, not raw infrastructure.\n\nDefinitions:\n• Kafka: the distributed event log / streaming platform; domains publish and consume domain events on topics.\n• OpenMetadata: the data catalog: which products exist, their schemas, owners, and the lineage graph across domains." }); (() => { const s = pres.addSlide(); s.background = { color: C.white }; L.head(s, "Self-serve platform", "Operators are the self-serve mechanism"); L.addBullets(s, [ - { text: "An operator packages the knowledge of how to run a complex system — Kafka, Postgres, a service mesh, autoscaling — into a controller that reconciles a simple declarative request into a running system.", }, + { text: "An operator packages the knowledge of how to run a complex system (Kafka, Postgres, a service mesh, autoscaling) into a controller that reconciles a simple declarative request into a running system.", }, { head: true, text: "On OpenShift" }, { text: "OperatorHub + OLM: the platform team curates which operators are available; domains install capabilities from a catalog, with lifecycle and upgrades managed.", lvl: 1 }, { text: "GitOps (OpenShift GitOps / Argo CD): the desired state of every domain and the platform lives in Git and is continuously reconciled.", lvl: 1 }, - { text: "The integrated image registry, Routes, and Service Mesh round out the substrate — all consumed by declaration.", lvl: 1 }, + { text: "The integrated image registry, Routes, and Service Mesh round out the substrate, all consumed by declaration.", lvl: 1 }, { text: "The result: a domain declares what it wants; the platform delivers the operational behavior.", color: C.ink }, ], { x: 0.7, y: 1.95, w: PW - 1.4 }); L.footer(s); - s.addNotes("The hero concept: the operator. Define it clearly — an operator encodes the operational knowledge of running a complex system into a controller that turns a declarative request into a running system. On OpenShift, OperatorHub + OLM curate and lifecycle these; GitOps makes the whole mesh reproducible from Git; the integrated registry/Routes/Service Mesh complete the substrate. Payoff: the domain declares what it wants; the platform delivers the behavior. We deliberately DON'T show operator YAML — the next slides show the value as diagrams.\n\nDefinitions:\n• OperatorHub — OpenShift's catalog of installable operators; the platform team curates what's available.\n• OLM (Operator Lifecycle Manager) — installs, updates, and manages the lifecycle of operators on the cluster.\n• GitOps — desired cluster state lives in Git and is continuously reconciled onto the cluster (OpenShift GitOps is based on Argo CD).\n• Argo CD — the GitOps controller that syncs Git-declared state to the cluster.\n• CRD (Custom Resource Definition) — extends the Kubernetes API with new object types; operators define and reconcile these.\n• AMQ Streams — Red Hat's supported distribution of Strimzi: Apache Kafka run on Kubernetes via operators.\n• CloudNativePG (CNPG) — an operator that runs PostgreSQL clusters on Kubernetes as a custom resource.\n• Service Mesh (OpenShift Service Mesh / Istio) — a layer of sidecar proxies that secures, routes, and observes service-to-service traffic."); + s.addNotes("The hero concept: the operator. Define it clearly: an operator encodes the operational knowledge of running a complex system into a controller that turns a declarative request into a running system. On OpenShift, OperatorHub + OLM curate and lifecycle these; GitOps makes the whole mesh reproducible from Git; the integrated registry/Routes/Service Mesh complete the substrate. Payoff: the domain declares what it wants; the platform delivers the behavior. We don't show operator YAML; the next slides show the value as diagrams.\n\nDefinitions:\n• OperatorHub: OpenShift's catalog of installable operators; the platform team curates what's available.\n• OLM (Operator Lifecycle Manager): installs, updates, and manages the lifecycle of operators on the cluster.\n• GitOps: desired cluster state lives in Git and is continuously reconciled onto the cluster (OpenShift GitOps is based on Argo CD).\n• Argo CD: the GitOps controller that syncs Git-declared state to the cluster.\n• CRD (Custom Resource Definition): extends the Kubernetes API with new object types; operators define and reconcile these.\n• AMQ Streams: Red Hat's supported distribution of Strimzi: Apache Kafka run on Kubernetes via operators.\n• CloudNativePG (CNPG): an operator that runs PostgreSQL clusters on Kubernetes as a custom resource.\n• Service Mesh (OpenShift Service Mesh / Istio): a layer of sidecar proxies that secures, routes, and observes service-to-service traffic."); })(); diagramSlide({ eyebrow: "Self-serve platform", title: "A trusted base for every data product", image: "17-trusted-supply-chain", - caption: "Red Hat UBI + trusted content: domains build on a base they can trust and the platform can verify — security inherited, not re-sourced per team.", - notes: "NEW slide — the secure supply chain, a strong piece of the self-serve value. The platform doesn't just give domains Kafka and Postgres; it gives them a trusted FOUNDATION to build on. Walk the chain left to right: Red Hat UBI is the base image — enterprise-maintained, freely redistributable, a legitimate production base, not a community image of unknown provenance. Trusted libraries layer language dependencies from Red Hat / verified channels with known provenance and patching. The domain's app layers on top. The result is signed and SBOM-stamped, and admission verifies it before it runs. The value, three ways: provenance you can prove; patched at the base (a CVE fix in UBI re-bases the whole fleet on rebuild); and it gives governance something to enforce (signatures and SBOMs let admission policy reject anything unverified, mesh-wide). This sits at the intersection of two principles: self-serve (shared infrastructure every domain consumes) AND federated governance (verification enforced automatically at admission).\n\nDefinitions:\n• UBI (Universal Base Image) — Red Hat's freely redistributable, enterprise-maintained container base image; a legitimate production base without a subscription.\n• SBOM (Software Bill of Materials) — a manifest of every component in an image, used to audit and verify what's inside.\n• CVE (Common Vulnerabilities and Exposures) — a publicly catalogued security vulnerability identifier.\n• Admission control — the API-server gate that validates or rejects resources as they're created; where computational governance runs." }); + caption: "Red Hat UBI + trusted content: domains build on a base they can trust and the platform can verify, security inherited, not re-sourced per team.", + notes: "The secure supply chain, a strong piece of the self-serve value. The platform doesn't just give domains Kafka and Postgres; it gives them a trusted foundation to build on. Walk the chain left to right: Red Hat UBI is the base image, enterprise-maintained, freely redistributable, a legitimate production base, not a community image of unknown provenance. Trusted libraries layer language dependencies from Red Hat / verified channels with known provenance and patching. The domain's app layers on top. The result is signed and SBOM-stamped, and admission verifies it before it runs. The value, three ways: provenance you can prove; patched at the base (a CVE fix in UBI re-bases the whole fleet on rebuild); and it gives governance something to enforce (signatures and SBOMs let admission policy reject anything unverified, mesh-wide). This sits at the intersection of two principles: self-serve (shared infrastructure every domain consumes) and federated governance (verification enforced automatically at admission).\n\nDefinitions:\n• UBI (Universal Base Image): Red Hat's freely redistributable, enterprise-maintained container base image; a legitimate production base without a subscription.\n• SBOM (Software Bill of Materials): a manifest of every component in an image, used to audit and verify what's inside.\n• CVE (Common Vulnerabilities and Exposures): a publicly catalogued security vulnerability identifier.\n• Admission control: the API-server gate that validates or rejects resources as they're created; where computational governance runs." }); diagramSlide({ eyebrow: "Self-serve platform", title: "Elastic products: scale on lag, even to zero", image: "17-keda-lag", - caption: "An event consumer's real demand is the backlog waiting for it — KEDA scales notification-service on Kafka lag, down to zero when idle.", - notes: "NEW KEDA diagram — shown as a diagram, not a ScaledObject YAML, on purpose. Walk left to right: the topic's consumer lag is the signal; KEDA reads it via the Kafka scaler; it scales notification-service from zero up to ten. The 'why not CPU' callout is the teaching point — a consumer idling at 0% CPU with a 10,000-message backlog SHOULD scale up, and CPU can't see that, but lag can. Scale-to-zero makes 'elastic data product' real economics: it costs nothing while idle.\n\nDefinitions:\n• KEDA (Kubernetes Event-Driven Autoscaling) — scales workloads on external signals (queue lag, request rate), including to zero.\n• Kafka — the distributed event log / streaming platform; domains publish and consume domain events on topics." }); + caption: "An event consumer's real demand is the backlog waiting for it: KEDA scales notification-service on Kafka lag, down to zero when idle.", + notes: "The KEDA story is shown as a diagram, not a ScaledObject YAML, because the shape of the idea matters more than the manifest. Walk left to right: the topic's consumer lag is the signal; KEDA reads it via the Kafka scaler; it scales notification-service from zero up to ten. The 'why not CPU' callout is the teaching point: a consumer idling at 0% CPU with a 10,000-message backlog should scale up, and CPU can't see that, but lag can. Scale-to-zero makes 'elastic data product' real economics: it costs nothing while idle.\n\nDefinitions:\n• KEDA (Kubernetes Event-Driven Autoscaling): scales workloads on external signals (queue lag, request rate), including to zero.\n• Kafka: the distributed event log / streaming platform; domains publish and consume domain events on topics." }); diagramSlide({ eyebrow: "Self-serve platform", title: "Elastic reads: the KEDA HTTP add-on", image: "17-keda-http", - caption: "A read gateway's demand is request volume — the HTTP add-on scales it on traffic, and deliberately not on the canaried service.", - notes: "Second KEDA diagram, also a picture. The gateway scales on HTTP request volume via the add-on's interceptor. The DESIGN point is the red band: HTTP scaling goes on the gateway, NOT order-service — because order-service carries the canary, and HTTP-scaling a service whose traffic is split by weight would have the two mechanisms fight over the same pods. 'Fit the mechanism to the workload.' The 'unknown at rest' note pre-empts a question: a scaled-to-zero workload reports unknown until the first request — expected.\n\nDefinitions:\n• KEDA (Kubernetes Event-Driven Autoscaling) — scales workloads on external signals (queue lag, request rate), including to zero.\n• Canary — a progressive rollout that shifts a fraction of live traffic to a new version, increasing it as confidence grows." }); + caption: "A read gateway's demand is request volume: the HTTP add-on scales it on traffic, and not on the canaried service.", + notes: "Second KEDA diagram, also a picture. The gateway scales on HTTP request volume via the add-on's interceptor. The design point is the red band: HTTP scaling goes on the gateway, not order-service, because order-service carries the canary, and HTTP-scaling a service whose traffic is split by weight would have the two mechanisms fight over the same pods. 'Fit the mechanism to the workload.' The 'unknown at rest' note pre-empts a question: a scaled-to-zero workload reports unknown until the first request; that's expected.\n\nDefinitions:\n• KEDA (Kubernetes Event-Driven Autoscaling): scales workloads on external signals (queue lag, request rate), including to zero.\n• Canary: a progressive rollout that shifts a fraction of live traffic to a new version, increasing it as confidence grows." }); contentSlide({ eyebrow: "Self-serve platform", title: "Resilience: recoverability as a platform property", bullets: [ - { text: "The cloud-native answer to failure isn't 'prevent it' — it's 'make recovery cheap and automatic.' A mesh inherits this from the platform.", }, + { text: "The cloud-native answer to failure isn't 'prevent it'; it's 'make recovery cheap and automatic.' A mesh inherits this from the platform.", }, { text: "A crashed pod is restarted; a failed rollout is rolled back; a node's workloads are rescheduled; declarative state continuously reconciles toward what you asked for.", lvl: 1 }, { head: true, text: "Design products toward recoverability" }, { text: "Don't hold critical state only in memory; come back to a known-good state after a crash; let the operator recover the systems it manages; let a downed consumer catch up on the backlog it missed.", }, - { text: "A product deployed this way is recoverable by construction — and that resilience is something the domain gets from the platform, not something it builds.", color: C.ink }, + { text: "A product deployed this way is recoverable by construction, and that resilience is something the domain gets from the platform, not something it builds.", color: C.ink }, ], - notes: "Resilience as the other half of the platform's value. Cloud-native doesn't prevent failure — it makes recovery cheap and automatic, and a mesh inherits that. List the reconciliation behaviors quickly. Then design guidance: build TOWARD recoverability — no critical in-memory-only state, known-good recovery, let operators recover what they manage, let consumers catch up. Payoff: recoverable by construction, from the platform." }); + notes: "Resilience as the other half of the platform's value. Cloud-native doesn't prevent failure; it makes recovery cheap and automatic, and a mesh inherits that. List the reconciliation behaviors quickly. Then design guidance: build toward recoverability (no critical in-memory-only state, known-good recovery, let operators recover what they manage, let consumers catch up). Payoff: recoverable by construction, from the platform." }); diagramSlide({ eyebrow: "Self-serve platform", title: "Infrastructure domains consume, not operate", image: "17-value-self-serve", - caption: "Streaming, databases, scaling, and the mesh — declared, not operated. One platform team's work, leveraged by every domain.", - notes: "SECTION VALUE-CLOSER for self-serve platform. Payoff: leverage — one platform team's work consumed by every domain — plus consistency and focus. Pills: OperatorHub/OLM, AMQ Streams/CNPG, KEDA, GitOps. Failure mode: every domain reinventing infrastructure, shadow platform teams. This is where the OpenShift value is most concrete: OperatorHub curates capabilities, GitOps makes it reproducible, the integrated substrate is there to consume, not assemble. Then to governance." }); + caption: "Streaming, databases, scaling, and the mesh: declared, not operated. One platform team's work, reused by every domain.", + notes: "Value closer for self-serve platform. Payoff: one platform team's work consumed by every domain, plus consistency and focus. Pills: OperatorHub/OLM, AMQ Streams/CNPG, KEDA, GitOps. Failure mode: every domain reinventing infrastructure, shadow platform teams. This is where the OpenShift value is most concrete: OperatorHub curates capabilities, GitOps makes it reproducible, the integrated substrate is there to consume, not assemble. Then to governance." }); /* ====================== 04 · FEDERATED COMPUTATIONAL GOVERNANCE ====================== */ -divider({ num: "04", title: "Federated computational governance", sub: "Standards enforced computationally by the platform — not by meetings and policy documents.", - notes: "Principle 4 — the one that holds the others together. Throughline: encode the few global rules so the platform RUNS them, and ownership stays decentralized while standards hold. We cover the governance model, the canary (contract evolution + selective-meshing decision), the mesh and mTLS as a diagram, admission policy framing, and observability as governance — then the value-closer." }); +divider({ num: "04", title: "Federated computational governance", sub: "Standards enforced computationally by the platform, not by meetings and policy documents.", + notes: "Principle 4, the one that holds the others together. Throughline: encode the few global rules so the platform runs them, and ownership stays decentralized while standards hold. We cover the governance model, the canary (contract evolution + selective-meshing decision), the mesh and mTLS as a diagram, admission policy framing, and observability as governance, then the value closer." }); contentSlide({ eyebrow: "Federated governance", title: "The principle, in one slide", bullets: [ - { text: "A small set of global rules keeps independent products interoperable, and the platform enforces them automatically, at the boundary — not a review board after the fact.", }, + { text: "A small set of global rules keeps independent products interoperable, and the platform enforces them automatically, at the boundary, not a review board after the fact.", }, { text: "Federated: domains decide their own local rules; the platform enforces the few global ones that let products join up.", lvl: 1 }, { head: true, text: "Why it matters" }, { text: "Bolt governance on from outside and it never fits; over-correct into an approval bureaucracy and you've rebuilt the central bottleneck.", }, { text: "Computational governance is the resolution: encode the global rules so the platform runs them, and ownership stays decentralized while standards hold.", }, ], - notes: "Principle recap. The tension: governance fails in two opposite directions — bolted on from outside (never fits) or over-corrected into an approval bureaucracy (the bottleneck returns). Computational governance resolves it: encode the few global rules, let the platform enforce them automatically. 'Federated' = domains own local rules, platform enforces global ones. This is the principle that lets all the others coexist." }); + notes: "Principle recap. The tension: governance fails in two opposite directions, bolted on from outside (never fits) or over-corrected into an approval bureaucracy (the bottleneck returns). Computational governance resolves it: encode the few global rules, let the platform enforce them automatically. 'Federated' means domains own local rules, platform enforces global ones. This is the principle that lets all the others coexist." }); diagramSlide({ eyebrow: "Federated governance", title: "Global rules, local autonomy", image: "federated-governance-model", caption: "A few global rules the platform enforces; the rest left to domains. Where that line falls is the central governance design decision.", - notes: "The governance-model diagram. The design question every mesh faces: which rules are GLOBAL (so products interoperate — shared identifiers, contract formats, lineage conventions) versus LOCAL (left to domains). Wrong toward global and you have a bottleneck; wrong toward local and nothing joins up. The platform enforces the global few automatically. Judgment, not a formula — but the diagram frames the decision." }); + notes: "The governance-model diagram. The design question every mesh faces: which rules are global (so products interoperate: shared identifiers, contract formats, lineage conventions) versus local (left to domains). Wrong toward global and you have a bottleneck; wrong toward local and nothing joins up. The platform enforces the global few automatically. Judgment, not a formula, but the diagram frames the decision." }); diagramSlide({ eyebrow: "Federated governance", title: "The service mesh: secure by default, shiftable on demand", image: "17-service-mesh", - caption: "Sidecars beside every product establish mTLS automatically and let the platform shift traffic by weight — both without application code.", - notes: "NEW service-mesh diagram — replaces PeerAuthentication YAML, because the mesh's shape is far clearer as a picture. Walk it: every product has a sidecar (Envoy) beside the app container; sidecar-to-sidecar traffic is mutual TLS, automatic, no app code; istiod is the control plane issuing identities and pushing routing/policy. Bottom band: the canary — weights shift 90/10 → 50/50 → 0/100, mesh splits live traffic, no client change. Two capabilities, one mechanism: security and traffic management as platform properties. The one piece of mesh code worth seeing — the canary weights — is next.\n\nDefinitions:\n• Istio — the service mesh project underlying OpenShift Service Mesh; injects sidecar proxies and controls traffic/security.\n• Service Mesh (OpenShift Service Mesh / Istio) — a layer of sidecar proxies that secures, routes, and observes service-to-service traffic.\n• Sidecar — a proxy container (Envoy) injected next to your app container; carries the mesh's traffic, security, and telemetry.\n• Envoy — the high-performance proxy used as the mesh sidecar/data plane.\n• istiod — the Istio control plane; issues identities/certificates and pushes routing and policy to every sidecar.\n• mTLS (mutual TLS) — both client and server present certificates; the mesh establishes it automatically between sidecars, with no app code.\n• Canary — a progressive rollout that shifts a fraction of live traffic to a new version, increasing it as confidence grows." }); + caption: "Sidecars beside every product establish mTLS automatically and let the platform shift traffic by weight, both without application code.", + notes: "This service-mesh diagram replaces PeerAuthentication YAML, because the mesh's shape is far clearer as a picture. Walk it: every product has a sidecar (Envoy) beside the app container; sidecar-to-sidecar traffic is mutual TLS, automatic, no app code; istiod is the control plane issuing identities and pushing routing/policy. Bottom band: the canary, with weights shifting 90/10 to 50/50 to 0/100, the mesh splitting live traffic, no client change. Two capabilities, one mechanism: security and traffic management as platform properties. The one piece of mesh code next shows the canary weights.\n\nDefinitions:\n• Istio: the service mesh project underlying OpenShift Service Mesh; injects sidecar proxies and controls traffic/security.\n• Service Mesh (OpenShift Service Mesh / Istio): a layer of sidecar proxies that secures, routes, and observes service-to-service traffic.\n• Sidecar: a proxy container (Envoy) injected next to your app container; carries the mesh's traffic, security, and telemetry.\n• Envoy: the high-performance proxy used as the mesh sidecar/data plane.\n• istiod: the Istio control plane; issues identities/certificates and pushes routing and policy to every sidecar.\n• mTLS (mutual TLS): both client and server present certificates; the mesh establishes it automatically between sidecars, with no app code.\n• Canary: a progressive rollout that shifts a fraction of live traffic to a new version, increasing it as confidence grows." }); -codeSlide({ eyebrow: "Federated governance", title: "Canary a contract — the one mesh manifest worth seeing", +codeSlide({ eyebrow: "Federated governance", title: "Canary a contract: the one mesh manifest that matters", lang: "YAML · OpenShift Service Mesh", - note: "The interesting thing to canary in a mesh isn't a new binary — it's a new version of the contract. v2 adds the currency field from section 02; the mesh shifts live traffic by weight. Shifting the canary is a one-line weight change: 90/10 → 50/50 → 0/100.", + note: "The interesting thing to canary in a mesh isn't a new binary; it's a new version of the contract. v2 adds the currency field defined on the registry's runtime-contract schema; the mesh shifts live traffic by weight. Shifting the canary is a one-line weight change: 90/10 to 50/50 to 0/100.", code: code(`apiVersion: networking.istio.io/v1beta1 kind: VirtualService metadata: { name: order-service, namespace: order } @@ -387,32 +387,32 @@ spec: weight: 90 - destination: { host: order-service, subset: v2 } weight: 10 # move 90/10 → 50/50 → 0/100`), - notes: "We show this ONE manifest because the weights ARE the lesson — everything else about the mesh was a diagram. Connect to section 02: v2 is the contract that added the currency field. The canary isn't canarying a new binary, it's canarying a CONTRACT CHANGE — the data-mesh-specific insight. Shifting the rollout is changing two numbers and re-applying; rolling back is the same in reverse. In the reference implementation: 90/10 produced ~95/5, 50/50 produced ~46/54 — both in band.\n\nDefinitions:\n• VirtualService — Istio's routing rule; here it splits traffic between service versions by weight (the canary).\n• DestinationRule — Istio's object defining service subsets (e.g. v1/v2) that a VirtualService routes to.\n• Canary — a progressive rollout that shifts a fraction of live traffic to a new version, increasing it as confidence grows.\n• Service Mesh (OpenShift Service Mesh / Istio) — a layer of sidecar proxies that secures, routes, and observes service-to-service traffic.\n• Istio — the service mesh project underlying OpenShift Service Mesh; injects sidecar proxies and controls traffic/security." }); + notes: "We show this one manifest because the weights are the lesson; everything else about the mesh was a diagram. Connect back to the contract: v2 is the schema that added the currency field. The canary isn't canarying a new binary, it's canarying a contract change, the data-mesh-specific insight. Shifting the rollout is changing two numbers and re-applying; rolling back is the same in reverse. In the reference implementation, 90/10 produced about 95/5, and 50/50 produced about 46/54, both in band.\n\nDefinitions:\n• VirtualService: Istio's routing rule; here it splits traffic between service versions by weight (the canary).\n• DestinationRule: Istio's object defining service subsets (e.g. v1/v2) that a VirtualService routes to.\n• Canary: a progressive rollout that shifts a fraction of live traffic to a new version, increasing it as confidence grows.\n• Service Mesh (OpenShift Service Mesh / Istio): a layer of sidecar proxies that secures, routes, and observes service-to-service traffic.\n• Istio: the service mesh project underlying OpenShift Service Mesh; injects sidecar proxies and controls traffic/security." }); -contentSlide({ eyebrow: "Federated governance", title: "Mesh selectively — a design decision", +contentSlide({ eyebrow: "Federated governance", title: "Mesh selectively: a design decision", bullets: [ { text: "The convenient default is namespace-wide sidecar injection: one label, everything meshed. The better default for a real platform is to opt specific workloads into the mesh.", }, { head: true, text: "Three kinds of workload don't belong in the mesh" }, - { text: "Batch jobs meant to finish — a sidecar that never exits keeps the job from ever completing.", lvl: 1 }, - { text: "Operator-managed infrastructure with its own TLS — a second TLS layer collides with the database's own.", lvl: 1 }, - { text: "Anything where the sidecar's cost buys nothing — resource overhead and a control-plane dependency for no benefit.", lvl: 1 }, + { text: "Batch jobs meant to finish: a sidecar that never exits keeps the job from ever completing.", lvl: 1 }, + { text: "Operator-managed infrastructure with its own TLS: a second TLS layer collides with the database's own.", lvl: 1 }, + { text: "Anything where the sidecar's cost buys nothing: resource overhead and a control-plane dependency for no benefit.", lvl: 1 }, { text: "Selective injection also contains blast radius: only workloads that need the mesh depend on its control plane being healthy.", color: C.ink }, ], - notes: "An important DESIGN DECISION, framed as value not war-story. The convenient default — namespace-wide injection — is wrong for a platform that holds more than just services. Opt workloads INTO the mesh instead. Three categories that don't belong: finish-and-terminate jobs (sidecar never exits, job hangs), operator-managed infra with its own TLS (collision), anything where the sidecar buys nothing. Plus the systemic argument: selective injection contains blast radius. State it as a principle here — the reasoning matters more than the war stories.\n\nDefinitions:\n• Sidecar — a proxy container (Envoy) injected next to your app container; carries the mesh's traffic, security, and telemetry.\n• Istio — the service mesh project underlying OpenShift Service Mesh; injects sidecar proxies and controls traffic/security.\n• Admission control — the API-server gate that validates or rejects resources as they're created; where computational governance runs." }); + notes: "An important design decision, framed as value not war-story. The convenient default, namespace-wide injection, is wrong for a platform that holds more than just services. Opt workloads into the mesh instead. Three categories that don't belong: finish-and-terminate jobs (sidecar never exits, job hangs), operator-managed infra with its own TLS (collision), anything where the sidecar buys nothing. Plus the systemic argument: selective injection contains blast radius. State it as a principle here; the reasoning matters more than the war stories.\n\nDefinitions:\n• Sidecar: a proxy container (Envoy) injected next to your app container; carries the mesh's traffic, security, and telemetry.\n• Istio: the service mesh project underlying OpenShift Service Mesh; injects sidecar proxies and controls traffic/security.\n• Admission control: the API-server gate that validates or rejects resources as they're created; where computational governance runs." }); -diagramSlide({ eyebrow: "Federated governance", title: "Observability — the mesh emits, the platform collects", +diagramSlide({ eyebrow: "Federated governance", title: "Observability: the mesh emits, the platform collects", image: "17-observability-stack", - caption: "Sidecars emit metrics and traces; Prometheus, Tempo, and Kiali collect them — most of it without touching application code.", - notes: "NEW observability diagram — replaces config YAML. Walk the flow: products with sidecars emit metrics and traces over OTLP; the OTEL Collector fans out to Prometheus (metrics), Tempo (traces), Kiali (live topology); Grafana unifies into dashboards. Headline: because the sidecars already measure the traffic they carry, most of this is observability you get FROM THE PLATFORM, not code you write. That 'for free from the mesh' property is the self-serve principle showing up inside governance.\n\nDefinitions:\n• Prometheus — the metrics database; scrapes request rates, errors, latencies, and consumer lag (much of it emitted by the mesh sidecars).\n• Tempo — the distributed-tracing backend; stores spans so a request's path across products can be reconstructed.\n• Kiali — the mesh console showing the live service topology and traffic, including the canary split.\n• Grafana — the dashboard layer over metrics and traces.\n• OTLP (OpenTelemetry Protocol) — the wire format workloads and sidecars use to export metrics and traces to the collector.\n• Sidecar — a proxy container (Envoy) injected next to your app container; carries the mesh's traffic, security, and telemetry." }); + caption: "Sidecars emit metrics and traces; Prometheus, Tempo, and Kiali collect them, most of it without touching application code.", + notes: "This observability diagram replaces config YAML. Walk the flow: products with sidecars emit metrics and traces over OTLP; the OTEL Collector fans out to Prometheus (metrics), Tempo (traces), Kiali (live topology); Grafana unifies into dashboards. Headline: because the sidecars already measure the traffic they carry, most of this is observability you get from the platform, not code you write. That property comes from the mesh itself, the self-serve principle showing up inside governance.\n\nDefinitions:\n• Prometheus: the metrics database; scrapes request rates, errors, latencies, and consumer lag (much of it emitted by the mesh sidecars).\n• Tempo: the distributed-tracing backend; stores spans so a request's path across products can be reconstructed.\n• Kiali: the mesh console showing the live service topology and traffic, including the canary split.\n• Grafana: the dashboard layer over metrics and traces.\n• OTLP (OpenTelemetry Protocol): the wire format workloads and sidecars use to export metrics and traces to the collector.\n• Sidecar: a proxy container (Envoy) injected next to your app container; carries the mesh's traffic, security, and telemetry." }); diagramSlide({ eyebrow: "Federated governance", title: "Three signals, correlated across a domain", image: "17-three-signals", - caption: "Metrics say something is wrong, traces say where, logs say what — tied together by a shared trace id across products.", - notes: "NEW three-signals diagram — explains WHY you need all three, correlated. Top: one request becomes one trace whose spans cross three products. The three columns: metrics answer 'is it slow/failing and is the platform reacting,' traces answer 'WHERE in the path,' logs answer 'exactly WHAT happened.' The red correlation band is the punchline: the same trace id on all three, so you jump from a latency spike to the slow trace to the exact log line — across the whole domain. Why it matters in a mesh: the interesting behavior lives BETWEEN products, where no single product's logs can see it. The slide that makes observability click.\n\nDefinitions:\n• OpenTelemetry (OTel) — the vendor-neutral standard and SDKs for emitting traces and metrics from application code." }); + caption: "Metrics say something is wrong, traces say where, logs say what, tied together by a shared trace id across products.", + notes: "This three-signals diagram explains why you need all three, correlated. Top: one request becomes one trace whose spans cross three products. The three columns: metrics answer 'is it slow/failing and is the platform reacting,' traces answer 'where in the path,' logs answer 'exactly what happened.' The red correlation band is the punchline: the same trace id on all three, so you jump from a latency spike to the slow trace to the exact log line, across the whole domain. Why it matters in a mesh: the interesting behavior lives between products, where no single product's logs can see it. The slide that makes observability click.\n\nDefinitions:\n• OpenTelemetry (OTel): the vendor-neutral standard and SDKs for emitting traces and metrics from application code." }); -codeSlide({ eyebrow: "Federated governance", title: "A trace span across products — in Python", +codeSlide({ eyebrow: "Federated governance", title: "A trace span across products, in Python", lang: "Python · OpenTelemetry", - note: "Instrument the gateway and one GraphQL query becomes a connected tree of spans — an HTTP server span, a REST client span to order-service, a gRPC client span to inventory-service — stitched into one distributed trace. One well-chosen instrumentation point illuminates the whole read path.", + note: "Instrument the gateway and one GraphQL query becomes a connected tree of spans (an HTTP server span, a REST client span to order-service, a gRPC client span to inventory-service) stitched into one distributed trace. One well-chosen instrumentation point illuminates the whole read path.", code: code(`from opentelemetry import trace tracer = trace.get_tracer("graphql-gateway") @@ -427,30 +427,30 @@ async def resolve_order(info, order_id: str): stock = await inventory.GetStock(sku=order["sku"]) return compose(order, stock)`), - notes: "Python again — the audience wants to see how tracing attaches. The gateway is the entry point for the federated read path, so instrumenting it yields the most instructive trace. One query produces an HTTP server span plus a REST client span plus a gRPC client span — the composition from section 02, now visible as a span tree in Tempo. Instrument-the-entry-point-first is the same incremental discipline as the rest of the build. This produces the trace in the previous diagram.\n\nDefinitions:\n• OpenTelemetry (OTel) — the vendor-neutral standard and SDKs for emitting traces/metrics; here, used to instrument the gateway.\n• GraphQL — a query language that lets a client request exactly the shape it needs; the gateway uses it to compose reads across products.\n• REST — resource-oriented HTTP APIs; used at the edge where external clients cross the trust boundary.\n• gRPC — a fast, strongly-typed RPC protocol over HTTP/2; used for synchronous service-to-service calls.\n• Tempo — the distributed-tracing backend; stores spans so a request's path across products can be reconstructed." }); + notes: "Python again; the audience wants to see how tracing attaches. The gateway is the entry point for the federated read path, so instrumenting it yields the most instructive trace. One query produces an HTTP server span plus a REST client span plus a gRPC client span, the same composition the gateway showed earlier, now visible as a span tree in Tempo. Instrument-the-entry-point-first is the same incremental discipline as the rest of the build. This produces the trace in the previous diagram.\n\nDefinitions:\n• OpenTelemetry (OTel): the vendor-neutral standard and SDKs for emitting traces/metrics; here, used to instrument the gateway.\n• GraphQL: a query language that lets a client request exactly the shape it needs; the gateway uses it to compose reads across products.\n• REST: resource-oriented HTTP APIs; used at the edge where external clients cross the trust boundary.\n• gRPC: a fast, strongly-typed RPC protocol over HTTP/2; used for synchronous service-to-service calls.\n• Tempo: the distributed-tracing backend; stores spans so a request's path across products can be reconstructed." }); diagramSlide({ eyebrow: "Federated governance", title: "Standards that hold, ownership that stays put", image: "17-value-governance", - caption: "Global rules enforced by the platform automatically — decentralization preserved, interoperability guaranteed.", - notes: "SECTION VALUE-CLOSER for federated governance. Payoff: standards hold WITHOUT re-centralizing — the platform enforces the global rules automatically, so ownership stays decentralized. Pills: Service Mesh mTLS, progressive delivery, admission policy, the observability stack. Failure mode: governance bolted on from outside, or re-centralized into an approval bottleneck — the mesh's own anti-pattern. This closes the four principles; next we assemble the whole picture.\n\nDefinitions:\n• mTLS (mutual TLS) — both client and server present certificates; the mesh establishes it automatically between sidecars, with no app code.\n• Admission control — the API-server gate that validates or rejects resources as they're created; where computational governance runs.\n• Prometheus — the metrics database; scrapes request rates, errors, latencies, and consumer lag (much of it emitted by the mesh sidecars).\n• Tempo — the distributed-tracing backend; stores spans so a request's path across products can be reconstructed.\n• Kiali — the mesh console showing the live service topology and traffic, including the canary split." }); + caption: "Global rules enforced by the platform automatically: decentralization preserved, interoperability guaranteed.", + notes: "Value closer for federated governance. Payoff: standards hold without re-centralizing, because the platform enforces the global rules automatically, so ownership stays decentralized. Pills: Service Mesh mTLS, progressive delivery, admission policy, the observability stack. Failure mode: governance bolted on from outside, or re-centralized into an approval bottleneck, the mesh's own anti-pattern. This closes the four principles; next we assemble the whole picture.\n\nDefinitions:\n• mTLS (mutual TLS): both client and server present certificates; the mesh establishes it automatically between sidecars, with no app code.\n• Admission control: the API-server gate that validates or rejects resources as they're created; where computational governance runs.\n• Prometheus: the metrics database; scrapes request rates, errors, latencies, and consumer lag (much of it emitted by the mesh sidecars).\n• Tempo: the distributed-tracing backend; stores spans so a request's path across products can be reconstructed.\n• Kiali: the mesh console showing the live service topology and traffic, including the canary split." }); /* ====================== 05 · THE WHOLE PICTURE ====================== */ -divider({ num: "05", title: "The whole picture", sub: "The four principles, realized — and what the assembled platform lets you see it do.", - notes: "Synthesis section. Return to the reference architecture assembled, show the principles-to-pieces closer, describe what you can SEE it do (the acceptance vision), give adoption guidance, the honest 'when not to,' and close. Bring the energy up — this is where it lands." }); +divider({ num: "05", title: "The whole picture", sub: "The four principles, realized, and what the assembled platform lets you see it do.", + notes: "Synthesis section. Return to the reference architecture assembled, show the principles-to-pieces closer, describe what you can see it do (the acceptance vision), give adoption guidance, the frank 'when not to,' and close. Bring the energy up; this is where it lands." }); diagramSlide({ eyebrow: "The whole picture", title: "The reference architecture, assembled", image: "reference-data-mesh-architecture", - caption: "Every piece in its place: domain products owning their data, the platform planes beneath, governance and observability spanning the whole — the mesh, complete.", - notes: "Callback to the centerpiece from section 0 — now the audience has built every box. Walk it once more, quickly, naming the principle each layer serves: domain products (ownership + product), the platform planes (self-serve), the mesh and observability spanning everything (governance). The promise from the start is kept: you recognize all of it. The 'it all fits' moment." }); + caption: "Every piece in its place: domain products owning their data, the platform planes beneath, governance and observability spanning the whole: the mesh, complete.", + notes: "A callback to the centerpiece from the opening orientation, now that the audience has built every box. Walk it once more, quickly, naming the principle each layer serves: domain products (ownership + product), the platform planes (self-serve), the mesh and observability spanning everything (governance). The promise from the start is kept: you recognize all of it. The 'it all fits' moment." }); diagramSlide({ eyebrow: "The whole picture", title: "The four principles, realized on OpenShift", image: "17-principles-to-pieces", caption: "One picture to leave with: each principle, the pieces that deliver it, on the OpenShift substrate that gives all four a home.", - notes: "THE closing picture — replaces the old table, because a picture sticks and a table doesn't. Four color-coded columns, one per principle, each stacking its implementing pieces, all on the black OpenShift substrate bar. If the audience remembers one slide, make it this one. Read down each column, then land on the base: OpenShift is where all four principles find a home. The takeaway image.\n\nDefinitions:\n• Project — OpenShift's unit of tenancy: a Kubernetes namespace with added governance (annotations, RBAC, lifecycle). One per domain here.\n• RBAC (Role-Based Access Control) — Kubernetes' permission model; Roles grant verbs on resources, RoleBindings bind them to users/groups, enforced by the API server.\n• SCC (Security Context Constraint) — OpenShift policy controlling what a pod may do on the node (run as root, mount host paths, privileged, etc.).\n• ResourceQuota — a namespace-level cap on aggregate resource use (CPU, memory, object counts) so one domain can't starve others.\n• OperatorHub — OpenShift's catalog of installable operators; the platform team curates what's available.\n• OLM (Operator Lifecycle Manager) — installs, updates, and manages the lifecycle of operators on the cluster.\n• GitOps — desired cluster state lives in Git and is continuously reconciled onto the cluster (OpenShift GitOps is based on Argo CD).\n• KEDA (Kubernetes Event-Driven Autoscaling) — scales workloads on external signals (queue lag, request rate), including to zero.\n• mTLS (mutual TLS) — both client and server present certificates; the mesh establishes it automatically between sidecars, with no app code.\n• Canary — a progressive rollout that shifts a fraction of live traffic to a new version, increasing it as confidence grows.\n• Admission control — the API-server gate that validates or rejects resources as they're created; where computational governance runs.\n• Prometheus — the metrics database; scrapes request rates, errors, latencies, and consumer lag (much of it emitted by the mesh sidecars).\n• Tempo — the distributed-tracing backend; stores spans so a request's path across products can be reconstructed.\n• Kiali — the mesh console showing the live service topology and traffic, including the canary split.\n• OpenMetadata — the data catalog: which products exist, their schemas, owners, and the lineage graph across domains." }); + notes: "This closing picture replaces the old table, because a picture sticks and a table doesn't. Four color-coded columns, one per principle, each stacking its implementing pieces, all on the black OpenShift substrate bar. If the audience remembers one slide, make it this one. Read down each column, then land on the base: OpenShift is where all four principles find a home. The takeaway image.\n\nDefinitions:\n• Project: OpenShift's unit of tenancy: a Kubernetes namespace with added governance (annotations, RBAC, lifecycle). One per domain here.\n• RBAC (Role-Based Access Control): Kubernetes' permission model; Roles grant verbs on resources, RoleBindings bind them to users/groups, enforced by the API server.\n• SCC (Security Context Constraint): OpenShift policy controlling what a pod may do on the node (run as root, mount host paths, privileged, etc.).\n• ResourceQuota: a namespace-level cap on aggregate resource use (CPU, memory, object counts) so one domain can't starve others.\n• OperatorHub: OpenShift's catalog of installable operators; the platform team curates what's available.\n• OLM (Operator Lifecycle Manager): installs, updates, and manages the lifecycle of operators on the cluster.\n• GitOps: desired cluster state lives in Git and is continuously reconciled onto the cluster (OpenShift GitOps is based on Argo CD).\n• KEDA (Kubernetes Event-Driven Autoscaling): scales workloads on external signals (queue lag, request rate), including to zero.\n• mTLS (mutual TLS): both client and server present certificates; the mesh establishes it automatically between sidecars, with no app code.\n• Canary: a progressive rollout that shifts a fraction of live traffic to a new version, increasing it as confidence grows.\n• Admission control: the API-server gate that validates or rejects resources as they're created; where computational governance runs.\n• Prometheus: the metrics database; scrapes request rates, errors, latencies, and consumer lag (much of it emitted by the mesh sidecars).\n• Tempo: the distributed-tracing backend; stores spans so a request's path across products can be reconstructed.\n• Kiali: the mesh console showing the live service topology and traffic, including the canary split.\n• OpenMetadata: the data catalog: which products exist, their schemas, owners, and the lineage graph across domains." }); contentSlide({ eyebrow: "The whole picture", title: "What you can see it do", bullets: [ - { text: "Not just that the mesh runs — that you can watch it run. This is the acceptance bar the platform is built toward:", }, + { text: "Not just that the mesh runs, but that you can watch it run. This is the acceptance bar the platform is built toward:", }, { text: "A single GraphQL query appears as a trace whose spans cross three products and three protocols.", lvl: 1 }, { text: "Metrics show the gateway's load and the autoscaler waking replicas to meet it.", lvl: 1 }, { text: "A contract rolls out version by version as live traffic shifts across the canary.", lvl: 1 }, @@ -458,71 +458,71 @@ contentSlide({ eyebrow: "The whole picture", title: "What you can see it do", { text: "Kiali shows the live topology, canary split and all.", lvl: 1 }, { text: "These are the instruments through which you understand a system too distributed for any single vantage point.", color: C.ink }, ], - notes: "The acceptance vision — what a live demo would show. If you're doing a live demo (the walkthrough), this slide is your script: the trace across products, the autoscaler reacting, the canary shifting, the lineage graph, the Kiali topology. Even without a live demo, it paints 'done looks like this.' Closing line is the observability thesis: instruments for a system too distributed to see from any one place.\n\nDefinitions:\n• GraphQL — a query language that lets a client request exactly the shape it needs; the gateway uses it to compose reads across products.\n• Kiali — the mesh console showing the live service topology and traffic, including the canary split.\n• Canary — a progressive rollout that shifts a fraction of live traffic to a new version, increasing it as confidence grows.\n• Lineage — the graph of which products produce and which consume each dataset/event; answers 'if this changes, what's downstream?'" }); + notes: "The acceptance vision: what a live demo would show. If you're doing a live demo (the walkthrough), this slide is your script: the trace across products, the autoscaler reacting, the canary shifting, the lineage graph, the Kiali topology. Even without a live demo, it paints 'done looks like this.' Closing line is the observability thesis: instruments for a system too distributed to see from any one place.\n\nDefinitions:\n• GraphQL: a query language that lets a client request exactly the shape it needs; the gateway uses it to compose reads across products.\n• Kiali: the mesh console showing the live service topology and traffic, including the canary split.\n• Canary: a progressive rollout that shifts a fraction of live traffic to a new version, increasing it as confidence grows.\n• Lineage: the graph of which products produce and which consume each dataset/event; answers 'if this changes, what's downstream?'" }); contentSlide({ eyebrow: "The whole picture", title: "Adoption: start small", bullets: [ { text: "You don't adopt a mesh by building all of this at once. The principles are independent enough to land incrementally.", }, - { text: "Start with one domain and one product — give it a Project, a contract, and an owner.", lvl: 1 }, + { text: "Start with one domain and one product: give it a Project, a contract, and an owner.", lvl: 1 }, { text: "Add the self-serve platform pieces as the second and third products need them, not before.", lvl: 1 }, - { text: "Introduce computational governance once you have products to govern — the registry before the catalog, mTLS before policy.", lvl: 1 }, + { text: "Introduce computational governance once you have products to govern: the registry before the catalog, mTLS before policy.", lvl: 1 }, { head: true, text: "Let the architecture correct against reality" }, - { text: "Each piece is verifiable on its own. Stand one up, confirm it, then add the next — the same incremental discipline the reference implementation followed.", }, + { text: "Each piece is verifiable on its own. Stand one up, confirm it, then add the next: the same incremental discipline the reference implementation followed.", }, ], - notes: "Practical adoption advice — the natural next question is 'where do I start?' Answer: not all at once. One domain, one product, with a Project, a contract, and an owner. Add platform pieces as products need them. Introduce governance once there are products to govern. Meta-point: each piece is independently verifiable, so build incrementally and let reality correct the design — exactly how the reference implementation was built." }); + notes: "Practical adoption advice; the natural next question is 'where do I start?' Answer: not all at once. One domain, one product, with a Project, a contract, and an owner. Add platform pieces as products need them. Introduce governance once there are products to govern. Meta-point: each piece is independently verifiable, so build incrementally and let reality correct the design, exactly how the reference implementation was built." }); /* ====================== 06 · ANTI-PATTERNS ====================== */ -divider({ num: "06", title: "Anti-patterns", sub: "How data-mesh efforts go wrong — the conceptual and organizational failure modes, so you can spot them early.", - notes: "Final section before we close. Everything so far was what to build and how. This section is what goes WRONG — and deliberately NOT the implementation potholes (those are operational and a different kind of lesson); these are the CONCEPTUAL and ORGANIZATIONAL failure modes that show up again and again. The framing to open with: a data mesh is a socio-technical system, and the literature is nearly unanimous that these efforts fail for organizational reasons far more than technical ones. Most of these are invisible at the architecture-diagram level and only become obvious months in, once they've calcified. Each anti-pattern: the trap, the tell, and the fix." }); +divider({ num: "06", title: "Anti-patterns", sub: "How data-mesh efforts go wrong: the conceptual and organizational failure modes, so you can spot them early.", + notes: "Final section before we close. Everything so far was what to build and how. This section is what goes wrong, and not the implementation potholes (those are operational and a different kind of lesson); these are the conceptual and organizational failure modes that show up again and again. The framing to open with: a data mesh is a socio-technical system, and the literature is nearly unanimous that these efforts fail for organizational reasons far more than technical ones. Most of these are invisible at the architecture-diagram level and only become obvious months in, once they've calcified. Each anti-pattern: the trap, the tell, and the fix." }); contentSlide({ eyebrow: "Anti-patterns", title: "The architecture diagram is the easy part", bullets: [ - { text: "A data mesh is a socio-technical system. The technical pieces — registry, catalog, streams, autoscaling — are the concrete, runnable part this deck spent most of its time on.", }, + { text: "A data mesh is a socio-technical system. The technical pieces (registry, catalog, streams, autoscaling) are the concrete, runnable part this deck spent most of its time on.", }, { text: "But the literature is nearly unanimous: data-mesh efforts fail for organizational reasons far more often than technical ones.", color: C.ink }, { head: true, text: "Why these are worth knowing before you start" }, - { text: "Most are invisible on the architecture diagram. They only become obvious months in — once they've calcified into how teams work.", }, + { text: "Most are invisible on the architecture diagram. They only become obvious months in, once they've calcified into how teams work.", }, { text: "Each of the next slides: the trap, the tell that reveals it, and the fix the literature points to.", }, ], - notes: "Set up the section. The key reframe for a technical audience that just sat through 40 slides of architecture: the diagram was the easy part. The parts that aren't on the diagram — who owns what, how governance is enforced, whether the loop is closed, whether the org needed a mesh at all — are where efforts actually succeed or fail. These anti-patterns are drawn from practitioners who've watched many of these efforts up close. They're stated generally; I'll note where each touched our own build." }); + notes: "Set up the section. The key reframe for a technical audience that just sat through 40 slides of architecture: the diagram was the easy part. The parts that aren't on the diagram (who owns what, how governance is enforced, whether the loop is closed, whether the org needed a mesh at all) are where efforts actually succeed or fail. These anti-patterns are drawn from practitioners who've watched many of these efforts up close. They're stated generally; I'll note where each touched our own build." }); (() => { // each anti-pattern: trap (what it is) + tell (the symptom) + fix const items = [ { h: "\u201cThe tool will solve it\u201d", - trap: "Treating data mesh as something you buy or install — adopt a catalog, relabel the lake's tables as \u201cdata products,\u201d declare victory. But the principles are about how teams work, not which software runs.", + trap: "Treating data mesh as something you buy or install: adopt a catalog, relabel the lake's tables as \u201cdata products,\u201d declare victory. But the principles are about how teams work, not which software runs.", tell: "A project plan that's entirely a tooling rollout, with no mention of team boundaries, ownership, or incentives.", fix: "Change the operating model first; let tools support the principles rather than substitute for them.", - n: "The most common trap, and practitioners put it first. A data mesh is an operating-model change; no tool delivers that on its own. The tell is a plan that's all tooling and no team-structure change — if the only thing changing is the software, you end up with the old centralized model plus a new dashboard. In our build, every component (Apicurio, OpenMetadata, KEDA, Istio) is deliberately a substrate the domains build on, not a turnkey mesh." }, + n: "The most common trap, and practitioners put it first. A data mesh is an operating-model change; no tool delivers that on its own. The tell is a plan that's all tooling and no team-structure change; if the only thing changing is the software, you end up with the old centralized model plus a new dashboard. In our build, every component (Apicurio, OpenMetadata, KEDA, Istio) is a substrate the domains build on, not a turnkey mesh." }, { h: "Centralization wearing a new name", trap: "Recreating the central-team bottleneck under new vocabulary: a \u201cplatform team\u201d that's still the only group that can ship a product; governance as a manual approval gate; or domains spinning up shadow data teams.", - tell: "Decisions still funnel through one team — for control, for \u201cconsistency,\u201d for governance — and the old lead times quietly return.", + tell: "Decisions still funnel through one team, for control, for \u201cconsistency,\u201d for governance, and the old lead times quietly return.", fix: "Push real ownership and the ability to ship to the domains; make governance automated and policy-driven, not approval-driven.", - n: "Data mesh exists to break the single-central-team bottleneck; the failure is rebuilding it under new words. Three shapes: the central team stays the proxy for every domain; governance re-centralizes as a review board; or domains build shadow data teams and you get silos again. Decentralization is the whole point — anything that funnels decisions back through one team reintroduces the original problem. In our build, the namespace-and-operator model gives each domain a place to own its slice with no central team in the loop." }, + n: "Data mesh exists to break the single-central-team bottleneck; the failure is rebuilding it under new words. Three shapes: the central team stays the proxy for every domain; governance re-centralizes as a review board; or domains build shadow data teams and you get silos again. Decentralization is the whole point; anything that funnels decisions back through one team reintroduces the original problem. In our build, the namespace-and-operator model gives each domain a place to own its slice with no central team in the loop." }, { h: "Data products that are \u201cdumb\u201d", trap: "Stripping a data product down to a renamed table or a catalog row. A real product serves its data, governs it, describes itself, and is discoverable; a dumb one is a static dataset with a label.", - tell: "A \u201cdata product\u201d you can't deploy, version, or call — you can only query the table it points at.", + tell: "A \u201cdata product\u201d you can't deploy, version, or call; you can only query the table it points at.", fix: "Give every product ports, a versioned contract, an owner, and a lifecycle. Autonomy is what lets governance live inside it.", - n: "The failure the principle's own author warns about most sharply. Lose product autonomy and you lose the governance model with it — because there's nowhere to embed governance in a renamed table, federated computational governance gets dropped too. The downgrade cascades. In our build, each service IS its data product: owns its schema, publishes a versioned contract, emits its own events, exposes its own API." }, - { h: "Governance as an afterthought — or as bureaucracy", - trap: "Governance fails in two opposite directions: bolted on from outside (it never fits — quality and access become things done TO a product), or over-corrected into a heavyweight approval bureaucracy (centralization again).", + n: "The failure the principle's own author warns about most sharply. Lose product autonomy and you lose the governance model with it, because there's nowhere to embed governance in a renamed table, and federated computational governance gets dropped too. The downgrade cascades. In our build, each service is its data product: owns its schema, publishes a versioned contract, emits its own events, exposes its own API." }, + { h: "Governance as an afterthought, or as bureaucracy", + trap: "Governance fails in two opposite directions: bolted on from outside (it never fits, and quality and access become things done to a product), or over-corrected into a heavyweight approval bureaucracy (centralization again).", tell: "Either a free-for-all where nothing joins up, or a bottleneck where nothing ships.", - fix: "Federated computational governance: a small set of global rules the platform enforces automatically, embedded in each product — not administered by a committee.", - n: "The interesting design question for any mesh is which rules are GLOBAL (so products interoperate — shared identifiers, contract formats, lineage conventions) versus left to domains. Get that line wrong either way and you get the free-for-all or the bottleneck. In our build, contracts live in a registry and lineage is recorded in the catalog automatically as part of deploying, not as a separate review step." }, + fix: "Federated computational governance: a small set of global rules the platform enforces automatically, embedded in each product, not administered by a committee.", + n: "The interesting design question for any mesh is which rules are global (so products interoperate: shared identifiers, contract formats, lineage conventions) versus left to domains. Get that line wrong either way and you get the free-for-all or the bottleneck. In our build, contracts live in a registry and lineage is recorded in the catalog automatically as part of deploying, not as a separate review step." }, { h: "No clear owner, or fuzzy domain boundaries", trap: "Two related failures: the ownership vacuum (a dataset nobody is responsible for, so quality drifts and trust erodes), and fuzzy boundaries (the same concept modeled three ways by three teams).", tell: "Consumers get conflicting versions of nominally the same data, and changes ripple across services that should have been independent.", - fix: "Bounded contexts — the same discipline that makes microservices work. Map the domains, name an owner per product, revisit boundaries as the org changes.", + fix: "Bounded contexts: the same discipline that makes microservices work. Map the domains, name an owner per product, revisit boundaries as the org changes.", n: "A mesh is only as good as the clarity of who owns what. Both failures come down to bounded contexts. Without clear, agreed domain boundaries and an explicit owner per product, the mesh degrades into the distributed mess the principles were meant to prevent. The remedy is unglamorous: actually map the domains and write down who owns each product. In our build, each domain is a service with a single clear responsibility and an owner by construction." }, - { h: "The open loop — no feedback", + { h: "The open loop: no feedback", trap: "Static analytical products built downstream from a lake, disconnected from the operational systems that produce the data and from the consumers who use them. No operational-to-analytical loop, no consumer-feedback loop.", - tell: "A data product nobody monitors for use and nobody updates in response to how it's consumed — published once, then frozen.", + tell: "A data product nobody monitors for use and nobody updates in response to how it's consumed, published once, then frozen.", fix: "Close the loop by domain: react to operational events as they happen, and track whether products are actually useful to their consumers.", - n: "Data mesh is meant to close the loop between operational systems and analytical uses, organized by domain. The degraded version is an open loop — products go stale, the lead time between an app change and its analytical impact never shrinks, and the mesh delivers little more than the warehouse it replaced. In our build, the async spine means analytical consumers react to operational events as they happen rather than to a nightly extract." }, + n: "Data mesh is meant to close the loop between operational systems and analytical uses, organized by domain. The degraded version is an open loop: products go stale, the lead time between an app change and its analytical impact never shrinks, and the mesh delivers little more than the warehouse it replaced. In our build, the async spine means analytical consumers react to operational events as they happen rather than to a nightly extract." }, { h: "Hype-driven and wrong-fit adoption", - trap: "Adopting a mesh because it's fashionable. It earns its complexity in large orgs with many domains, many consumers, and the maturity to operate products and standards across teams — for a small org, the overhead can cost more than the bottleneck it removes.", + trap: "Adopting a mesh because it's fashionable. Its added complexity pays off in large orgs with many domains, many consumers, and the maturity to operate products and standards across teams; for a small org, the overhead can cost more than the bottleneck it removes.", tell: "Chasing more data products as an end in itself; months of planning the perfect mesh instead of shipping one real product; adopting the whole paradigm when one principle would do.", - fix: "Weigh data size, organizational complexity, existing tooling, and culture. Be willing to conclude a full mesh — or only some of its principles — is the right fit.", - n: "Less about HOW you build a mesh and more about WHETHER you should. This is the honest slide — and it builds credibility. A mesh is not for every organization. Sometimes a single principle, usually the self-serve platform, delivers most of the value without the full paradigm. Telling people when NOT to do this makes them trust you on when to. In our build, the final project is deliberately a learning implementation — sized to teach the shape, not to argue everyone should run a mesh in production." }, + fix: "Weigh data size, organizational complexity, existing tooling, and culture. Be willing to conclude a full mesh, or only some of its principles, is the right fit.", + n: "Less about how you build a mesh and more about whether you should. This is the frank slide, and it builds credibility. A mesh is not for every organization. Sometimes a single principle, usually the self-serve platform, delivers most of the value without the full paradigm. Naming when not to do this builds the trust to say when to. In our build, the final project is a learning implementation, sized to teach the shape, not to argue everyone should run a mesh in production." }, ]; items.forEach((it) => { const s = pres.addSlide(); @@ -540,23 +540,23 @@ contentSlide({ eyebrow: "Anti-patterns", title: "The architecture diagram is the contentSlide({ eyebrow: "Anti-patterns", title: "Recognizing them early", bullets: [ - { text: "None of these are exotic. They're the predictable result of taking a paradigm about ownership, autonomy, and feedback — and implementing only its visible technical surface.", }, + { text: "None of these are exotic. They're the predictable result of taking a paradigm about ownership, autonomy, and feedback, and implementing only its visible technical surface.", }, { text: "The recurring lesson across everyone who's written about failed efforts is the same:", color: C.ink }, - { text: "The architecture diagram is the easy part. Who owns what, how governance is enforced, whether the loop is closed, whether the organization needed a mesh at all — the parts that aren't on the diagram are where efforts actually succeed or fail.", lvl: 1 }, + { text: "The architecture diagram is the easy part. Who owns what, how governance is enforced, whether the loop is closed, whether the organization needed a mesh at all: the parts that aren't on the diagram are where efforts actually succeed or fail.", lvl: 1 }, ], - notes: "Section close. Tie the seven together: each is what happens when you implement the technical surface of a paradigm that's fundamentally about ownership, autonomy, and feedback — and skip the rest. The single sentence to leave them with: the architecture diagram is the easy part. That's a strong setup for the conclusion, which reframes the whole deck as having built exactly the parts that ARE on the diagram, while naming what isn't. Then go to the closing slide." }); + notes: "Section close. Tie the seven together: each is what happens when you implement the technical surface of a paradigm that's fundamentally about ownership, autonomy, and feedback, and skip the rest. The single sentence to leave them with: the architecture diagram is the easy part. That's a strong setup for the conclusion, which reframes the whole deck as having built exactly the parts that are on the diagram, while naming what isn't. Then go to the closing slide." }); (() => { const s = pres.addSlide(); s.addImage({ path: L.ILLUS, x: 0, y: 0, w: PW, h: PH, sizing: { type: "cover", w: PW, h: PH } }); const rx = PW * 0.42, rw = PW - rx - 0.7; - s.addText("The mesh is the network of products —", { x: rx, y: 2.5, w: rw, h: 0.9, fontSize: 28, color: "FFFFFF", fontFace: F.head, bold: true, valign: "top", margin: 0 }); + s.addText("The mesh is the network of products,", { x: rx, y: 2.5, w: rw, h: 0.9, fontSize: 28, color: "FFFFFF", fontFace: F.head, bold: true, valign: "top", margin: 0 }); s.addText("plus the platform and standards that let them interoperate.", { x: rx, y: 3.35, w: rw, h: 1.2, fontSize: 28, color: "FFD9D9", fontFace: F.head, bold: true, valign: "top", margin: 0 }); s.addText("OpenShift is where all four principles find a home.", { x: rx, y: 4.85, w: rw, h: 0.6, fontSize: 15, color: "FFFFFF", fontFace: F.body, italic: true, valign: "top", margin: 0 }); const lw = 1.25, lh = lw / L.LOGO_AR; s.addImage({ path: L.LOGO_LIGHT, x: PW - 0.6 - lw, y: PH - 0.3 - lh, w: lw, h: lh }); L.pageNumOnly(s, { dark: true }); - s.addNotes("Closing. The one-sentence definition that ties the talk together: a mesh is the network of products PLUS the platform and standards that let them interoperate — every word of which we built today. And, per the anti-patterns we just covered, the platform and standards are exactly the parts teams skip. End on OpenShift as the home for all four principles. Open for questions."); + s.addNotes("Closing. The one-sentence definition that ties the talk together: a mesh is the network of products plus the platform and standards that let them interoperate, every word of which we built today. And, per the anti-patterns we just covered, the platform and standards are exactly the parts teams skip. End on OpenShift as the home for all four principles. Open for questions."); })(); /* ============================ WRITE ============================ */ diff --git a/presentation/data-mesh-openshift/deck-lib.js b/presentation/data-mesh-openshift/deck-lib.js index 4a8ebb4..ff74f44 100644 --- a/presentation/data-mesh-openshift/deck-lib.js +++ b/presentation/data-mesh-openshift/deck-lib.js @@ -1,4 +1,4 @@ -// deck-lib.js — design-system helpers matched to the reference deck +// deck-lib.js: design-system helpers matched to the reference deck // "Designing Cloud-Native APIs" (Red Hat brand). Reuses the extracted brand // assets (illustration + logo) so branding is pixel-identical. const pptxgen = require("pptxgenjs"); @@ -9,13 +9,13 @@ const IMG = (name) => `dpng/${name}.png`; // brand assets extracted from the reference deck const ILLUS = "brand-illustration.png"; // 2500x1407: left ~37% icon collage, right solid dark-red -const LOGO_DARK = "brand-logo-dark.png"; // black wordmark — for WHITE slides -const LOGO_LIGHT = "brand-logo-light.png"; // white wordmark — for DARK slides +const LOGO_DARK = "brand-logo-dark.png"; // black wordmark, for WHITE slides +const LOGO_LIGHT = "brand-logo-light.png"; // white wordmark, for DARK slides const LOGO_AR = 496 / 117; // ---- palette (from the reference deck) ---- const C = { - red: "EE0000", // Red Hat Red — eyebrows, title-slide accents + red: "EE0000", // Red Hat Red: eyebrows, title-slide accents dkred: "8A0000", // (illustration's right side reads ~8A0000) ink: "151515", // near-black titles/body body: "242424", // body text @@ -191,7 +191,7 @@ function buildCodeRuns(code) { const lines = code.split("\n"); lines.forEach((ln, i) => { const br = i < lines.length - 1; - // find inline comment (# not inside quotes — simple heuristic) + // find inline comment (# not inside quotes, simple heuristic) const hashIdx = findComment(ln); if (hashIdx === 0) { runs.push({ text: ln, options: { color: C.codecmt, fontFace: F.mono, breakLine: br } }); From b616f569e95faca94f1700f7d3a6425626741d13 Mon Sep 17 00:00:00 2001 From: PatternCatalyst Date: Wed, 28 Oct 2026 09:29:45 -0400 Subject: [PATCH 2/2] docs(deck): Red Hat Display titles on the data-mesh decks Replace the Overpass SemiBold heading font token with Red Hat Display in both pptxgenjs sources (The Data Mesh 101, Data Mesh on OpenShift) and rebuild; body (Red Hat Text) and code (Red Hat Mono) fonts are unchanged. --- .../data-mesh-101/The_Data_Mesh_101.pptx | Bin 1943669 -> 1943411 bytes presentation/data-mesh-101/build-101.js | 2 +- .../Data_Mesh_on_OpenShift.pptx | Bin 8239219 -> 8238295 bytes presentation/data-mesh-openshift/deck-lib.js | 2 +- 4 files changed, 2 insertions(+), 2 deletions(-) diff --git a/presentation/data-mesh-101/The_Data_Mesh_101.pptx b/presentation/data-mesh-101/The_Data_Mesh_101.pptx index 6b37da96d4e3df79cabca61072e186aa3758a5d4..6c4d6fa29761381d75df4c93140e6c38145fc8db 100644 GIT binary patch delta 6131 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 diff --git a/presentation/data-mesh-openshift/deck-lib.js b/presentation/data-mesh-openshift/deck-lib.js index ff74f44..830af79 100644 --- a/presentation/data-mesh-openshift/deck-lib.js +++ b/presentation/data-mesh-openshift/deck-lib.js @@ -32,7 +32,7 @@ const C = { codemut: "8A8A8A", // lang label }; const F = { - head: "Overpass SemiBold", // headers/eyebrows/titles + head: "Red Hat Display", // headers/eyebrows/titles body: "Red Hat Text", // body mono: "Red Hat Mono", // code + section numbers };