#!/usr/bin/env python3 """Folie 5 – Event Sourcing: das groessere Bild (DEUTSCH). Quellen differenziert (Publisher / Subsysteme), Event-Prozessoren als Teil des Stores, Hot Store & Cold Store als Kernfunktion (Coral, CORE-Tag), Projektionen dazwischen, darunter Cold Store / Monitoring / Reporting. Tech-Impls als Chips. (adesso-Styling) Hintergrund: solides dunkles Blau (BG) – Farbe frei anpassbar.""" import os from pptx import Presentation from pptx.util import Inches, Pt from pptx.dml.color import RGBColor from pptx.enum.text import PP_ALIGN, MSO_ANCHOR from pptx.enum.shapes import MSO_SHAPE from adesso_style import (WHITE, INK, BLUE, TEAL, GREEN, CORAL, LIGHT, MUTE, RECF, F, FC, chrome) # ---- Hintergrundfarbe (solid, keine Transparenz) – frei anpassbar ---------- BG = RGBColor(0x0A, 0x3D, 0x6B) # dunkles Blau prs = Presentation() prs.slide_width = Inches(13.333) prs.slide_height = Inches(7.5) SW, SH = prs.slide_width, prs.slide_height s = prs.slides.add_slide(prs.slide_layouts[6]) def shp(kind, x, y, w, h, fill=None, line=None, lw=Pt(1), rot=0, adj=None): sh = s.shapes.add_shape(kind, x, y, w, h) if fill is None: sh.fill.background() else: sh.fill.solid(); sh.fill.fore_color.rgb = fill if line is None: sh.line.fill.background() else: sh.line.color.rgb = line; sh.line.width = lw if rot: sh.rotation = rot if adj is not None: try: sh.adjustments[0] = adj except Exception: pass sh.shadow.inherit = False return sh def txt(x, y, w, h, t, size=16, color=WHITE, bold=False, italic=False, align=PP_ALIGN.LEFT, font=F, anchor=MSO_ANCHOR.MIDDLE): tb = s.shapes.add_textbox(x, y, w, h); tf = tb.text_frame tf.word_wrap = True; tf.vertical_anchor = anchor tf.margin_left = tf.margin_right = tf.margin_top = tf.margin_bottom = Pt(0) first = True for line in t.split("\n"): p = tf.paragraphs[0] if first else tf.add_paragraph() first = False p.alignment = align r = p.add_run(); r.text = line r.font.size = Pt(size); r.font.bold = bold; r.font.italic = italic r.font.name = font; r.font.color.rgb = color return tb def chip(cx, y, text, fill=INK, fg=WHITE, size=11): """Tech-Impl als kleine Pill, zentriert um cx.""" w = Inches(0.34 + 0.092 * len(text)); h = Inches(0.30) x = cx - w / 2 shp(MSO_SHAPE.ROUNDED_RECTANGLE, x, y, w, h, fill=fill, adj=0.5) txt(x, y, w, h, text, size=size, color=fg, bold=False, align=PP_ALIGN.CENTER, font=FC) def core_tag(x, y): w, h = Inches(0.62), Inches(0.26) shp(MSO_SHAPE.ROUNDED_RECTANGLE, x, y, w, h, fill=INK, adj=0.5) txt(x, y, w, h, "CORE", size=9.5, color=TEAL, bold=True, align=PP_ALIGN.CENTER, font=FC) # ---- solider Hintergrund (zuerst -> liegt hinten) -------------------------- bg = shp(MSO_SHAPE.RECTANGLE, 0, 0, SW, SH, fill=BG) chrome(s, "System Landscape", 6) CX = Inches(6.665) # Folienmitte (x) # ---- Quellen: differenziert ------------------------------------------------ src_y, src_h = Inches(1.36), Inches(0.92) def source_box(x, w, title, examples): shp(MSO_SHAPE.ROUNDED_RECTANGLE, x, src_y, w, src_h, fill=WHITE, adj=0.10) txt(x + Inches(0.28), src_y + Inches(0.10), w - Inches(0.56), Inches(0.32), title, size=14.5, color=INK, bold=True, anchor=MSO_ANCHOR.TOP) txt(x + Inches(0.28), src_y + Inches(0.46), w - Inches(0.56), Inches(0.36), examples, size=12, color=MUTE, anchor=MSO_ANCHOR.TOP) pub_x, pub_w = Inches(0.9), Inches(5.6) sub_x, sub_w = Inches(6.83), Inches(5.6) pub_cx = pub_x + pub_w / 2 sub_cx = sub_x + sub_w / 2 source_box(pub_x, pub_w, "Publisher (nur publizieren)", "z. B. AB+M · LabAccess · MDN · Stammdaten — proprietär") source_box(sub_x, sub_w, "Subsysteme (konsumieren & publizieren)", "z. B. LIS · Geräte / Analyzer — proprietär") # ---- Pfeile Quellen -> Hot Store ------------------------------------------ ay, ah = src_y + src_h + Inches(0.02), Inches(0.30) shp(MSO_SHAPE.DOWN_ARROW, pub_cx - Inches(0.16), ay, Inches(0.32), ah, fill=CORAL) txt(pub_cx + Inches(0.28), ay - Inches(0.01), Inches(1.6), ah + Inches(0.02), "publizieren", size=11, color=LIGHT, italic=True) shp(MSO_SHAPE.UP_DOWN_ARROW, sub_cx - Inches(0.11), ay - Inches(0.05), Inches(0.22), ah + Inches(0.10), fill=CORAL) txt(sub_cx - Inches(2.85), ay - Inches(0.01), Inches(2.5), ah + Inches(0.02), "konsumieren & publizieren", size=11, color=LIGHT, italic=True, align=PP_ALIGN.RIGHT) # ---- HOT STORE (Kern) + Event-Prozessoren (Teil des Stores) ---------------- hx, hy, hw, hh = Inches(3.4), Inches(2.62), Inches(6.53), Inches(1.50) shp(MSO_SHAPE.ROUNDED_RECTANGLE, hx, hy, hw, hh, fill=CORAL, line=INK, lw=Pt(3.5), adj=0.07) core_tag(hx + Inches(0.16), hy + Inches(0.13)) txt(hx, hy + Inches(0.05), hw, Inches(0.34), "Event Store — Hot Store", size=20, color=INK, bold=True, align=PP_ALIGN.CENTER) txt(hx, hy + Inches(0.40), hw, Inches(0.20), "append-only · kanonische Wahrheit", size=12.5, color=INK, align=PP_ALIGN.CENTER) chip(CX, hy + Inches(0.62), "z. B. Kafka") # Event-Prozessoren als eingebetteter Bestandteil des Stores ep_x, ep_y, ep_w, ep_h = hx + Inches(0.28), hy + Inches(0.98), hw - Inches(0.56), Inches(0.44) shp(MSO_SHAPE.ROUNDED_RECTANGLE, ep_x, ep_y, ep_w, ep_h, fill=WHITE, adj=0.22) txt(ep_x, ep_y, ep_w, ep_h, "Event-Prozessoren (konsumieren & publizieren) · z. B. Proben-Routing", size=11.5, color=INK, bold=True, align=PP_ALIGN.CENTER) # ---- Hot -> Projektionen --------------------------------------------------- shp(MSO_SHAPE.DOWN_ARROW, CX - Inches(0.16), hy + hh + Inches(0.03), Inches(0.32), Inches(0.28), fill=CORAL) # ---- PROJEKTIONEN ---------------------------------------------------------- px, py, pw, ph = Inches(3.4), Inches(4.46), Inches(6.53), Inches(0.66) shp(MSO_SHAPE.ROUNDED_RECTANGLE, px, py, pw, ph, fill=WHITE, adj=0.14) txt(px + Inches(0.3), py, Inches(3.0), ph, "Projektionen", size=16, color=INK, bold=True) chip(px + pw - Inches(2.0), py + ph / 2 - Inches(0.15), "Kafka Streams → PostgreSQL") # ---- Verteiler (Projektionen -> 3 Bloecke) -------------------------------- boxes_y = Inches(5.34) bw = Inches(3.7); bh = Inches(1.56) cold_x = Inches(0.9); cold_cx = cold_x + bw / 2 mon_x = Inches(4.815); mon_cx = mon_x + bw / 2 rep_x = Inches(8.73); rep_cx = rep_x + bw / 2 bus_y = py + ph + Inches(0.10) shp(MSO_SHAPE.RECTANGLE, CX - Pt(0.75), py + ph, Pt(1.5), bus_y - (py + ph), fill=LIGHT) shp(MSO_SHAPE.RECTANGLE, cold_cx, bus_y, rep_cx - cold_cx, Pt(1.5), fill=LIGHT) for cx in (cold_cx, mon_cx, rep_cx): shp(MSO_SHAPE.DOWN_ARROW, cx - Inches(0.11), bus_y, Inches(0.22), boxes_y - bus_y, fill=LIGHT) def block(x, title, body, tech, core=False): fill = CORAL if core else WHITE line = INK if core else None lw = Pt(3.5) if core else Pt(1) shp(MSO_SHAPE.ROUNDED_RECTANGLE, x, boxes_y, bw, bh, fill=fill, line=line, lw=lw, adj=0.07) ty = boxes_y + (Inches(0.30) if core else Inches(0.20)) if core: core_tag(x + Inches(0.16), boxes_y + Inches(0.16)) txt(x, ty, bw, Inches(0.42), title, size=17, color=INK, bold=True, align=PP_ALIGN.CENTER) txt(x + Inches(0.25), ty + Inches(0.46), bw - Inches(0.5), Inches(0.5), body, size=12.5, color=(INK if core else MUTE), align=PP_ALIGN.CENTER) chip(x + bw / 2, boxes_y + bh - Inches(0.42), tech) block(cold_x, "Cold Store", "Archiv · Audit-Trail · Dokumente", "z. B. PostgreSQL", core=True) block(mon_x, "Monitoring", "Auftragsstatus / Auslastung", "z. B. Grafana") block(rep_x, "Reporting", "Bericht an Gruppe · KPIs", "z. B. Power BI") out = os.path.join(os.path.dirname(os.path.abspath(__file__)), "Folie5.pptx") prs.save(out) print("saved", out)