651 lines
36 KiB
TeX
651 lines
36 KiB
TeX
% !TEX encoding = UTF-8 Unicode
|
|
% ============================================================================
|
|
% AISE502 -- AI in Software Engineering II
|
|
% Lecture 5 slides, typeset with the official FHGR beamer theme
|
|
% (beamerthemeFHGR.sty, University of Applied Sciences of the Grisons).
|
|
% Slide content is unchanged; only the presentation layer is the FHGR template.
|
|
% ============================================================================
|
|
\documentclass[aspectratio=169]{beamer}
|
|
|
|
\usetheme[showsection, titlebg=pics/theme_pics/titlepage.png]{FHGR}
|
|
|
|
% ============================================
|
|
% PACKAGES
|
|
% The theme already loads tikz, graphicx, xcolor, tabularx, colortbl,
|
|
% listings, hyperref, environ and xparse -- only the extras are needed here.
|
|
% ============================================
|
|
\usepackage[british]{babel}
|
|
\usepackage{booktabs}
|
|
\usepackage{amsmath}
|
|
\usepackage{amssymb}
|
|
\usepackage{tcolorbox}
|
|
\usetikzlibrary{shapes.geometric, arrows.meta, positioning, fit, backgrounds, calc}
|
|
|
|
% ============================================
|
|
% SEMANTIC COLOURS, MAPPED ONTO THE FHGR PALETTE
|
|
% The names used throughout the slides are kept, so no slide text changes;
|
|
% they now resolve to the FHGR brand colours defined by the theme.
|
|
% ============================================
|
|
\colorlet{bankblue}{blue} % FHGR blue (4B92A4)
|
|
\colorlet{bankgreen}{green} % FHGR green (817E65)
|
|
\colorlet{bankred}{red} % FHGR red (C60219)
|
|
\colorlet{codegray}{gray} % FHGR gray (595959)
|
|
\colorlet{backcolour}{linen} % FHGR linen (E1D3B5)
|
|
\definecolor{aiviolet}{HTML}{6B4E71} % muted plum, kept distinct for the AI lens
|
|
|
|
% Attribution labels in English (theme default is German)
|
|
\renewcommand{\source}[1]{\par\hfill {\tiny\color{FHGRDeco} Source:\,\itshape #1}}
|
|
\renewcommand{\imagesource}[1]{\par\hfill {\tiny\color{FHGRDeco} Image source:\,\itshape #1}}
|
|
|
|
% ============================================
|
|
% CUSTOM TCOLORBOXES (same semantics as the script, FHGR colours)
|
|
% ============================================
|
|
\newtcolorbox{keypoint}{
|
|
colback=bankblue!7!white,
|
|
colframe=bankblue,
|
|
title=Key Concept,
|
|
fonttitle=\bfseries\small,
|
|
boxrule=0.8pt,
|
|
arc=2pt,
|
|
top=2pt, bottom=2pt, left=4pt, right=4pt
|
|
}
|
|
|
|
\newtcolorbox{examplebox}[1][]{
|
|
colback=bankgreen!10!white,
|
|
colframe=bankgreen,
|
|
title={Example: #1},
|
|
fonttitle=\bfseries\small,
|
|
boxrule=0.8pt,
|
|
arc=2pt,
|
|
top=2pt, bottom=2pt, left=4pt, right=4pt
|
|
}
|
|
|
|
\newtcolorbox{definitionbox}[1][]{
|
|
colback=linen!40!white,
|
|
colframe=camel!85!black,
|
|
title={Definition: #1},
|
|
fonttitle=\bfseries\small,
|
|
boxrule=0.8pt,
|
|
arc=2pt,
|
|
top=2pt, bottom=2pt, left=4pt, right=4pt
|
|
}
|
|
|
|
\newtcolorbox{thinkbox}{
|
|
colback=lightGray!35!white,
|
|
colframe=darkGray,
|
|
title=Discussion,
|
|
fonttitle=\bfseries\small,
|
|
boxrule=0.8pt,
|
|
arc=2pt,
|
|
top=2pt, bottom=2pt, left=4pt, right=4pt
|
|
}
|
|
|
|
\newtcolorbox{hinweisbox}{
|
|
colback=bankred!5!white,
|
|
colframe=bankred,
|
|
title=Important Note,
|
|
fonttitle=\bfseries\small,
|
|
boxrule=0.8pt,
|
|
arc=2pt,
|
|
top=2pt, bottom=2pt, left=4pt, right=4pt
|
|
}
|
|
|
|
\newtcolorbox{ailinse}[1][]{
|
|
colback=aiviolet!7!white,
|
|
colframe=aiviolet,
|
|
title={AI Lens: #1},
|
|
fonttitle=\bfseries\small,
|
|
boxrule=0.8pt,
|
|
arc=2pt,
|
|
top=2pt, bottom=2pt, left=4pt, right=4pt
|
|
}
|
|
|
|
\newtcolorbox{projektbox}{
|
|
colback=bankblue!4!white,
|
|
colframe=bankblue!70!black,
|
|
title=Project Link: Portfolio Intelligence Platform,
|
|
fonttitle=\bfseries\small,
|
|
boxrule=0.8pt,
|
|
arc=2pt,
|
|
top=2pt, bottom=2pt, left=4pt, right=4pt
|
|
}
|
|
|
|
% ============================================
|
|
% TITLE METADATA
|
|
% ============================================
|
|
\title[AI in Software Engineering II]{AISE502: AI in Software Engineering II}
|
|
\subtitle{Lecture 5: Patterns II -- Microservices and Event-Driven Architecture\\[0.4ex]{\small Script: Part II, Sections MS / EDA}}
|
|
\author{Dr.\ Florian Herzog}
|
|
\shortname{AISE502}
|
|
\fullname{Fachhochschule Graub\"unden, Chur -- Autumn Semester 2026}
|
|
|
|
\begin{document}
|
|
|
|
% ============================================
|
|
% TITLE SLIDE
|
|
% ============================================
|
|
\FHGRTitlePage
|
|
|
|
% ============================================
|
|
% AGENDA
|
|
% ============================================
|
|
\begin{frame}{Agenda}
|
|
\small
|
|
\begin{enumerate}\setlength\itemsep{1pt}
|
|
\item Recap: one quantum, three cuts -- today: many quanta
|
|
\item \textbf{MS} -- Microservices: what buys team independence, and what it costs
|
|
\item Sagas vs.\ ACID: why compensation is not rollback
|
|
\item \textbf{EDA} -- Event-driven architecture: what an intermediary gives and takes
|
|
\item Resilience primitives for distributed edges
|
|
\item This week's exercise: architecture study II
|
|
\end{enumerate}
|
|
\end{frame}
|
|
|
|
% ============================================
|
|
% RECAP
|
|
% ============================================
|
|
\section{Recap}
|
|
|
|
\begin{frame}{Recap: where we are in the catalogue}
|
|
\footnotesize
|
|
Last week -- three patterns, all \textbf{single-quantum}:
|
|
\begin{itemize}\setlength\itemsep{1pt}
|
|
\item \textbf{L}: technical layers -- cheapest entry, pays on every change axis
|
|
\item \textbf{MM}: the \emph{domain} cut at constant quantum count -- D7/D9 rise from $-$ to $+$
|
|
\item \textbf{HX}: a delta discipline -- $++$ on D7/D9/D12 for any host
|
|
\end{itemize}
|
|
|
|
\vspace{0.15cm}
|
|
\textbf{Today: the distributed half of the catalogue.} Both patterns multiply quanta, both buy their strengths with the same currency -- and both carry $--$ on D8:
|
|
\begin{itemize}\setlength\itemsep{1pt}
|
|
\item \textbf{MS} distributes \emph{by domain}: one quantum per business capability
|
|
\item \textbf{EDA} decouples \emph{in time}: an intermediary between producers and consumers
|
|
\end{itemize}
|
|
|
|
\vspace{0.15cm}
|
|
\begin{keypoint}
|
|
\footnotesize Keep last week's lens: every $++$ and $--$ below traces to a \textbf{tactic} the topology bundles or impedes -- and to the \textbf{quantum boundaries} in the figures.
|
|
\end{keypoint}
|
|
\end{frame}
|
|
|
|
% ============================================
|
|
% MS -- MICROSERVICES
|
|
% ============================================
|
|
\section{MS -- Microservices}
|
|
|
|
\begin{frame}{MS -- Microservices}
|
|
\emph{\textcolor{bankblue}{Thirty teams share one release train and step on each other with every deployment: what buys their independence -- and what does it cost?}}
|
|
|
|
\vspace{0.15cm}
|
|
\small This section derives both halves of the answer -- \textbf{and the cost half is the longer one.}
|
|
|
|
\vspace{0.15cm}
|
|
\begin{definitionbox}[Microservices (MS)]
|
|
\footnotesize A macro-structure of \textbf{independently deployable services}, each modelled around one business capability, each owning its persistent data exclusively (\emph{database per service}), communicating via lightweight protocols. \textbf{Every service is its own architecture quantum}: its own unit of deployment, scaling, and failure.
|
|
\end{definitionbox}
|
|
|
|
\vspace{0.15cm}
|
|
\footnotesize In the taxonomy: \textbf{domain partitioning, many quanta} -- the highest aggregate star score of all styles, and one star on overall cost and simplicity. Newman's one-sentence definition names the two load-bearing properties: \emph{``independently deployable services modelled around a business domain.''}
|
|
\end{frame}
|
|
|
|
\begin{frame}{MS -- topology}
|
|
\begin{center}
|
|
\resizebox{0.82\textwidth}{!}{%
|
|
\begin{tikzpicture}[
|
|
sysbox/.style={rectangle, draw, rounded corners=4pt, align=center, font=\small\sffamily, line width=0.8pt},
|
|
gateway/.style={sysbox, fill=bankblue!20, draw=bankblue, minimum width=4.6cm, minimum height=0.95cm, font=\small\sffamily\bfseries},
|
|
svc/.style={sysbox, fill=bankgreen!15, draw=bankgreen, minimum width=2.9cm, minimum height=1.0cm},
|
|
data/.style={sysbox, fill=gray!15, draw=gray!60!black, minimum width=2.9cm, minimum height=0.75cm, font=\footnotesize\sffamily},
|
|
arr/.style={-{Stealth[length=2.5mm]}, thick, gray!60!black},
|
|
saga/.style={-{Stealth[length=2.5mm]}, thick, bankred, dashed}
|
|
]
|
|
\node[gateway] (gw) at (0,2.6) {API gateway};
|
|
\node[svc] (s1) at (-4.2,0.6) {Catalogue\\service};
|
|
\node[svc] (s2) at (0,0.6) {Order\\service};
|
|
\node[svc] (s3) at (4.2,0.6) {Payment\\service};
|
|
\node[data, below=0.45cm of s1] (db1) {own database};
|
|
\node[data, below=0.45cm of s2] (db2) {own database};
|
|
\node[data, below=0.45cm of s3] (db3) {own database};
|
|
\draw[arr] (gw) -- (s1);
|
|
\draw[arr] (gw) -- (s2);
|
|
\draw[arr] (gw) -- (s3);
|
|
\draw[arr] (s1) -- (db1);
|
|
\draw[arr] (s2) -- (db2);
|
|
\draw[arr] (s3) -- (db3);
|
|
\draw[saga] (s2) -- node[above, font=\footnotesize\sffamily, text=bankred] {saga, no ACID} (s3);
|
|
\draw[dashed, gray!60!black, rounded corners=5pt] (-5.85,1.25) rectangle (-2.55,-1.75);
|
|
\draw[dashed, gray!60!black, rounded corners=5pt] (-1.65,1.25) rectangle (1.65,-1.75);
|
|
\draw[dashed, gray!60!black, rounded corners=5pt] (2.55,1.25) rectangle (5.85,-1.75);
|
|
\node[font=\footnotesize\sffamily, text=gray!60!black, align=center] at (0,-2.3)
|
|
{one quantum per service: own pipeline, own release, own blast radius, own database};
|
|
\end{tikzpicture}%
|
|
}
|
|
\end{center}
|
|
|
|
\vspace{0.1cm}
|
|
\small Cross-service consistency is bought with \textcolor{bankred}{\textbf{sagas instead of ACID transactions}} -- the structural reason for D4 $= --$.
|
|
\end{frame}
|
|
|
|
\begin{frame}{MS -- the problem it solves}
|
|
\footnotesize
|
|
\textbf{Organisational before technical.} By the mid-2000s, Amazon and Netflix had hundreds of teams contributing to shared deployables -- and a shared deployable means a shared \textbf{release train}: every team's change waits on every other team's; integration scope grows with the \emph{organisation}, not the change; one team's defect rolls back everyone's release. Past a certain size, \emph{the queue for the release train} -- not compute, not traffic -- binds how fast the company ships.
|
|
|
|
\vspace{0.1cm}
|
|
The answer: \textbf{make the team's unit of ownership the system's unit of deployment.} Cut along business capabilities, give each service exclusive data ownership and its own pipeline -- ``you build it, you run it.''
|
|
|
|
\vspace{0.1cm}
|
|
\begin{itemize}\setlength\itemsep{1pt}
|
|
\item strongest empirical support is organisational: in high performers, \textbf{deployment frequency scales linearly with team count} instead of collapsing under coordination (DORA)
|
|
\item the documented \emph{upper} bound: Uber, at $\sim 2{,}200$ critical services, re-introduced a second structuring level -- $\sim 70$ domains with gateways (DOMA) -- a convergence back towards macro-modules
|
|
\end{itemize}
|
|
\end{frame}
|
|
|
|
\begin{frame}{MS -- capability profile (column MS)}
|
|
\scriptsize
|
|
\renewcommand{\arraystretch}{0.84}%
|
|
\vspace{-0.1cm}
|
|
\begin{center}
|
|
\begin{tabular}{@{}p{2.9cm}cp{7.8cm}@{}}
|
|
\toprule
|
|
\textbf{Dimension} & \textbf{Rating} & \textbf{Structural reason} \\
|
|
\midrule
|
|
D1 Read scalability & $++$ & independently replicated, cacheable read paths per service \\
|
|
D2 Write scal.\ \& elasticity & $++$ & per-service horizontal scaling; fine-grained provisioning \\
|
|
D3 Latency \& predictability & $-$ & network hops, serialisation, tail amplification along synchronous chains \\
|
|
D4 Consistency \& integrity & $--$ & database-per-service: no ACID across a boundary \\
|
|
D5 Availability \& isolation & $++$ & bulkheads: one failing service is not a failing product \\
|
|
D6 Security \& auditability & $\circ$ & expanded attack surface, scattered audit trails vs.\ isolation \\
|
|
D7 Evolvability & $++$ & domain partitioning: a change lands inside one deployable service \\
|
|
D8 Simplicity \& TTM & $--$ & platform engineering before the first feature -- the premium \\
|
|
D9 Testability \& deployability & $+$ & per-service excellent; system-level verification shifts towards production \\
|
|
D10 Operating cost & $--$ & the premium is paid mostly as platform staffing \\
|
|
D11 Team scaling & $++$ & independent deployability: teams release without queueing \\
|
|
D12 AI integrability & $\circ$ & isolation helps, but synchronous chains multiply LLM latency and failure probability \\
|
|
\midrule
|
|
Native shape $S(p)$ & \multicolumn{2}{l}{interactive} \\
|
|
\bottomrule
|
|
\end{tabular}
|
|
\end{center}
|
|
\end{frame}
|
|
|
|
\begin{frame}{MS -- the catalogue's steepest trade, cell by cell}
|
|
\footnotesize
|
|
\begin{itemize}\setlength\itemsep{3pt}
|
|
\item \textbf{D11 $= ++$ is the real payoff.} Deployments per developer per day scale \emph{linearly} with team count in high-performing organisations -- teams stop queueing. \textbf{This, not raw traffic, is the requirement that legitimately forces the pattern.}
|
|
\item \textbf{D4 $= --$ is structural} (the method's negative example from week 3). A cross-service invariant (order $\to$ stock $\to$ ledger) becomes a saga -- and \emph{compensation is not rollback}: a rollback erases an intermediate state as if it never existed; a compensating action cannot, because \textbf{other services have already seen and acted on that state}. Every ``undo'' is a new business operation (cancel, restock, refund) with its own logic, tests, and failure modes. No implementation skill removes this cell -- it can only be \emph{relocated} by cutting boundaries so invariants live inside one service.
|
|
\item \textbf{D8/D10 $= --$: the premium is staffing.} Self-managed Kubernetes TCO runs roughly $3\times$ managed offerings, dominated by personnel.
|
|
\item \textbf{D9 $= +$ averages a genuine split}: per-service testing is excellent; \emph{system-level} verification shifts towards production -- consumer-driven contracts, canary releases, progressive delivery.
|
|
\end{itemize}
|
|
\end{frame}
|
|
|
|
\begin{frame}{MS -- software engineering implications}
|
|
\footnotesize
|
|
Omitting these does not produce a leaner variant of the pattern -- it produces a \textbf{broken} one:
|
|
|
|
\vspace{0.05cm}
|
|
\begin{columns}[T]
|
|
\begin{column}{0.48\textwidth}
|
|
\textcolor{bankblue}{\textbf{Build, test, deploy}}
|
|
\begin{itemize}\setlength\itemsep{1pt}
|
|
\item one pipeline and one contract-test suite \emph{per service}; \textbf{consumer-driven contracts}; canaries, \textbf{feature flags}, progressive delivery
|
|
\end{itemize}
|
|
\end{column}
|
|
\begin{column}{0.48\textwidth}
|
|
\textcolor{bankblue}{\textbf{Operations, maintenance, teams}}
|
|
\begin{itemize}\setlength\itemsep{1pt}
|
|
\item distributed tracing, per-service SLOs, \textbf{circuit breakers}, \textbf{bulkheads}
|
|
\item \textbf{cross-cutting change multiplies by service count} -- the Segment mechanism
|
|
\item stream-aligned teams $+$ a platform team: a \emph{precondition}
|
|
\end{itemize}
|
|
\end{column}
|
|
\end{columns}
|
|
|
|
\vspace{0.1cm}
|
|
\begin{hinweisbox}
|
|
\footnotesize \textbf{The distributed monolith} -- services that only build, test, and release together -- combines the costs of both worlds without the payoff. Measurable: \emph{lockstep release ratio}, cross-service change dispersion, synchronised version bumps.
|
|
\end{hinweisbox}
|
|
\end{frame}
|
|
|
|
\begin{frame}{MS -- Monzo and Segment: the homogeneity condition}
|
|
\begin{examplebox}[two documented cases bracket the viability conditions]
|
|
\footnotesize \textbf{Monzo} operates a licensed retail bank on $\sim 2{,}800$ microservices -- viable because the company enforces \textbf{extreme technological homogeneity}: one language (Go), one monorepo, shared infrastructure libraries, central migration automation that upgrades hundreds of services mechanically.
|
|
|
|
\vspace{0.1cm}
|
|
\textbf{Segment} cut its pipeline into $> 140$ services -- one per analytics destination, i.e.\ along \emph{instances of configuration} rather than domain seams -- and publicly reversed course in 2018: one shared-library change required over a hundred deployments; test and operations load crushed a small team.
|
|
\end{examplebox}
|
|
|
|
\vspace{0.2cm}
|
|
\begin{keypoint}
|
|
\footnotesize Read jointly: viability at scale depends on \textbf{where the boundaries run} (Maxim 4) and on paying the platform premium \emph{centrally} -- not on service count.
|
|
\end{keypoint}
|
|
\end{frame}
|
|
|
|
\begin{frame}{MS -- build it and study it, signals, anti-patterns}
|
|
\begin{examplebox}[Build it and study it -- microservices]
|
|
\footnotesize \textbf{Build.} FastAPI (Python, MIT): one small HTTP service with a generated OpenAPI contract per capability; Kubernetes underneath. Java: Spring Cloud (Apache-2.0).
|
|
|
|
\textbf{Study.} Google's Online Boutique ($\sim$20.6k stars, Apache-2.0): twelve polyglot services under \texttt{src/}, gRPC contracts in \texttt{protos/}, one deployment per service. Runs on kind/minikube via \texttt{skaffold run}: moderate to hard -- \emph{instructively} so, because the platform effort you feel \textbf{is} D8 $= --$. Alternative: \texttt{dotnet/eShop}. (Sock Shop, cited by older literature, was archived in 2023 -- the maintenance check in action.)
|
|
\end{examplebox}
|
|
|
|
\vspace{0.1cm}
|
|
\footnotesize
|
|
\textbf{Anti-patterns:} \emph{entity services} (cut around nouns -- alarm: fan-out per business transaction); \emph{grains of sand} (too small to own an invariant -- alarm: service count outgrowing team count).
|
|
|
|
\vspace{0.1cm}
|
|
\textcolor{bankgreen}{\textbf{Choose when}} many teams must deliver in parallel (D11 \emph{measurably} binding); subdomains have independent scaling/failure/compliance profiles; the organisation can staff a platform. \textcolor{bankred}{\textbf{Avoid when}} the premium exceeds the system's complexity; the domain is not yet stable enough to cut boundaries; or the motivation is fashion.
|
|
\end{frame}
|
|
|
|
\begin{frame}{MS -- AI lens and key concept}
|
|
\begin{ailinse}[Microservices and AI: the synchronous-chain trap]
|
|
\footnotesize D12 $= \circ$, and the ambivalence is precise. Per-service isolation is welcome for a fallible component -- an AI capability can be its own service with its own SLO. But an LLM call inside a \emph{synchronous} service chain multiplies seconds-scale latency and per-hop failure probability -- without constitutive stability patterns this is a \textbf{cascade design}. And token cost per request is a \emph{gateway} concern orthogonal to distribution: splitting a system into services does nothing to measure or cap it.
|
|
\end{ailinse}
|
|
|
|
\vspace{0.2cm}
|
|
\begin{keypoint}
|
|
\footnotesize Microservices are the \textbf{only pattern rated $++$ on team scaling (D11)} -- and everything else in the column is the bill: $--$ on consistency, simplicity, and cost are structural properties of many quanta, not implementation accidents. Adopt for \emph{measured organisational scale}, never for traffic alone -- traffic has cheaper answers.
|
|
\end{keypoint}
|
|
\end{frame}
|
|
|
|
% ============================================
|
|
% EDA -- EVENT-DRIVEN
|
|
% ============================================
|
|
\section{EDA -- Event-Driven Architecture}
|
|
|
|
\begin{frame}{EDA -- Event-driven architecture}
|
|
\emph{\textcolor{bankblue}{Tomorrow a new consumer needs every order event: can you attach it without touching the producer?}}
|
|
|
|
\vspace{0.15cm}
|
|
\small If the answer must be yes, an \textbf{intermediary} has to stand between the two -- and everything in this section follows from what that intermediary gives and takes.
|
|
|
|
\vspace{0.15cm}
|
|
\begin{definitionbox}[Event-driven architecture (EDA)]
|
|
\footnotesize A macro-structure of \textbf{asynchronously decoupled} event producers and consumers connected through a messaging substrate. Processing is a reaction to \emph{events} -- immutable records of facts -- rather than a response to synchronous calls; producers and consumers share knowledge of \textbf{event schemas, never of each other}.
|
|
\end{definitionbox}
|
|
|
|
\vspace{0.1cm}
|
|
\footnotesize Two topologies, and the distinction is load-bearing: the \textbf{broker topology} (decentralised event chains -- maximal scaling, no central workflow control, hard error recovery) and the \textbf{mediator topology} (central orchestrator dispatching via queues -- workflow visibility and recoverability, at the price of coupling and a potential bottleneck).
|
|
\end{frame}
|
|
|
|
\begin{frame}{EDA -- the two topologies}
|
|
\begin{center}
|
|
\resizebox{0.86\textwidth}{!}{%
|
|
\begin{tikzpicture}[
|
|
sysbox/.style={rectangle, draw, rounded corners=4pt, align=center, font=\footnotesize\sffamily, line width=0.8pt},
|
|
proc/.style={sysbox, fill=bankgreen!15, draw=bankgreen, minimum width=2.0cm, minimum height=0.8cm},
|
|
med/.style={sysbox, fill=bankblue!20, draw=bankblue, minimum width=2.2cm, minimum height=0.9cm, font=\footnotesize\sffamily\bfseries},
|
|
broker/.style={sysbox, fill=gray!15, draw=gray!60!black, minimum width=5.6cm, minimum height=0.7cm},
|
|
queue/.style={sysbox, fill=gray!15, draw=gray!60!black, minimum width=1.7cm, minimum height=0.55cm, font=\scriptsize\sffamily},
|
|
arr/.style={-{Stealth[length=2.5mm]}, thick, gray!60!black}
|
|
]
|
|
\node[proc] (prod) at (-4.2,2.2) {Producer};
|
|
\node[broker] (bk) at (-4.2,0.7) {event broker (topics)};
|
|
\node[proc] (c1) at (-5.9,-0.9) {Processor A};
|
|
\node[proc] (c2) at (-2.5,-0.9) {Processor B};
|
|
\node[proc] (c3) at (-4.2,-2.4) {Processor C};
|
|
\draw[arr] (prod) -- node[right, font=\scriptsize\sffamily] {event} (bk);
|
|
\draw[arr] (bk.south west)+(0.6,0) -- (c1.north);
|
|
\draw[arr] (bk.south east)+(-0.6,0) -- (c2.north);
|
|
\draw[arr] (c1.east) -- node[above, font=\scriptsize\sffamily] {derived event} (c3.west |- c1.east) -- (c3.north west);
|
|
\draw[arr] (c2.south) -- (c3.north east);
|
|
\node[font=\footnotesize\sffamily\bfseries, text=gray!60!black] at (-4.2,-3.3) {(a) broker topology};
|
|
\node[proc] (src) at (4.2,2.2) {Initiating event};
|
|
\node[med] (md) at (4.2,0.7) {Mediator};
|
|
\node[queue] (q1) at (2.6,-0.7) {queue};
|
|
\node[queue] (q2) at (5.8,-0.7) {queue};
|
|
\node[proc] (w1) at (2.6,-2.0) {Processor A};
|
|
\node[proc] (w2) at (5.8,-2.0) {Processor B};
|
|
\draw[arr] (src) -- (md);
|
|
\draw[arr] (md) -- (q1);
|
|
\draw[arr] (md) -- (q2);
|
|
\draw[arr] (q1) -- (w1);
|
|
\draw[arr] (q2) -- (w2);
|
|
\draw[arr, dashed] (w1.east) to[bend right=25] node[right, font=\scriptsize\sffamily] {status} (md.south);
|
|
\node[font=\footnotesize\sffamily\bfseries, text=gray!60!black] at (4.2,-3.3) {(b) mediator topology};
|
|
\end{tikzpicture}%
|
|
}
|
|
\end{center}
|
|
|
|
\vspace{0.05cm}
|
|
\footnotesize Four things commonly conflated under ``event-driven'' (Fowler): \textbf{event notification} $\cdot$ \textbf{event-carried state transfer} $\cdot$ \textbf{event sourcing} (the log \emph{is} the system of record -- LMAX: $6$M orders/s on one event-sourced JVM thread) $\cdot$ \textbf{CQRS} (separate write and read models).
|
|
\end{frame}
|
|
|
|
\begin{frame}{EDA -- the problem it solves}
|
|
\footnotesize
|
|
\textbf{Integration at scale.} Around 2010, LinkedIn's activity data -- page views, profile edits, connections -- was wanted by an ever-growing set of consumers: search indexes, recommenders, metrics warehouses, security monitoring. Point-to-point wiring grows roughly \textbf{quadratically} with the number of systems and breaks with every schema change.
|
|
|
|
\vspace{0.1cm}
|
|
LinkedIn's answer: put \textbf{one durable, partitioned, replayable log} in the middle and let every consumer read at its own pace -- the system that became \textbf{Apache Kafka}.
|
|
|
|
\vspace{0.1cm}
|
|
The general form recurs wherever the consumer set is \emph{open-ended}: the producer of a fact -- an order placed, a sensor fired, a light switched on -- \textbf{cannot know today who will need that fact tomorrow}. Home Assistant faces the domestic version: thousands of device integrations, none depending on any other, coordinated by one event bus.
|
|
|
|
\vspace{0.1cm}
|
|
\begin{examplebox}[Kafka at LinkedIn; the Uber real-time stack]
|
|
\footnotesize By 2019, LinkedIn's Kafka carried $> 7$ \emph{trillion} messages per day -- the existence proof for D1/D2 $= ++$. Uber composes Kafka $+$ Flink $+$ Pinot into an end-to-end event-driven stack -- the canonical EDA-plus-pipeline hybrid for the IoT class (C8).
|
|
\end{examplebox}
|
|
\end{frame}
|
|
|
|
\begin{frame}{EDA -- capability profile (column EDA)}
|
|
\scriptsize
|
|
\renewcommand{\arraystretch}{0.84}%
|
|
\vspace{-0.1cm}
|
|
\begin{center}
|
|
\begin{tabular}{@{}p{2.9cm}cp{7.8cm}@{}}
|
|
\toprule
|
|
\textbf{Dimension} & \textbf{Rating} & \textbf{Structural reason} \\
|
|
\midrule
|
|
D1 Read scalability & $++$ & consumers replicate and cache independently behind the broker \\
|
|
D2 Write scal.\ \& elasticity & $++$ & partitioned ingest with elastic, decoupled consumers \\
|
|
D3 Latency \& predictability & $+$ & async throughput excellent; request/response through events is not the mode \\
|
|
D4 Consistency \& integrity & $--$ & eventual consistency moves correctness into the design \\
|
|
D5 Availability \& isolation & $++$ & a slow or dead consumer does not stall producers \\
|
|
D6 Security \& auditability & $\circ$ & durable log supports audit; causal trails need correlation IDs \\
|
|
D7 Evolvability & $++$ & new consumers attach without touching producers \\
|
|
D8 Simplicity \& TTM & $--$ & eventuality and broker operations designed and staffed first \\
|
|
D9 Testability \& deployability & $-$ & non-deterministic event flows resist end-to-end testing \\
|
|
D10 Operating cost & $\circ$ & few quanta, no premium -- but the broker is a standing cost \\
|
|
D11 Team scaling & $+$ & teams per processor decouple well; the broker needs an owner \\
|
|
D12 AI integrability & $++$ & queues absorb LLM latency, limits, outages; log $=$ audit journal \\
|
|
\midrule
|
|
Native shape $S(p)$ & \multicolumn{2}{l}{stream / async} \\
|
|
\bottomrule
|
|
\end{tabular}
|
|
\end{center}
|
|
\end{frame}
|
|
|
|
\begin{frame}{EDA -- the same coin, two sides}
|
|
\footnotesize
|
|
\begin{itemize}\setlength\itemsep{3pt}
|
|
\item \textbf{D7 $= ++$: the decoupling is threefold.} In \emph{topology} (producers know the event schema, never the audience), in \emph{time} (a consumer may be down, slow, or not yet written when the event is published), and in \emph{organisation} (the team attaching a fraud detector needs no meeting with the team owning the order flow).
|
|
\item \textbf{D4 $= --$ is the same coin, seen from the other side.} Eventual consistency moves correctness \emph{out of the database and into the design}: read-your-writes anomalies, ordering across partitions, duplicate delivery are all \textbf{design obligations} -- and their testing bill is what keeps D8 at $--$ and D9 at $-$.
|
|
\item \textbf{D3 $= +$ refines the five stars}: throughput and responsiveness are excellent, but synchronous round trips \emph{through} asynchronous flows are not the pattern's mode.
|
|
\end{itemize}
|
|
|
|
\vspace{0.1cm}
|
|
\begin{keypoint}
|
|
\footnotesize EDA packages ``use an intermediary'' and ``introduce concurrency'' by construction -- and \emph{impedes} ``transactions'' by the same construction. \textbf{No broker product removes the trade.}
|
|
\end{keypoint}
|
|
\end{frame}
|
|
|
|
\begin{frame}{EDA -- software engineering implications}
|
|
\footnotesize
|
|
\begin{columns}[T]
|
|
\begin{column}{0.48\textwidth}
|
|
\textcolor{bankblue}{\textbf{Build and test}}
|
|
\begin{itemize}\setlength\itemsep{1pt}
|
|
\item the hard artefact is the \textbf{event schema}: schema registry $+$ compatibility rules (backward/forward/full) as pipeline gates; AsyncAPI in OpenAPI's role
|
|
\item contract tests per event type; consumer replays against recorded streams
|
|
\item \textbf{idempotency tests}: processing an event twice must equal processing it once -- under deliberate duplicate delivery
|
|
\end{itemize}
|
|
\end{column}
|
|
\begin{column}{0.48\textwidth}
|
|
\textcolor{bankblue}{\textbf{Operations and maintenance}}
|
|
\begin{itemize}\setlength\itemsep{1pt}
|
|
\item the RED metrics of this world: \textbf{consumer lag}, \textbf{dead-letter queues}, duplicate rates
|
|
\item debugging $=$ \textbf{correlation-ID tracing} across hops -- a core competency, not an advanced topic
|
|
\item additive evolution is the glory (attach a consumer); \emph{changing} an established schema is the most expensive operation
|
|
\end{itemize}
|
|
\end{column}
|
|
\end{columns}
|
|
\end{frame}
|
|
|
|
\begin{frame}{EDA -- build it and study it, anti-patterns}
|
|
\begin{examplebox}[Build it and study it -- event-driven]
|
|
\footnotesize \textbf{Build.} Apache Kafka (durable, partitioned, replayable log); RabbitMQ (classic AMQP routing). Python-first layer: FastStream (Apache-2.0) -- FastAPI-style producers/consumers with the broker behind a decorator.
|
|
|
|
\textbf{Study.} Home Assistant (\texttt{home-assistant/core}, $\sim$89k stars, Apache-2.0): genuinely event-driven Python -- \texttt{homeassistant/core.py} defines \texttt{Event} and \texttt{EventBus} with \texttt{async\_fire}, and \emph{every} state change flows through that one bus. Runs via \texttt{pip install homeassistant}; the codebase is huge, so \textbf{read exactly that one file} -- the whole pattern is in it.
|
|
\end{examplebox}
|
|
|
|
\vspace{0.1cm}
|
|
\footnotesize
|
|
\textbf{Anti-patterns with alarms:} \emph{event soup} (no workflow visibility -- alarm: share of events without correlation IDs; time to reconstruct one causal trail); \emph{events as disguised RPCs} (a producer blocking for a reply event -- synchronous coupling with extra steps); \emph{missing idempotency} (at-least-once $+$ non-idempotent consumers $=$ silent corruption); \emph{event sourcing as default} (the burden of proof lies with the auditability requirement).
|
|
\end{frame}
|
|
|
|
\begin{frame}{Discussion}
|
|
\begin{thinkbox}
|
|
\small D4 $= --$, and yet Part IV will rate EDA the \emph{primary} pattern for three application classes.
|
|
|
|
\vspace{0.15cm}
|
|
Which classes can afford to make correctness \emph{eventual} -- and what, precisely, is the \textbf{response measure} that would tell you your class cannot?
|
|
|
|
\vspace{0.15cm}
|
|
Formulate it as a six-part quality attribute scenario before looking at Part III.
|
|
\end{thinkbox}
|
|
\end{frame}
|
|
|
|
\begin{frame}{EDA -- AI lens and key concept}
|
|
\begin{ailinse}[Queues absorb what LLMs are worst at]
|
|
\footnotesize D12 $= ++$. The three operational pathologies of LLM components -- seconds-scale latency, rate limits, provider outages -- are \textbf{exactly what a queue absorbs}; asynchronous integration is the default for non-interactive AI work. Queues also create natural \emph{batching points}: batch APIs price roughly $50\,\%$ below synchronous calls. And the durable event log \textbf{doubles as the audit journal} that logging obligations for AI systems demand -- every prompt, every response, every agent step, replayable. The one thing EDA does not give: a synchronous answer -- where the user is waiting, the latency budget must be engineered explicitly.
|
|
\end{ailinse}
|
|
|
|
\vspace{0.2cm}
|
|
\begin{keypoint}
|
|
\footnotesize For your project this cell is load-bearing: the \textbf{event-driven edges} around the hexagonal modular monolith carry the AI job spine -- analysis requests queue asynchronously, and the event journal is the audit trail.
|
|
\end{keypoint}
|
|
\end{frame}
|
|
|
|
% ============================================
|
|
% RESILIENCE PRIMITIVES
|
|
% ============================================
|
|
\section{Resilience Primitives}
|
|
|
|
\begin{frame}{Resilience primitives for distributed edges}
|
|
\footnotesize
|
|
Distribution and asynchrony are bought with \textbf{explicit failure engineering} -- constitutive for MS, standard for EDA edges, and the toolkit of this week's exercise:
|
|
|
|
\vspace{0.05cm}
|
|
\begin{center}
|
|
\scriptsize
|
|
\renewcommand{\arraystretch}{0.95}%
|
|
\begin{tabular}{@{}p{2.6cm}p{5.6cm}p{4.6cm}@{}}
|
|
\toprule
|
|
\textbf{Primitive} & \textbf{What it does} & \textbf{Measured by} \\
|
|
\midrule
|
|
Timeout & bounds how long a caller waits -- no call may wait forever & timeout rate per dependency \\
|
|
Retry with backoff & re-attempts transient failures with growing pauses -- safe only on \emph{idempotent} operations & retry rate; success-after-retry \\
|
|
Circuit breaker & callers stop calling a dependency that keeps failing; probes recovery & breaker state transitions; open time \\
|
|
Bulkhead & partitions resources so one failing component cannot drain the rest & saturation per pool \\
|
|
Fallback / degradation & a defined degraded answer beats no answer & share of degraded responses \\
|
|
Dead-letter queue & parks messages that repeatedly fail processing for inspection & DLQ depth and age \\
|
|
Idempotent consumer & processing twice $=$ processing once -- makes retries safe & duplicate rate under replay \\
|
|
\bottomrule
|
|
\end{tabular}
|
|
\end{center}
|
|
|
|
\vspace{0.05cm}
|
|
Each is a \textbf{tactic} in the week-3 sense -- and each becomes a \emph{fitness function} in your measurement contract.
|
|
\end{frame}
|
|
|
|
% ============================================
|
|
% CLOSING
|
|
% ============================================
|
|
\section{Closing}
|
|
|
|
\begin{frame}{MS and EDA side by side}
|
|
\footnotesize
|
|
\begin{center}
|
|
\footnotesize
|
|
\begin{tabular}{@{}lccl@{}}
|
|
\toprule
|
|
& \textbf{MS} & \textbf{EDA} & \\
|
|
\midrule
|
|
D4 Consistency & $--$ & $--$ & both trade ACID away -- differently: sagas vs.\ eventuality \\
|
|
D5 Availability & $++$ & $++$ & bulkheads vs.\ temporal decoupling \\
|
|
D7 Evolvability & $++$ & $++$ & domain cut vs.\ attach-a-consumer \\
|
|
D8 Simplicity & $--$ & $--$ & platform premium vs.\ asynchrony bill \\
|
|
D11 Team scaling & $++$ & $+$ & the release train vs.\ the broker owner \\
|
|
D12 AI integrability & $\circ$ & $++$ & synchronous chains vs.\ absorbing queues \\
|
|
\bottomrule
|
|
\end{tabular}
|
|
\end{center}
|
|
|
|
\vspace{0.2cm}
|
|
\begin{itemize}\setlength\itemsep{2pt}
|
|
\item \textbf{MS distributes by domain}; \textbf{EDA decouples in time} -- and they compose: event-driven communication \emph{between} services is the standard hybrid
|
|
\item both demand the resilience primitives -- and both pay D8 $= --$ \emph{before the first feature ships}
|
|
\item for your project: neither carries the \emph{core} (the mini-match verdict stands) -- but \textbf{EDA carries the edges}
|
|
\end{itemize}
|
|
\end{frame}
|
|
|
|
\begin{frame}{This week's exercise: architecture study II}
|
|
\begin{projektbox}
|
|
\small
|
|
\begin{itemize}\setlength\itemsep{2pt}
|
|
\item \textbf{Design the edges} of your platform: the ingestion queue for analysis requests, and \textbf{resilience against external-API failure} -- timeouts, retries with backoff, circuit breaker, fallback, DLQ on every external call
|
|
\item \textbf{Sketch the service contracts}: which events exist, which schemas, who produces, who consumes
|
|
\item \textbf{Matrix pre-filter}: hold your A1 requirements profile against all candidate patterns seen so far -- which survive stage 1 (shape gate) and stage 2 (vetoes)?
|
|
\end{itemize}
|
|
\end{projektbox}
|
|
|
|
\vspace{0.25cm}
|
|
\small Next week you will run the \textbf{full three-stage match} and take the architecture decision -- the pre-filter is its preparation.
|
|
\end{frame}
|
|
|
|
\begin{frame}{Summary}
|
|
\footnotesize
|
|
\begin{enumerate}\setlength\itemsep{2pt}
|
|
\item \textbf{MS}: the only $++$ on team scaling -- adopt for \emph{measured organisational scale}, never for traffic; D4 $= --$ is structural (compensation $\neq$ rollback), D8/D10 $= --$ is staffing
|
|
\item The \textbf{distributed monolith} is the most common failure outcome -- and it is measurable (lockstep release ratio)
|
|
\item Monzo and Segment bracket the viability conditions: \emph{where the boundaries run} and \emph{central} platform investment -- not service count
|
|
\item \textbf{EDA}: threefold decoupling (topology, time, organisation) -- and D4/D8/D9 are the same coin's other side; no broker removes the trade
|
|
\item \textbf{Resilience primitives} (timeout, retry, circuit breaker, bulkhead, fallback, DLQ, idempotency) are constitutive, not hardening -- each one a fitness function
|
|
\item For the project: MM$+$HX core unchanged; \textbf{EDA takes the edges} -- queues absorb what LLMs are worst at
|
|
\end{enumerate}
|
|
\end{frame}
|
|
|
|
\begin{frame}{Next week}
|
|
\begin{columns}[T]
|
|
\begin{column}{0.55\textwidth}
|
|
\textcolor{bankblue}{\textbf{Lecture 6 -- Patterns III and your class}}
|
|
\begin{itemize}\small
|
|
\item \textbf{Pipes-and-filters} and \textbf{Serverless}
|
|
\item Stepping back: the quantum, partitioning beats distribution, the consolidated table
|
|
\item Part III: \textbf{C10 in depth} $+$ the C1/C2 mirror pair
|
|
\end{itemize}
|
|
\end{column}
|
|
\begin{column}{0.42\textwidth}
|
|
\textcolor{bankblue}{\textbf{Reading}}
|
|
\begin{itemize}\small
|
|
\item this week: Part II, MS / EDA
|
|
\item ahead: Part II, PF / SL $+$ closing; Part III, C10
|
|
\end{itemize}
|
|
|
|
\vspace{0.2cm}
|
|
\textcolor{bankblue}{\textbf{Exercise}}
|
|
\begin{itemize}\small
|
|
\item edges, resilience, contracts; matrix pre-filter
|
|
\end{itemize}
|
|
\end{column}
|
|
\end{columns}
|
|
\end{frame}
|
|
|
|
% ============================================
|
|
% END
|
|
% ============================================
|
|
\FHGRClosingPage[][{\color{white}\parbox{0.9\paperwidth}{\centering Thank you!\\[3ex]
|
|
{\normalsize\mdseries Dr.\ Florian Herzog\\[0.9ex]
|
|
Fachhochschule Graub\"unden, Chur\\[2.4ex]
|
|
{\small AISE502 -- AI in Software Engineering II}}}}]
|
|
|
|
\end{document}
|