1
0
Fork 0
mirror of https://github.com/TheTaz25/denis.ergin.git synced 2025-07-07 06:08:53 +00:00

Merge branch 'main' of github.com:TheTaz25/denis.ergin

This commit is contained in:
Denis Ergin 2025-05-28 13:52:34 +02:00
commit f38495eeaf
62 changed files with 2585 additions and 10 deletions

View file

@ -6,5 +6,6 @@
"landing",
"build",
"knowledge-base"
]
],
"typescript.tsdk": "node_modules/typescript/lib"
}

View file

@ -178,7 +178,7 @@ import NthBox from "./nth-box.astro"
</section>
<section class="apply-styles pseudo">
<p><code>(n+7)</code> <br>- Selektiert das 4. und alle darauffolgenden Elemente</p>
<p><code>(n+4)</code> <br>- Selektiert das 4. und alle darauffolgenden Elemente</p>
<div class="container nth nth-n+4">
<NthBox amount={5} />
</div>

View file

@ -0,0 +1,15 @@
---
import Title from './title.astro';
import Intro from './intro.astro';
import Setup from './setup.astro';
import Sql from './sql.astro';
import Usage from './usage.astro';
---
<div class="slides">
<Title />
<Intro />
<Setup />
<Sql />
<Usage />
</div>

View file

@ -0,0 +1,69 @@
<section>
<section>
<h2>Einführung</h2>
</section>
<section>
<p>Wenn wir (Nutzer)-Daten speichern wollen, legen wir in großen Projekten keine Dateien im Datei-System ab. Gründe hierfür sind:</p>
<ul>
<li>Ineffizienz</li>
<li>Sicherheit</li>
<li>"Instabilität"</li>
</ul>
</section>
<section>
<p>Hierfür gibt es Datenbanken. Sie sorgen dafür das die Daten optimiert und <em>sicherer</em> gespeichert werden.</p>
<p>Je nach Anwendungsfall gibt es auch weiter optimierte Datenbanken, die in einer Niche besonders gut sind.</p>
</section>
<section>
<h3>Relationale Datenbanken</h3>
</section>
<section>
<p>In einer Relationelen Datenbank werden Daten strukturiert abgelegt. Hierfür muss die "Form" der zu speichernden Daten vordefiniert sein.</p>
<p>Daten in Relationelen Datenbanken werden in Tabellen gespeichert. Tabellen können miteinander Verknüpft werden (sie haben eine relation zueinander).</p>
</section>
<section>
<h3>Nicht-Relationale Datenbanken</h3>
</section>
<section>
<p>Auch genannt "NoSQL"-Datenbanken, verfolgt den genau Gegenteiligen Ansatz einer relationelen Datenbank. Daten werden in einer strukturierten Art und Weise (z.B. JSON) abgelegt.</p>
<p>Das Ziel der abgelegten Daten ist hier aber dabei nicht relationel aufgebaut, alle notwendigen Informationen sollten eher in einem "Dokument" gespeichert sein.</p>
</section>
<section>
<h3>In-Memory Datenbanken</h3>
</section>
<section>
<p>Diese Art von Datenbanken sind spezieller Natur. Daten werden primär im Arbeitsspeicher abgelegt.</p>
<p>Die abgelegten Daten sind normalerweise in einer einfachen Datenstruktur (z.B. Strings) gespeichert.</p>
</section>
<section>
<h3>Weitere (speziellere) Datenbanken</h3>
</section>
<section>
<ol>
<li>Time-Series-Datenbanken</li>
<li>Graphdatenbanken</li>
<li>Objektdatenbanken</li>
<li>Geodatenbanken</li>
</ol>
</section>
<section>
<h3>SQLite</h3>
</section>
<section>
<p>In unserem kurzen Kurs versuchen wir es einfach und standardisiert zu halten.</p>
<p>Hierzu verwenden wir das vereinfachte Datenbank-System "SQLite".</p>
<p>SQLite ist eine relatione Datenbank. Die Inhalte werden dabei innerhalb einer Datenbank-Datei lokal gespeichert, eine eigene Datenbank-Applikation ist hierbei nicht notwendig, alle Transaktionen werden durch eine eigene Bibliothek gemanaged.</p>
</section>
</section>

View file

@ -0,0 +1,61 @@
<section>
<section>
<h2>SQLite &amp; NodeJS</h2>
</section>
<section>
<p><strong>Vorab anzumerken: Um die Nachfolgenden Code-Snippets auszuführen, ist NodeJS in der Mindestversion 22.5.0 notwendig.</strong> Seit dieser Version wurde die <em>libsql</em> in die JavaScript Engine miteingebaut.</p>
<p>Hiermit ist es möglich eine SQLite Datenbank zu bedienen, ohne auf externe Pakete zurückgreifen zu müssen.</p>
</section>
<section>
<h3>Setup</h3>
</section>
<section>
<p>Wir bleiben weiterhin mit fastify unterwegs. Hier verwenden wir das Plugin-System um die Datenbank im Web-Server zu nutzen.</p>
<p>Beginnen wir nun auch damit. Im existierenden Projekt legen wir eine db.ts im Plugin-Ordner an.</p>
</section>
<section>
<p>Initialer Aufbau:</p>
<pre class="ts"><code data-trim data-line-numbers="1-3|5-7|9-13" is:raw>
import fp from 'fastify-plugin';
import { DatabaseSync } from 'node:sqlite';
import path from 'node:path';
export default fp(async (fastify, opts) => {
// ToDo
})
declare module 'fastify' {
export interface FastifyInstance {
db: DatabaseSync,
}
}
</code></pre>
</section>
<section>
<p>Plugin-Implementierung</p>
<pre class="ts"><code data-trim data-line-numbers="2-3|6-10|12" is:raw>
export default fp(async (fastify, opts) => {
const p = path.join(process.cwd(), 'db', 'store.db');
const db = new DatabaseSync(p);
db.exec(`
CREATE TABLE IF NOT EXISTS test(
key INTEGER PRIMARY KEY,
value TEXT
) STRICT
`);
fastify.decorate('db', db);
});
</code></pre>
</section>
<section>
<p>Mit diesen Zeilen haben wir nun in allen Fastify-Umgebungen auf die Datenbank-Konstante zugreifen.</p>
</section>
</section>

View file

@ -0,0 +1,148 @@
<section>
<section>
<h2>Schnell-Kurs zu SQL</h2>
</section>
<section>
<p>Um im weiteren mit der Datenbank zu arbeiten, müssen wir kurz auf ein paar Basics mit SQL (Structured Query Language) eingehen.</p>
<p>Da wir hier mit SQLite arbeiten, müssen wir uns nicht um User-Management und Rechte kümmern, das haben wir hier nicht.</p>
</section>
<section>
<h3>Tabellen erstellen</h3>
</section>
<section>
<p>Um mit Tabellen und deren Daten zu arbeiten, müssen wir zuerst eine oder mehrere Tabellen anlegen, in der wir die Daten speichern wollen.</p>
<pre class="sql"><code data-trim data-line-numbers is:raw>
CREATE TABLE IF NOT EXISTS tasks (
id INTEGER PRIMARY KEY,
title TEXT NOT NULL,
completed INTEGER DEFAULT 0,
due_date TEXT
) STRICT
</code></pre>
</section>
<section>
<p>SQLite unterstützt nur 5 Daten-Typen:</p>
<ul>
<li>NULL</li>
<li>INTEGER</li>
<li>REAL</li>
<li>TEXT</li>
<li>BLOB</li>
</ul>
</section>
<section>
<p>
CREATE TABLE IF NOT EXISTS tasks
<br />
Erstelle die Tabelle <em>tasks</em>, falls diese noch nicht angelegt ist
</p>
<hr>
<p>
"id" - "INTEGER" "PRIMARY KEY"
<br />
"name" - "Datentype" "Primärschlüssel"
</p>
</section>
<section>
<p>In Datenbanken ist ein <em>PRIMARY KEY</em> notwendig um Einträge in der Tabelle eindeutig zu identifizieren.</p>
<p>Die Anweisung <strong>PRIMARY KEY</strong> zeigt an, dass diese Spalte eindeutig (UNIQUE) ist und das Datenbanksystem die Zahl automatisch hochzählen soll für jeden Eintrag.</p>
</section>
<section>
<p>Die Anweisung <strong>NOT NULL</strong> zeigt der Datenbank an, dass dieses Feld (Spalte) nicht leer sein darf. Leere Werte werden in relationalen Datenbanken als <strong>NULL</strong> definiert.</p>
</section>
<section>
<h3>Einträge speichern</h3>
</section>
<section>
<p>Die Tabelle ist nun angelegt, wir können also jetzt Daten darin speichern:</p>
<pre class="sql"><code data-trim data-line-numbers is:raw>
INSERT INTO tasks (title) VALUES
("Für Web-Engineering lernen");
</code></pre>
<p>Mit diesem Eintrag speichern wie eine neue Aufgabe in die neue Tabelle. Defaults werden automatisch gesetzt und der erste Eintrag erhält die id <strong>1</strong>.</p>
</section>
<section>
<h3>Einträge abfragen</h3>
</section>
<section>
<p>Die wahre Stärke von relationalen Datenbanken besteht in der <em>effizienten</em> Abfrage von Daten. Fragen wir zum Anfang <strong>alle</strong> in der befindlichen Tabelle abgelegten Aufgaben ab:</p>
<pre class="sql"><code data-trim data-line-numbers is:raw>
SELECT * FROM tasks;
</code></pre>
</section>
<section>
<p>Wenn wir nur einzelne <em>Spalten</em> abfragen wollen, können wir die Spalten-Namen anstatt der <strong>Wildcard *</strong> verwenden. Die Spalten-Liste muss Komma-Separiert sein.</p>
<pre class="sql"><code data-trim data-line-numbers>
SELECT title, completed FROM tasks;
</code></pre>
</section>
<section>
<p>Wenn wir bestimmte Einträge filtern wollen können wir dies auch erreichen:</p>
<pre class="sql"><code data-trim data-line-numbers is:raw>
SELECT * FROM tasks WHERE completed = 0;
SELECT * FROM tasks WHERE completed <> 1;
</code></pre>
</section>
<section>
<h3>Einträge aktualisieren</h3>
</section>
<section>
<p>Wir können unsere Aufgaben nun abhaken:</p>
<pre class="sql"><code data-trim data-line-numbers is:raw>
UPDATE tasks SET completed = 1 WHERE id = 1;
</code></pre>
</section>
<section>
<h3>Einträge löschen</h3>
</section>
<section>
<p>Zu guter letzt ist es auch möglich, Einträge aus einer Datenbank zu löschen</p>
<pre class="sql"><code data-trim data-line-numbers is:raw>
DELETE FROM tasks WHERE completed = 1;
</code></pre>
</section>
<section>
<h3>Relationen (JOINs)</h3>
</section>
<section>
<p>Eine der Stärken / Features von relationelen Datenbanken ist die Möglichkeit, Tabellen miteinander zu verknüpfen. So lassen sich komplexe Datenstrukturen stark vereinfachen. Besonders wenn es 1:n oder m:n Relationen gibt.</p>
<p>Wir können also Einträge einer Tabelle als Referenz in einer zweiten Tabelle verwenden.</p>
</section>
<section>
<p>Legen wir zuerst eine neue Tabelle an, die Nutzer abbilden soll:</p>
<pre class="sql"><code data-trim data-line-numbers is:raw>
CREATE TABLE IF NOT EXISTS user (
id INTEGER PRIMARY KEY,
name TEXT NOT NULL
) STRICT
</code></pre>
</section>
<section>
<p>Im zweiten Schritt müssen wir die existierende Tabelle um eine Spalte erweitern, die die Referenz zum Nutzer (genauer gesagt dessen id) speichern kann.</p>
<pre class="sql"><code data-trim data-line-numbers is:raw>
ALTER TABLE tasks ADD COLUMN
user_id INTEGER REFERENCES user(id);
</code></pre>
</section>
</section>

View file

@ -0,0 +1,3 @@
<section>
<h1>Kurzeinstieg in Datenbanken</h1>
</section>

View file

@ -0,0 +1,77 @@
<!-- Live Action! -->
<section>
<section>
<h2>Aufgabe!</h2>
</section>
<section>
<p>Lasst uns einen kleinen Finanz-Tracker bauen:</p>
<ul>
<li>Einnahmen tracken</li>
<li>Ausnahmen tracken</li>
<li>Kategorisierung von Transaktionen (z.B. Lebensversicherung, Lebensmittel, etc)</li>
<li>Accounts (z.B. Bank, Kreditkarte, etc)</li>
<li>"Transaktionspartner" (an wen ging die Zahlung?)</li>
</ul>
</section>
<section>
<p>Accounts "Managen"</p>
<pre class="sql"><code data-trim data-line-numbers>
CREATE TABLE accounts (
id INTEGER PRIMARY KEY,
name TEXT NOT NULL UNIQUE,
type TEXT NOT NULL
) STRICT
</code></pre>
</section>
<section>
<p>Kategorien bereitstellen</p>
<pre class="sql"><code data-trim data-line-numbers>
CREATE TABLE categories (
id INTEGER PRIMARY KEY,
name TEXT NOT NULL UNIQUE
) STRICT
</code></pre>
</section>
<section>
<p>Transaktionspartner</p>
<pre class="sql"><code data-trim data-line-numbers>
CREATE TABLE transactors (
id INTEGER PRIMARY KEY,
name TEXT NOT NULL
) STRICT
</code></pre>
</section>
<section>
<p>Zu guter letzt, die Transaktionstabelle selbst:</p>
<pre class="sql"><code data-trim data-line-numbers>
CREATE TABLE transactions (
id INTEGER PRIMARY KEY,
name TEXT NOT NULL,
date TEXT NOT NULL,
cat_id INTEGER REFERENCES categories(id),
acc_id INTEGER NOT NULL REFERENCES accounts(id),
tra_id INTEGER NOT NULL REFERENCES transactors(id),
value INTEGER
) STRICT
</code></pre>
</section>
</section>
<!-- Here are some simple applications that can help you learn SQL while building practical projects:
1. **To-Do List App**
- **Database Tables:** Tasks, Categories, Users
- **Features:** Add, update, and delete tasks; categorize tasks; assign deadlines; filter tasks by status.
3. **Personal Finance Tracker**
- **Database Tables:** Transactions, Categories, Accounts, Users
- **Features:** Record income/expenses; categorize transactions; view financial summaries and reports.
6. **Student Management System**
- **Database Tables:** Students, Courses, Grades, Instructors
- **Features:** Enroll students in courses; assign grades; generate reports of student performance.
-->

View file

@ -0,0 +1,142 @@
<section>
<section>
<h2>Wie machen wir den Code Produktionsreif?</h2>
</section>
<section>
<p>Wir haben nun unsere erste App geschrieben. Nun wollen wir die natürlich auch "öffentlich" Verfügbar haben. Das macht ja immerhin das Internet aus...</p>
<p>Wir schauen uns nun an, welche Schritte wir unternehmen <b>sollten</b>, um von lokal zu global zu wechseln.</p>
</section>
<section>
<p>Der erste Schritt besteht darin, den von uns geschrieben Code zu <em>optimieren</em>.</p>
</section>
<section>
<h3>Warum sollte ich meinen Code optimieren?</h3>
</section>
<section>
<p>Der <em>Rohe</em> Code den wir geschrieben haben ist, wenn wir das auf große Projekte projezieren, ineffizient.</p>
<p>Wir können den Code mittels Tools auf verschiedene Arten transformieren und effizienter gestalten.</p>
</section>
<section>
<p>Optimierungsmöglichkeiten:</p>
<ul>
<li>Minification</li>
<li>Tree-Shaking</li>
<li>Bundling</li>
<li>Compatibility</li>
</ul>
</section>
<section>
<h3>Minification</h3>
</section>
<section>
<p>Während der <strong>Minification</strong> wird der Code verkleinert. Dabei werden unnötige Whitespaces entfernt, und Variablennamen verkürzt.</p>
<p>Variablennamen werden so verkürzt, dass es nur noch kurze Buchstabenfolgen (a, b, c, d, usw...) sind.</p>
<p>Verkleinert das finale JavaScript deutlich und reduziert dadurch die Menge der Daten die übertragen werden müssen.</p>
</section>
<section>
<h3>Tree-Shaking</h3>
</section>
<section>
<p>Wenn wir externe Bibliotheken und Frameworks verwenden, nutzen wir in den meisten Fällen nicht alle Features des Pakets.</p>
<p>Während des Tree-Shaking werden ungenutzte Code-Schnippsel von externen Paketen entfernt, sodass ungenutzter Code erst gar nicht ausgeliefert wird.</p>
</section>
<section>
<h3>Bundling</h3>
</section>
<section>
<p>Der Übersicht wegen teilen wir unseren Code in mehrere Dateien auf.</p>
<p>Beim Bundling sollen diese zusammenhängenden JavaScript-Codeschnippsel möglichst so zusammengefasst werden, dass eine Website nur ein einziges File herunter laden muss.</p>
<p>Hier gibt es aber auch eine "Anforderung", dass solche Bundles eine bestimmte Dateigröße nicht überschreiten sollten.</p>
</section>
<section>
<h3>Compatibility</h3>
</section>
<section>
<p>Das ist eher ein Relikt aus alten Zeiten, als der Support für den Internet Explorer 11 noch "sichergestellt" werden musste.</p>
<p>Tools wie Babel stellt sicher, dass moderne Features aus neueren JavaScript-Versionen auch "funktional" in alten Browsern verwendet werden können.</p>
<p>Hierzu wurden sogenannte "Polyfills" verwendet. Code der moderne Features "nachmacht", ohne dass sich der Entwickler darum kümmern muss.</p>
</section>
<section>
<p>Das sind die wichtigsten Dinge, die passieren müssen, damit unser JavaScript-Code auf einer realen Website "ausgrollt" werden können.</p>
<p>Jetzt stellt man sich natürlich die Frage, wie man das alles erreichen kann. Dass sind immerhin keine "kleinen" Änderungen die man an seinem Code machen muss.</p>
<p>Das gute: Wir müssen eigentlich recht wenig machen. Tools wie Bundler und Co. erledigen die schwere arbeit für uns.</p>
</section>
<section>
<p>Ein solches Tool verwenden wir bereits: Vite</p>
<p>Wir haben unseren vergangenen (Frontend) Projekte mit Vite aufgesetzt. Und Vite macht für uns das alles, was oben beschrieben wurde.</p>
</section>
<section>
<p>Um unser Frontend für die Produktion optimiert zu bauen, ist auch nur ein einzlnes Kommando notwendig: <code>npm run build</code></p>
</section>
<section>
<h2>Ein kurzer Blick: Möglichkeiten um eine Website zu "rendern"</h2>
</section>
<section>
<p>Das was wir bisher gebaut haben ist eine <strong>Statische Website</strong>. Die Inhalte die Ausgespielt werden sind vollständig und es sind z.B. keine weiteren Requests notwendig, damit die Website funktionieren kann.</p>
<p>Auch meine (also diese) Website ist Statisch erzeugt: Alle Inhalte sind zum nach dem Build-Prozess vollständig uns es muss der Content nur noch auf einen Server geladen werden.</p>
</section>
<section>
<p>Aber was gibt es für Alternativen?</p>
<p>Im Allgemeinen gibt es 2 weitere "Möglichkeiten", wie eine Website funktionieren kann</p>
</section>
<section>
<h3>Single Page Applications</h3>
</section>
<section>
<p>Single Page Applications sind spezialisierte Websites, die größtenteils auf JavaScript basiertem Rendering aufgebaut sind (Beispiele sind: React, Vue, etc.)</p>
<p>Die Website besteht eigentlich nur aus einer kleinen HTML-Datei mit einem Script-Tag. Das Script sorgt dann dafür, dass Inhalte angezeigt werden und Interaktivität hergestellt wird.</p>
</section>
<section>
<p>Die angezeigten Inhalte sind dann meist dynamischer Natur. Das heißt, die Inhalte werden nachträglich über Requests aus einem Content-Server ausgespielt.</p>
<p>Konzepte wie <em>Routing</em> passiert dann auch vollständig im Browser/JavaScript. In einer Art und Weise wie es der Endnutzer nicht mehr mitbekommt.</p>
</section>
<section>
<h3>Server Side Rendering</h3>
</section>
<section>
<p>Server-Side-Rendering ist eigentlich ein sehr altes Konzept:</p>
<p>Das auszuliefernde HTML-Markup wird auf einem Server dynamisch bei der Abfrage generiert.</p>
<p>Eines dieser älteren Systeme ist PHP. Moderne Alternativen sind vor allem React (NextJS oder Remix), Vue (Nuxt), Svelte oder eines der vielen anderen Systeme die es mittlerweile gibt.</p>
</section>
<section>
<p>Auch hier gibt es nochmals 2 "Unter"-Wege: Ein Hybrides Setup oder Klassiches Server-Side-Rendering</p>
<p>Im klassischen Server-Side-Rendering wird bei einem Seitenwechsel ein neues HTML angefordert.</p>
<p>Im Hybriden Setup (z.B. NUXT), wird der initiale Inhalt auf dem Server vor-generiert. Sobald der Inhalt beim Nutzer im Browser liegt, "übernimmt" JavaScript alle weiteren Seiten (ähnlich zu einer Single-Page-Application)</p>
</section>
<section>
<p>Aber wieso das "Hin-und-Her" mit Server-Side-Rendering?</p>
<p>Wir (als Webentwickler oder Betreiber eures eigenen Shops) wollen natürlich auffindbar sein. Und das größte "Ding" um gefunden zu werden ist Google.</p>
<p>Und Google hat bestimmte Anforderungen um unsere Website besser zu "ranken". In der Webentwicklung spricht man bei diesen Anforderungen von "Search Engine Optimization".</p>
</section>
<section>
<p>Im großen und ganzen wollen wir simpel gesprochen unsere Inhalte bereits bei der Ankunft beim Nutzer im HTML-Markup haben und nicht erst nachträglich durch JavaScript generieren.</p>
<p>Das Thema Search-Engine-Optimization wird nochmals tiefer in Web-Engineering II (zumindest bei mir behandelt.)</p>
</section>
</section>

View file

@ -0,0 +1,97 @@
<section>
<section>
<h2>Docker</h2>
</section>
<section>
<p><strong>Was ist Docker?</strong></p>
<p>
Docker ist eine Plattform, mit der Anwendungen in isolierten Containern verpackt, verteilt und ausgeführt werden können. Diese Container enthalten alles, was die Anwendung zum Laufen braucht, wodurch sie unabhängig von der Umgebung funktioniert.
</p>
<p>-- ChatGPT</p>
</section>
<section>
<p>Docker ist zunächst eines: Eine Virtualisierungs-Software. Auf ihr können also im weitesten Sinne komplette Betriebssysteme in Isolation laufen.</p>
<p>Jede Applikation eines Anwendungs-System sollte möglichst allein laufen. Abhängigkeiten zu weiteren Applikationen (zum Beispiel Datenbanken), werden in einem isolierten Container ausgeführt.</p>
</section>
<section>
<p>Da wir nur einen statischen Webserver laufen lassen wollen, ist dies zunächst alles recht einfach.</p>
<p>Wir bauen innerhalb eines Docker-Containers unsere Website und stellen die gebaute Website innerhalb eines "sauberen" Containers zur Verfügung.</p>
<p>Wir schauen uns den Prozess Schritt für Schritt an.</p>
</section>
<section>
<h3>Eine Simple Konfiguration für Caddy</h3>
</section>
<section>
<p>Wir werden als Beispiel einen Caddy Web-Server verwenden um unsere Website verfügbar zu machen.</p>
<p>Die minimale Konfiguration für Caddy umfasst lediglich 3 Zeilen, wer an ein Beispiel mit nginx interessiert ist, findet diese auf meinem GitHub.</p>
</section>
<section>
<pre class="toml"><code data-trim data-line-numbers is:raw>
# Caddyfile
:80
root * /srv
file_server
</code></pre>
</section>
<section>
<h3>Anweisungen für den Bau eines Docker-Containers</h3>
</section>
<section>
<p>Wir müssen nun Docker eine Bauanleitung geben, wie wir vom Development-Code, zu einer gebauten Version und dann zu einem isolierten Web-Server (Caddy) kommen.</p>
<p>Dafür legen wir die Datei "Dockerfile" im root unseres Projektes an.</p>
</section>
<section>
<p>Wir können uns den Prozess in 2 Schritten vorstellen:</p>
<ol>
<li>Die Website bauen</li>
<li>Die gebaute Website in einem Caddy-File-Server ablegen</li>
</ol>
</section>
<section>
<pre class="docker"><code data-trim data-line-numbers>
FROM node:20-alpine AS base
COPY . /app
WORKDIR /app
FROM base AS build
RUN npm ci
RUN npm run build
</code></pre>
</section>
<section>
<pre class="docker"><code data-trim data-line-numbers>
FROM caddy:alpine
COPY --from=build /app/dist/ /srv/
COPY ./Caddyfile /etc/caddy/Caddyfile
</code></pre>
</section>
<section>
<p>Wenn wir das ganze lokal ausprobieren wollen, müssen wir einfach (bei laufender Docker-Engine) folgenden Befehl in der Kommandozeile ausführen:</p>
<p><code>docker build . -t my-website</code></p>
<p>Das veranlasst Docker zum Bau der Website anhand des angegebenen Dockerfile</p>
</section>
<section>
<p>Der gebaute Container ist kann dann lokal mit Docker ausgeführt werden:</p>
<p><code>docker run my-website -p 8080:80</code></p>
</section>
<section>
<p>Damit haben wir nun die Möglichkeit, die Website bei Anbietern wie Sevalla, Railway oder Fly zu deployen...</p>
</section>
</section>

View file

@ -0,0 +1,29 @@
<section>
<section>
<h2>Wie wir unseren Website "hosten"</h2>
</section>
<section>
<p>Wir haben nun unseren optimierten Code bei uns liegen, wie kommt also der Inhalt von dir zu mir?</p>
<p>Nun der Code der bei uns liegt, wird nicht dort bleiben. Wir müssen die Website in einem Web-Server ablegen und diesen Server korrekt konfigurieren.</p>
</section>
<section>
<p>Je nach Anwendungsfall gibt es hierfür verschiedene Optionen.</p>
<p>Da wir bis jetzt nur eine statische Website erstellt haben, ist ein einfacher Webserver, der Dateien "ausliefern" kann, vollkommen ausreichend.</p>
</section>
<section>
<p>Mögliche Server-Applikationen für einen statischen Web-Server:</p>
<ul>
<li>nginx ("Hoher" Konfigurationsaufwand, existiert bereits länger)</li>
<li>Caddy (Moderner mit weniger Konfigurationsaufwand)</li>
<li>Viele Weitere...</li>
</ul>
</section>
<section>
<p>Unser Ziel ist es, die fertig gebaute Website an einem für den Web-Server geeigneten Ort abzulegen, sodass die Dateien ausgeliefert werden können.</p>
<p>Wir werden unsere Arbeit aber nicht direkt auf unserem PC "laufen" lassen. Wir verwenden die Virtualisierungs-Software "Docker" um unsere Applikation lokal zu testen und später im Internet laufen zu lassen.</p>
</section>
</section>

View file

@ -0,0 +1,13 @@
---
import Title from "./title.astro";
import Build from "./build.astro";
import FileServer from "./file-server.astro";
import Docker from "./docker.astro";
---
<div class="slides">
<Title />
<Build />
<FileServer />
<Docker />
</div>

View file

@ -0,0 +1,4 @@
<section>
<h1>Vom Code zu "Production"</h1>
<p>Wie wir unsere Website der Öffentlichkeit zugänglich machen.</p>
</section>

View file

@ -110,7 +110,7 @@
</section>
<section>
<p>push &amp; pop</p>
<p>.push() &amp; .pop()</p>
<pre class="js"><code data-trim data-line-numbers is:raw>
const arr = [1, 2, 3];
arr.push(4);
@ -135,14 +135,14 @@
// 10
console.log(arr.reduce((acc, val) =&gt; {
acc += val;
return acc += val;
}, 0));
</code></pre>
<p>Kalkuliert einen Wert basierend aus den Elementen des Array. Das Array erwartet 2 Werte: eine Funktion in der ein Accumulator und der aktuelle Wert als Parameter übergeben wird und den nächsten Wert zurück geben soll. Und den initialen Wert das Accumulators.</p>
</section>
<section>
<p>some</p>
<p>.some()</p>
<pre class="js"><code data-trim data-line-numbers is:raw>
const arr = [1, 5, 10, 25];
@ -165,7 +165,6 @@
<p>Sortiert das Array "In Place" (Ursprüngliches Array wird modifiziert).</p>
<p>Hierzu führt <code>sort</code> eine Funktion aus, die mitgegeben wird. Anhand des Ergebnisses werden Elemente im Array umsortiert</p>
</section>
<section>
<ul>
<li>Ergebnis -1: Wert "a" ist kleiner als Wert "b"</li>

View file

@ -14,10 +14,12 @@
// Button erstellen
const newButton = document.createElement('button');
// wir können noch mehr mit dem button machen (klick-events, attribute, etc);
// wir können noch mehr mit dem button machen
// (klick-events, attribute, etc);
newButton.innerText = "Klick mich!";
// Wir können einem beliebigen Element nun den Button als Kind anhängen.
// Wir können einem beliebigen Element nun
// den Button als Kind anhängen.
document.body.appendChild(newButton);
</code></pre>
</section>

View file

@ -34,6 +34,9 @@
<section>
<p>In JavaScript ist es nicht möglich die Abarbeitung einer Funktion anzuhalten.</p>
<p>Im Event Loop wird darauf gewartet, dass eine Funktion komplett abschließt / abgearbeitet wird bevor die nächste Message aus der Queue geladen wird.</p>
</section>
<section>
<p>Halten wir in einer Funktion die weitere Abarbeitung auf, so Sorgen wir gleichzeitig dafür dass der Browser in dieser Zeit nicht weiter arbeiten kann.</p>
<p>Wir empfinden dass als Lag oder simples "Nicht-Antworten" der Website auf Nutzer-Interaktionen (wenn die Abarbeitung länger als ein paar Millisekunden benötigt)</p>
</section>

View file

@ -158,6 +158,11 @@
<li>Das Resultat der Funktion <code>readAsArrayBuffer</code> erhalten wir in unserer Funktion im Event. Dieses nehmen wir und konvertieren es in ein Uint8Array</li>
<li>In den Zeilen 5 bis 7 konvertieren wir die Informationen in Characters und wandeln diese in einen Base-64 kodierten String um</li>
<li>Darauffolgend entnehmen wir den Bild-Typen (JPEG)</li>
</ol>
</section>
<section>
<ol start="4">
<li>Wir fassen alle gesamelten Informationen in einen korrekt definierten String zur Bild-Darstellung zusammen</li>
<li>Zu guter letzt verwenden wir die Bildinformationen im <code>src</code>-Attribut des <code>img</code>-Elementes</li>
</ol>

View file

@ -0,0 +1,65 @@
<section>
<section>
<h2>Alpine Attribute</h2>
</section>
<section>
<h3>x-data</h3>
<p>Um ein HTML-Element als Alpine-Komponente zu initialisieren und Daten Reaktiv zu halten</p>
</section>
<section>
<h3>x-show</h3>
<p>Um HTML-Elemente abhängig einer Variable anzuzeigen</p>
</section>
<section>
<h3>x-on</h3>
<p>Um auf Nutzer-Interaktionen mit anderen Elementen zu reagieren</p>
</section>
<section>
<h3>x-text</h3>
<p>Um Text anzuzeigen</p>
</section>
<section>
<h3>x-html</h3>
<p>Render HTML-Strings ohne diese zu escapen</p>
</section>
<section>
<h3>x-bind</h3>
<p>Um andere HTML-Element Attribute an eine Variable zu binden (um damit Attribute zu setzen)</p>
</section>
<section>
<h3>x-model</h3>
<p>Um eine Variable gegen ein Input-Element zu binden</p>
</section>
<section>
<h3>x-for</h3>
<p>Zum iterativen Rendern von Listen</p>
</section>
<section>
<h3>x-transition</h3>
<p>Um Übergänge zu definieren</p>
</section>
<section>
<h3>x-effect</h3>
<p>Um Reaktiv auf Änderungen von Variablen zu reagieren</p>
</section>
<section>
<h3>x-ref</h3>
<p>Um HTML-Elemente an eine Variable zu binden um schneller damit interagieren zu können</p>
</section>
<section>
<h3>x-if</h3>
<p>Um HTML-Elemente anhand bestimmter Bedingungen zu mounten/unmounten</p>
</section>
</section>

View file

@ -0,0 +1,25 @@
<section>
<section>
<h2>Globale Funktionen</h2>
</section>
<section>
<p>Globale Funktionen in Alpine dienen als eine Art Convencience Layer für bereits existierende Funktionalität (um z.B. ein Data-Objekt in JS zu definieren)</p>
</section>
<section>
<h3>Alpine.data()</h3>
<p>Um ein wiederverwendbares x-data Objekt zu erschaffen</p>
</section>
<section>
<h3>Alpine.store()</h3>
<p>Bietet die Möglichkeit um einen globalen State zu definieren</p>
</section>
<section>
<h3>Alpine.bind()</h3>
<p>Bietet eine bequeme Möglichkeit ein wiederverwendbares x-bind Objekt zu erstellen.</p>
</section>
</section>

View file

@ -0,0 +1,18 @@
---
import Title from './title.astro';
import Introduction from './introduction.astro';
import SetupAndInstall from './setup-and-installation.astro';
import Attribute from './alpine-attributes.astro';
import Globals from './globals.astro';
import Magics from './magics.astro';
---
<div class="slides">
<Title />
<Introduction />
<SetupAndInstall />
<Attribute />
<Globals />
<Magics />
</div>

View file

@ -0,0 +1,17 @@
<section>
<section>
<p>Es wird nun Zeit, auf das nächste "Level" zu steigen, wenn es um die Web-Entwicklung geht</p>
<p>Wir haben nun alle notwendigen API's angesehen, um mit einer Website auf rudimentäre Art und Weise zu interagieren. Nun haben natürlich viele Leute viele Ideen gehabt, die sich in Bibliotheken und Frameworks manifestiert haben.</p>
<p>Das heißt für uns: Wir müssen das Rad nicht zum 100sten mal neu Erfinden.</p>
</section>
<section>
<p>Wir werden <strong>nicht</strong> jQuery verwenden. Dafür gibt es aber aus meiner Sicht eine gute und moderne Alternative ohne gleich mit einem Monster-Framework um die Ecke zu kommen: AlpineJS</p>
<p>AlpineJS ist <em>robustes und minimales tool um Verhalten direkt ins HTML zu stricken.</em> (laut eigener Aussage von AlpineJS)</p>
<p>Insgesamt gibt es 15 Attribute, 6 Eigenschaften und 2 globale Methoden die ALpine nach außen liefert.</p>
</section>
<section>
<p>Sehen wir uns das doch einfach mal genauer an</p>
</section>
</section>

View file

@ -0,0 +1,34 @@
<section>
<section>
<h2>"Magics" / Properties</h2>
</section>
<section>
<p>AlpineJS hat ein paar Variablen die man zu bestimmten Zeitpunkten nutzen kann. Diese sind generel mit einem "$" prefixed.</p>
</section>
<section>
<h3>$el</h3>
<p>Mit dieser Variable können wir auf das aktuelle Element zugreifen (und es modifizieren wenn wir wollen)</p>
</section>
<section>
<h3>$refs</h3>
<p>Mit der Variable <code>$refs</code> können wir auf alle Referenzen die mit <code>x-ref</code> angelegt worden sind, zugreifen.</p>
</section>
<section>
<h3>$store</h3>
<p><code>$store</code> gibt uns die Möglichkeit, auf alle globalen Stores (mithilfe deren Namens) zuzugreifen</p>
</section>
<section>
<h3>$watch</h3>
<p><code>$watch</code> ist eine Funktion die wir innerhalb von Alpine-Komponenten verwenden können um auf reaktiven State und dessen Änderung zu reagieren</p>
</section>
<section>
<h3>$dispatch</h3>
<p>Mithilfe von <code>$dispatch</code> können wir Custom Events versenden, die dann wiederum von Parent-Komponenten verarbeitet werden können</p>
</section>
</section>

View file

@ -0,0 +1,23 @@
<section>
<section>
<h2>Setup und Installation des neuen Projektes</h2>
</section>
<section>
<p>Da wir uns hier mit etwas komplett neuem befassen, legen wir auch ein neues Projekt auf.</p>
<p>Wir verwenden Vite, um das Grundprojekt zu initialisieren, und verwenden dann npm um AlpineJS zu installieren</p>
</section>
<section>
<pre class="sh"><code data-trim data-line-numbers>
mkdir alpine-test
cd alpine-test
npm init -y
npm install --save-dev alpinejs
</code></pre>
</section>
<section>
<p>Alpine importieren und starten</p>
</section>
</section>

View file

@ -0,0 +1,3 @@
<section>
<h1>Externe Bibliotheken in JavaScript</h1>
</section>

View file

@ -0,0 +1,141 @@
<section>
<section>
<h2>Bessere Webserver mit Fastify</h2>
</section>
<section>
<p>Wir haben in den vorangegenangenen Folien gesehen, wie man einen einfachen Web-Server in NodeJS erstellt.</p>
<p>Moderne Features wie Routing-Logik, Logging, Error-Handling und weitere Features waren nicht mit inbegriffen.</p>
<p>Wir wollen das auch nicht selber schreiben. Hierfür wurde das Rad unzählige male für verschiedenste Problemstellungen neu Entwickelt.</p>
</section>
<section>
<p>Eines dieser vielen Räder schauen wir uns genauer an: Fastify</p>
<p>Fastify ist ein modernes Web-Framework um seinen eigenen Web-Server zu bauen. Es bietet die vorher erwähnten Features und hat darüber hinaus noch mehr zu bieten.</p>
<p>Das wichtigste aus meiner Sicht: Eine solide TypeScript Unterstützung.</p>
</section>
<section>
<h3>Setup</h3>
</section>
<section>
<p>Fastify hat eine CLI die leider nicht sehr gut Dokumentiert ist, obwohl diese Optionen hat um ein Fastify-Projekt extrem schnell aufzusetzen.</p>
<p>Folgender Befehl erstellt ein Fastify-Projekt in einen neuen Order "my-new-project" inklusive TypeScript-Unterstützung</p>
<pre class="bash"><code data-trim data-line-numbers is:raw>
# Projekt generieren
npx fastify-cli generate my-new-project -- --lang=ts
# Ins Projekt wechseln und Dependencies installieren
cd my-new-project
npm install
# Dev-Server starten
npm run dev
</code></pre>
</section>
<section>
<p>Das Basis-Projekt sieht nach der Initialisierung folgendermaßen aus:</p>
<pre><code data-trim data-line-numbers>
src/
├── plugins/
│ ├── sensible.ts
│ └── support.ts
├── routes/
│ ├── example/
│ │ └── index.ts
│ └── root.ts
└── app.ts
test/
├── plugins/
│ └── ...
└── routes/
└── ...
</code></pre>
</section>
<section>
<h3>Routen</h3>
</section>
<section>
<p>Im Ordner <code>src/routes</code> befindet sich die gesamte Routen-Logik innerhalb von fastify.</p>
<p>Mit dem CLI-Setup wurde das Projekt so konfiguriert, dass die Ordner und Dateien später den aufrufbaren Pfaden für die API entsprechen.</p>
<p>Konkret bedeutet dies: Die Datei im Pfad <code>routes/example/index.ts</code> wird über den URL-Pfad <code>/example</code> ansprechbar sein.</p>
</section>
<section>
<p>Innerhalb der Dateien kann der Pfad aber nochmal <strong>erweitert</strong> werden.</p>
<pre class="ts"><code data-trim data-line-numbers="1|3|4-6" is:raw>
import { FastifyPluginAsync } from "fastify"
const example: FastifyPluginAsync = async (fastify, opts): Promise<void> => {
fastify.get('/', async function (request, reply) {
return 'this is an example'
})
}
export default example;
</code></pre>
</section>
<section>
<p>Fastify basiert stark auf einem Plug-And-Play basiertem Subsystem, um ein korrektes Typing zu haben, verwenden wir TypeScript-Types</p>
<p>"example" ist eine Variable die als Plugin-Funktion exportiert wird. Die Funktion nimmt das "globale" fastify-Objekt (und dessen konfiguration an). Zusätzlich erhalten wir weitere Optionen.</p>
</section>
<section>
<p>Innerhalb unseres Plugins haben wir dann die Möglichkeit (Unter)-Routen auf Basis des aktuellen Pfades zu erstellen.</p>
<p><code>fastify.get(...)</code> legt einen GET-Handler auf einer Route an. Die Konfiguration erfolgt im Aufruf der Funktion "get".</p>
<p>Der erste Parameter beschreibt die Pfad-Erweiterung (basierend auf dem Ordner im routes-Ordner).</p>
<p>Der zweite Paramter ist eine Callback-Funktion die aufgerufen wird, wenn die Route durch einen Client aufgerufen wird.</p>
</section>
<section>
<p>Innerhalb dieses Callbacks können wir dann unsere Routen-Logik implementieren.</p>
</section>
<section>
<h3>Plugins</h3>
</section>
<section>
<p>In dem generierten Projekt gibt es noch einen 2. Ordner mit dem Namen "plugins".</p>
<p>Die hier erstellten Dateien legen Funktionen in den fastify-Scope die wir später in den Route-Handlers wiederverwenden können.</p>
<p>Unter anderem können wir hiermit Datenbank-Verbindungen als Plugins bereitstellen die wir später dazu verwenden um Datenbankabfragen zu machen.</p>
</section>
<section>
<h3>Dynamische Routen</h3>
</section>
<section>
<p>Wenn Routen einen dynamischen Anteil haben (also ein Teil des Pfades ist dynamisch mit zum Beispiel einer ID die wir abfragen), lässt sich dies simpel darstellen.</p>
<pre class="ts"><code data-trim data-line-numbers is:raw>
fastify.get('/:name', async function (request, reply) {
return `Hello, ${request.params.name}!`;
});
</code></pre>
<p>Nun ist aber <code>request.params.name</code> Fehlerhaft.</p>
</section>
<section>
<p>Da wir mit TypeScript arbeiten, müssen wir der Funktion nun noch mitteilen, dass wir einen solchen Paramater erwarten:</p>
<pre class="ts"><code data-trim data-line-numbers is:raw>
type HelloNamedParams = {
name: string;
}
// ...
fastify.get<{
Params: HelloNamedParams;
}>('/:name', async function (request, reply) {
return `Hello, ${request.params.name}!`;
});
</code></pre>
</section>
<section>
<p>Mit denn weiteren Optionen <code>Body, Querystring und Headers</code> können wir auch die erwartete Form dieser Request-Bestandteile definieren.</p>
</section>
</section>

View file

@ -0,0 +1,13 @@
---
import Title from "./title.astro";
import Plain from "./plain.astro";
import Protocols from './protocols.astro';
import Fastify from "./fastify.astro";
---
<div class="slides">
<Title />
<Plain />
<Protocols />
<Fastify />
</div>

View file

@ -0,0 +1,116 @@
<section>
<section>
<p>Wir beginnen nun damit, einen (bzw. mehrere) Server mit JavaScript auf NodeJS zu implementieren.</p>
<p>NodeJS ist im Vergleich zur Umgebung im Browser anders aufgebaut. Wir haben zum Beispiel kein <code>document</code> auf das wir beispielsweise zugreifen könnten.</p>
</section>
<section>
<p>Dafür haben wir nun Zugriff auf die gesamte NodeJS-API um mit dem Betriebssystem zu interagieren (Dateizugriffe, Netzwerk-Ports öffnen und so weiter).</p>
<p>Wenn Node installiert ist, können wir eine JavaScript Datei mit <code>node name-der-datei.js</code> ausführen.</p>
</section>
<section>
<p>Legen wir nun eine erste Datei <code>server.js</code> an:</p>
<pre class="js"><code data-trim data-line-numbers="1|3-5" is:raw>
const http = require("node:http");
http.createServer(function(req, res) {
res.end();
}).listen(8080);
console.log("Server listening on port 8080!");
</code></pre>
</section>
<section>
<p>Funktionalitäten in NodeJS sind in <strong>Modulen</strong> geschrieben. Wir können auf diese jederzeit zugreifen.</p>
<p>Je nachdem wie das Projekt aufgebaut ist, müssen wir auf diese Module anders zugreifen.</p>
<p>In nicht näher konfigurierten Projekten müssen wir mittels <code>require("modulname")</code> auf diese Sachen zurgeifen.</p>
</section>
<section>
<p>In moderneren Projekten, benutzen wir die neuere Modul-Syntax die mittels den keywords <code>import something from 'modul';</code> aufgerufen wird.</p>
<p>Das aktuelle Setup macht nicht viel... Senden wir mal etwas an den Sender zurück!</p>
</section>
<section>
<p>Wir nutzen <code>res.write()</code> um Inhalte zum Client zurück zu senden</p>
<pre class="js"><code data-trim data-line-numbers="2" is:raw>
http.createServer(function(req, res) {
res.write("Hallo vom Server!");
res.end();
}).listen(8080);
</code></pre>
</section>
<section>
<h3>Request &amp; Response</h3>
</section>
<section>
<p>Wenn der Server startet, wird bei jedem Request zum Server die Callback-Funktion ausgeführt.</p>
<p>Diese Funktion erhält zum einen ein Request-Objekt und zum zweiten ein Response-Objekt.</p>
</section>
<section>
<p>Mit dem Request-Objekt können wir alle Informationen aus der vom Client gesendeten Anfrage extrahieren. Das Objekt ist (ähnlich zum Reponse-Objekt) recht komplex gestaltet.</p>
<p>Im Request finden wir die URL (den Pfad) über den wir die Anfrage erhalten haben, ggf. den Body, welche Methode, und mehr.</p>
</section>
<section>
<p>Das Response-Objekt beinhaltet Funktionalität um Daten zurück zu senden. Wichtig ist dabei zu wissen das wir mit einem NodeJS-Stream Objekt interagieren.</p>
<p>Streams sind Lesbar, Schreibbar oder beides. Sie dienen dazu mit verschiedenen Dingen zu interagieren (wie zum Beispiel Netzwerk-Streams, Interkation mit Dateien und mehr).</p>
</section>
<section>
<p>Für uns wichtig ist erstmal der Umstand, das wir in das response-Objekt mittels <code>.write()</code> Daten in den Stream hinein schreiben können, und dass wir mit <code>.end()</code> den Stream schließen können (und damit ultimativ den Request "beenden" oder schließen).</p>
</section>
<section>
<p>Geben wir ein paar Infos zum <em>testen</em> zurück.</p>
<pre class="js"><code data-trim data-line-numbers="3-4" is:raw>
http.createServer(function(req, res) {
res.write("Hallo vom Server!&lt;br /&gt;");
res.write(`Request unter dem Pfad ${req.url} aufgerufen.
Verwendete Methode: ${req.method}`);
res.end();
}).listen(8080);
</code></pre>
</section>
<section>
<p>Nun erhalten wir ein paar Informationen im Browser (Pfad + Methode). Aber offensichtlich denkt der Browser nicht, dass wir hier mit HTML arbeiten..</p>
<p>Wir arbeiten auch nicht wirklich mit HTML, aber wir können dem Browser mitteilen, dass das was wir versenden HTML ist und vom Browser entsprechend interpretiert werden soll.</p>
</section>
<section>
<pre class="js"><code data-trim data-line-numbers="2" is:raw>
http.createServer(function(req, res) {
res.writeHead(200, { 'Content-Type': 'text/html' });
res.write("Hallo vom Server!&lt;br /&gt;");
res.write(`Request unter dem Pfad ${req.url} aufgerufen.
Verwendete Methode: ${req.method}`);
res.end();
}).listen(8080);
</code></pre>
</section>
<section>
<p>Damit sollte der Browser die Empfangenen Daten als HTML erkennen und sein möglichstes Versuchen um dieses korrekt zu rendern (also ohne, dass wir extra den Kompletten Rahmen mitsenden müssten.)</p>
</section>
<section>
<p>Wenn wir nun mehr daraus machen wollen, müssten wir ein paar Dinge implementieren:</p>
<ul>
<li>Routing</li>
<li>Header-Logik</li>
<li>Middleware-Systeme</li>
<li>Und mehr...</li>
</ul>
</section>
<section>
<p>Aber bevor wir uns damit auseinander setzen, verwenden wir lieber bereits existierende Frameworks die uns all diesen Aufwand ersparen.</p>
<p>Bevor wir aber dazu kommen, sehen wir uns die darunter liegenden Protokolle an, die wir hier verwenden.</p>
</section>
</section>

View file

@ -0,0 +1,162 @@
<section>
<section>
<h2>Protokolle</h2>
</section>
<section>
<p>Wir betrachten nun in den Nachfolgenden Slides was passiert wenn wir eine URL im Browser angeben bis zu dem Zeitpunkt an dem wir alle Daten empfangen haben.</p>
</section>
<section>
<h3>DNS</h3>
</section>
<section>
<p>Wenn wir eine URL wie "google.de" haben, weiß unser PC nicht, wohin der Request gesendet werden muss. Dazu benötigt der PC die IP-Adresse des Ziels.</p>
<p>IP-Adressen sind heutzutage entweder in der Version 4 oder Version 6 vorhanden. Version 6 wurde primär dazu erfunden, weil es keine Verfügbaren IPv4 Adressen mehr gab.</p>
</section>
<section>
<p>Um einen Namen wie "google.de" in eine für den Computer / das Netzwerk interpretierbare Adresse umzuwandeln, frägt der Computer bei einem DNS-Server nach.</p>
<p>Der Namens-Server gibt uns eine IP-Adresse zu der dann der Request gesendet werden kann.</p>
</section>
<section>
<p>Die Info, unter welcher IP der Namensserver zu finden ist, erhalten wir durch unseren Internet-Service-Provider (Über den Router des Providers).</p>
<p>Es gibt ein paar Schritte die bei einem DNS-Lookup geschehen:</p>
</section>
<section>
<h4>Anfrage beim "Recursive Resolver"</h4>
</section>
<section>
<p>Die IP vom DNS-Server die wir "haben", ist die eines "DNS Recursive Resolvers". Dieser ist der "Einstiegspunkt" in der Suche eines Namenseintrages.</p>
<p>DNS-Server bestehen nicht nur aus einzelnen Servern sondern sind einer Verkettung von mehreren Namens-Servern die jeweils einen kleineren Teil des Namens-Raums kennen.</p>
</section>
<section>
<h4>Anfrage beim Root-Server</h4>
</section>
<section>
<p>Der DNS-Root-Server ist die erste Übersetzungs-Ebene im DNS-System.</p>
<p>Der Root-Server kennt dabei den "nächsten" Ansprechpartner (Server), der die Adresse kennt.</p>
<p>Im Normalfall ist dies ein Top-Level-DNS-Server</p>
</section>
<section>
<h4>Anfrage beim TLD-DNS-Server</h4>
</section>
<section>
<p>Der Recursive Resolver frägt nun beim TLD-DNS-Server nach der IP des gesuchten Ziels.</p>
<p>Überlicherweise erhält der Resolver die IP des <strong>Authorative Nameserver</strong>.</p>
</section>
<section>
<h4>Anfrage beim Authorative Nameserver</h4>
</section>
<section>
<p>Der Authorative Nameserver hat die Einträge der gesuchten Domains (Name zu IP-Mapping)</p>
<p>Der Recursive Resolver kann uns nun mit der gesuchten IP Antworten.</p>
</section>
<section>
<h3>http</h3>
</section>
<section>
<p>Jeder Gängige Web-Request wird über das HTTP-Protokol versendet.</p>
<p>Über die Jahre hat sich das HTTP-1 Protokol entwickelt (HTTP2 und HTTP3). Der Inhalt eines jeden Requests ist aber zunächst gleich/ähnlich.</p>
</section>
<section>
<p>Ein HTTP-Request besteht aus mehreren Informationen:</p>
<ol>
<li>Versions-Information (HTTP)</li>
<li>Ziel-URL</li>
<li>HTTP-Methode</li>
<li>Request-Header</li>
<li>(optionaler) Body</li>
</ol>
</section>
<section>
<h4>HTTP-Methode</h4>
</section>
<section>
<p>Die HTTP-Methode definiert die Aktion die wir auf einer URL (auch genannt Resource) ausführen wollen.</p>
<p>Der Browser sendet im ohne Information "by default" ein GET bei allen Requests</p>
</section>
<section>
<ul>
<li><strong>GET</strong> um Inhalte zu erhalten</li>
<li><strong>POST</strong> um Inhalte anzulegen</li>
<li><strong>PUT</strong> um Inhalte zu modifizieren</li>
<li><strong>DELETE</strong> um Inhalte zu löschen</li>
</ul>
</section>
<section>
<p>Es gibt noch weitere Methoden wie OPTIONS, aber diese werden seltener direkt in der Webentwicklung genutzt.</p>
</section>
<section>
<h4>Headers</h4>
</section>
<section>
<p>Header im HTTP-Request enthalten zusätzliche Informationen als Key-Value-Paare.</p>
<p>Header können entweder durch Code gesetzt werden (also durch den Entwickler) als auch durch den User-Agent (Web-Browser)</p>
</section>
<section>
<p>Beispielhafte Inhalte</p>
<ul>
<li><strong>accept</strong>: Welche Datentypen als Antwort akzeptiert werden</li>
<li><strong>Auhtorization</strong>: Optionale Angabe von Informationen zum verifizieren von Nutzern.</li>
<li><strong>User-Agent</strong>: Informationen zum User-Agent (Browser zum Beispiel)</li>
<li><strong>Cache-Control</strong>: Informationen wie Inhalte gecached werden sollen</li>
<li><strong>Cookie</strong></li>
</ul>
</section>
<section>
<h4>Body</h4>
</section>
<section>
<p>Im Body eines Requests stehen die zu übertragenden Informationen der jeweiligen Seite.</p>
<p>Der Body kann bei GET-Requests von der Sendenden Partei nicht gesetzt werden.</p>
<p>Im Body befindet sich bei einer Anfrage mittels Post zum Beispiel die Information für die zu anlegende Resource.</p>
</section>
<section>
<h4>HTTP-Responses</h4>
</section>
<section>
<p>Sobald / Während der Bearbeitung kann der Server in einer Response einen StatusCode festlegen, die dem Client mitteilen soll, wie der Status zur Bearbeitung der Anfrage ist.</p>
<p>Diese Responses sind eine Zahl im Bereich zwischen 100 und 599. Wobei jede "100-er" Kategorie einen anderen Allgemeinen Zustand definiert:</p>
</section>
<section>
<ol>
<li><strong>1xx</strong> Information</li>
<li><strong>2xx</strong> Success</li>
<li><strong>3xx</strong> Redirect</li>
<li><strong>4xx</strong> Client Error</li>
<li><strong>5xx</strong> Server Error</li>
</ol>
<p>Eine Ausführliche Erklärung (mit Katzen) ist <a href="https://http.cat/" rel="noopener noreferrer">hier</a> zu finden</p>
</section>
<!-- TODO: TCP/IP -->
<!-- <section>
<h3>TCP/UDP</h3>
</section> -->
</section>

View file

@ -0,0 +1,3 @@
<section>
<h1>Backend-Entwicklung mit NodeJS (und Fastify)</h1>
</section>

View file

@ -0,0 +1,45 @@
<section>
<section>
<h2>Erste Schritte mit TypeScript</h2>
</section>
<section>
<p>Versehen wir nun mal einfache Variablen mit einer Typisierung</p>
<pre class="ts"><code data-trim data-line-numbers>
const x: number = 5;
const y: string = "Hallo Welt!";
const isFalse: boolean = false;
</code></pre>
</section>
<section>
<p>In TypeScript erhalten Variablen einen Typ, indem nach dem Namen ein Doppelpunkt mit der Typ-Deklaration folgt.</p>
<p>Standard-Typen umfassen in der Regel <code>string, number, boolean, undefined, null</code>.</p>
</section>
<section>
<p>Bei der Interaktion mit den Variablen sollten nun Informationen zu den Variablen besser angezeigt werden.</p>
<p>Sehen wir uns nun das Beispiel mit einer String-Variable an.</p>
</section>
<section>
<p>Neben diesen normalen Datentypen, kennt TypeScript noch zwei weitere wichtige: <code>any</code> und <code>unknown</code>.</p>
</section>
<section>
<p>Sobald man <code>any</code> verwendet, gibt man dem TypeScript Type-Checker die Information, dass diese Variable nicht weiter beachtet werden soll. Für diese Variable wird also jegliches Type-Checking deaktiviert und wir erhalten keine Warnungen und Informationen mehr.</p>
<p>Als <i>sichere</i> alternative gibt es hierfür <code>unknown</code>. Hiermit teilen wir TypeScript mit, dass wir zum aktuellen Zeitpunkt nicht wissen, was das für ein Datentyp ist.</p>
</section>
<section>
<p>Wenn wir den Datentyp einer unknown Variable später eindeutig kennen, können wir diesen mit folgendem Code-Schnipsel konvertieren:</p>
<pre class="ts"><code data-trim data-line-numbers is:raw>
function x(y: unknown): number | undefined {
if (typeof y === 'number') {
return y as number;
}
return undefined;
}
</code></pre>
</section>
</section>

View file

@ -0,0 +1,29 @@
<section>
<section>
<h2>Funktionen</h2>
</section>
<section>
<p>Funktionen in TypeScript lassen sich einfach definieren. Wir verwenden dazu das Type-Keyword und geben eine Funktion ähnlich zu einer Standard-Arrow-Function an:</p>
<pre class="ts"><code data-trim data-line-numbers is:raw>
type MyFunction = () =&gt; void;
</code></pre>
</section>
<section>
<p>In den runden Klammern können wir die notwendigen Parameter für die Funktion definieren:</p>
</section>
<section>
<pre class="ts"><code data-trim data-line-numbers is:raw>
type MyFunction = (paramA: string, paramB: string) =&gt; void;
</code></pre>
</section>
<section>
<p>Der Rückgabe-Type wird nach dem "Fat-Arrow" definiert. <code>void</code> signalisiert, dass die Funktion keinen Wert zurückggibt.</p>
<pre class="ts"><code data-trim data-line-numbers is:raw>
type MyFunction = () =&gt; string;
</code></pre>
</section>
</section>

View file

@ -0,0 +1,46 @@
<section>
<section>
<h2>Generics</h2>
</section>
<section>
<p>Generics in TypeScript sind ein wichtiges Feature um Typen dynamisch festzulegen</p>
<p>Im Grunde geben wir mit einem Generic an, das der <code>type</code> von außen festgelegt oder durch Code-Analyse automatisch bestimmbar ist.</p>
</section>
<section>
<p>Fangen wir einfach an: Wir wollen den <code>type</code> einer Eigenschaft eines Objekts frei einstellen:</p>
<pre class="ts"><code data-trim data-line-numbers is:raw>
type MyObject&lt;T&gt; = {
value: T,
};
const x: MyObject&lt;number&gt; = {
value: 5,
};
const y: MyObject&lt;string&gt; = {
value: 'Hallo!',
};
</code></pre>
</section>
<section>
<p>Fangen wir einfach an</p>
<pre class="ts"><code data-trim data-line-numbers is:raw>
const convertToString = (thing): string =&gt; {
return thing.toString();
}
</code></pre>
<p>Nicht alles kann mit <code>toString</code> in etwas nützliches konvertiert werden (z.B. Objekte). Deshalb wollen wir nun nur "Sinnvolle" Typen zulassen</p>
</section>
<section>
<pre class="ts"><code data-trim data-line-numbers is:raw>
const convertToString = &lt;T extends
string | number | boolean | null | undefined
&gt;(thing: T): string =&gt; {
return thing.toString();
}
</code></pre>
</section>
</section>

View file

@ -0,0 +1,19 @@
---
import Title from "./title.astro";
import Introduction from "./introduction.astro";
import Installation from "./installation.astro";
import Basics from "./basics.astro";
import Objects from "./objects.astro";
import Generics from "./generics.astro";
import Functions from './functions.astro';
---
<div class="slides">
<Title />
<Introduction />
<Installation />
<Basics />
<Objects />
<Generics />
<Functions />
</div>

View file

@ -0,0 +1,30 @@
<section>
<section>
<h2>Projekt-Setup mit TS</h2>
</section>
<section>
<h3>Schnelles Setup mit Vite</h3>
</section>
<section>
<p>Wenn wir ein Projekt mit Vite aufsetzen, können wir aus den vielen Templates von Vite wählen. Viele "Vanilla"-Templates haben auch eine TS-Variante mit der unnötiges Setup vermieden werden kann.</p>
</section>
<section>
<pre class="sh"><code data-trim data-line-numbers>
npm create vite@latest my-first-ts-app
-- --template vanilla-ts
</code></pre>
</section>
<section>
<p>In dem nun generierten Projekt sehen wir nun die ersten Unterschiede: Javascript-Dateien gibt es hier nicht mehr und sind mit <code>.ts</code> abgekürzt. Das sind entsprechende TypeScript-Dateien.</p>
<p>TypeScript wird diese Dateien analysieren, und während der Build-Phase diese in JavaScript umwandeln, sodass dieser dann in nachgelagerten Schritten weiter prozessiert werden kann.</p>
</section>
<section>
<p>Euch wird wahrscheinlich eine tsconfig.json im Projekt aufgefallen sein.</p>
<p>Dies ist die Konfigurations-Datei für TypeScript. Hier sind alle Einstellungen für den TypeScript-Compiler und können den eigenen Anforderungen entsprechend angepasst werden.</p>
</section>
</section>

View file

@ -0,0 +1,32 @@
<section>
<section>
<h2>Was ist TypeScript?</h2>
</section>
<section>
<p>TypeScript stellt im weitesten Sinne eine Erweiterung zu klassischem JavaScript dar. Hierbei wird JavaScript um ein Type-System erweitert.</p>
<p>Die Types hierbei umfassen die normalen Datentypen wie <em>number</em>, <em>boolean</em>, oder <em>string</em>. Weitergehend können Objekte und Arrays mit Typen ausgestattet werden.</p>
</section>
<section>
<p>Ziel des ganzen ist es hierbei, JavaScript's fehleranfälliges Verhalten aufgrund der schlechten Typ-Information sicherer zu gestalten. TypeScript bietet bei der Entwicklung viele Vorteile wie z.B. Type-Hints, Error-Meldungen bei unbekannten Typen und mehr.</p>
</section>
<section>
<p>Da TypeScript nur eine Erweiterung darstellt, sind auch moderne Browser und JS-Parsende Systeme nicht in der Lage TypeScript zu verstehen. Für uns Entwickler heißt das konkret, dass wir unseren TypeScript (TS) Code vorher in JavaScript umwandeln müssen.</p>
<p>Eine Ausnahme die sich zu dieser Aussage "anbahnt" ist NodeJS: Seit Version 23.8.0 ist NodeJS in der Lage, die Typ-Informationen aus der TS-Datei zu "strippen" und den Code der dahinter liegt auszuführen.</p>
</section>
<section>
<p>Für Browser ist aber fürs erste weiterhin eine Konvertierung von TS -&gt; JS durchzuführen. Wann gängige Browser-Engines nachziehen ist noch nicht abzusehen.</p>
</section>
<section>
<p>Hierfür gibt es einen entsprechendes package auf npm: <code>typescript</code>.</p>
<p>TypeScript wurde durch Microsoft eingeführt und wird auch durch Microsoft ständig weiter entwickelt.</p>
</section>
<section>
<p>Das <code>typescript</code> package ist ein Compiler der TypeScript Code analysiert, Fehlermeldungen auf Typen generiert und den Code letzten Endes zu JavaScript konvertiert.</p>
</section>
</section>

View file

@ -0,0 +1,164 @@
<section>
<section>
<h2>Objekte</h2>
</section>
<section>
<p>In TS gibt es 2 Varianten Objekte zu Typisieren. Dabei definieren wir die Form bzw die Eigenschaften (Properties) innerhalb des Objektes.</p>
</section>
<section>
<h3>Interfaces</h3>
</section>
<section>
<p>Mit Interfaces definieren wir Objekte. Dabei können wir uns das <code>interface</code> noch am ehesten wie eine normale JavaScript-Klasse vorstellen, die später durch weitere Interfaces erweitert werden kann.</p>
<pre class="ts"><code data-trim data-line-numbers is:raw>
interface Point2D {
x: number,
y: number,
};
const location: Point2D = {
x: 5,
y: 10,
};
</code></pre>
</section>
<section>
<p>Ein Interface kann nun mit einem bestehenden Interface "erweitert" (extended) werden.</p>
<pre class="ts"><code data-trim data-line-numbers is:raw>
interface Point3D extends Point2D {
z: number,
};
const location: Point3D = {
x: 5,
y: 10,
z: 20,
};
</code></pre>
</section>
<section>
<h3>type</h3>
</section>
<section>
<p>Ein <code>type</code> dient ähnlich zum interface dazu, ein Objekt zu definieren, beschränkt sich aber nicht darauf.</p>
<pre class="ts"><code data-trim data-line-numbers is:raw>
type Point2D = {
x: number;
y: number;
};
// Mit dem "&" können wir mehrere types zusammenführen
type Point3D = {
z: number;
} & Point2D;
</code></pre>
</section>
<section>
<p>Wie gesagt beschränkt sich eine <code>type</code>-Definition nicht nur auf Objekte, wir können zum Beispiel einen String auf bestimmte Werte "restriktieren".</p>
</section>
<section>
<pre class="ts"><code data-trim data-line-numbers is:raw>
type Animal = "cat" | "dog" | "hamster";
const pet: Animal = "cat";
// Mischformen sind auch möglich
type Falsy = null | undefined | "" | 0 | false;
</code></pre>
</section>
<section>
<h3>Optionale Properties</h3>
</section>
<section>
<p>Manchmal benötigen wir einzelne Properties in einem Objekt nicht immer, also können wir diese auch weg lassen.</p>
<p>In TypeScript werden optionale Properties in einem Objekt mit einem Fragezeichen markiert:</p>
<pre class="ts"><code data-trim data-line-numbers is:raw>
type MyObject = {
required: boolean;
optional?: boolean;
};
</code></pre>
</section>
<section>
<h3>Record</h3>
</section>
<section>
<p>Der spezielle Type <code>Record</code>, ist ein generischer Type von TypeScript, der ein Objekt beschreiben kann.</p>
<p>Er nimmt 2 Parameter an: Einen <code>Keys</code>-Type und einen <code>Value</code>-Type.</p>
</section>
<section>
<pre class="ts"><code data-trim data-line-numbers is:raw>
type Keys = "cat" | "dog" | "hamster";
type Value = {
name: string;
age: number;
}
type Pets = Record&lt;Keys, Value&gt;;
</code></pre>
</section>
<section>
<p>Dies erzeugt folgenden Typ:</p>
<pre class="ts"><code data-trim data-line-numbers is:raw>
type Pets = {
cat: Value,
dog: Value,
hamster: Value
}
</code></pre>
<p>Jeder Key wird als Property mit dem angegebenen Value definiert!</p>
</section>
<section>
<h3>typeof</h3>
</section>
<section>
<p>Manchmal macht es Sinn, einen Datentypen (Interface oder Type) anhand eines bereits existierenden JavaScript-Objektes zu generieren.</p>
<p>Hierfür haben wir das keyword <code>typeof</code>. TypeScript wandelt bei der Verwendung eines JS-Ojbektes mit diesem keyword, in einen Datentypen um:</p>
</section>
<section>
<pre class="ts"><code data-trim data-line-numbers is:raw>
const myObject = {
key1: 'Hello',
key2: 'World',
};
const MyObject = typeof myObject;
/**
* {
* key1: string;
* key2: string;
* }
*/
</code></pre>
</section>
<section>
<h3>keyof</h3>
</section>
<section>
<p>Das keyword <code>keyof</code> ist ein weiterer nützliches Tool. Damit können wir die property-keys aus einem Type extrahieren:</p>
<pre class="ts"><code data-trim data-line-numbers is:raw>
type Keys = keyof MyObject;
// Keys => 'key1' | 'key2';
</code></pre>
</section>
</section>

View file

@ -0,0 +1,3 @@
<section>
<h1>Einführung in TypeScript</h1>
</section>

View file

@ -0,0 +1,27 @@
<section>
<section><h2>Einführung in den Lösungsprozess "Design Thinking"</h2></section>
<section>
<p>Design Thinking ist ein Prozess, der in letzten Jahren viel "Traktion" erhalten hat. Mit Design Thinking will man Probleme lösen und neue Ideen Entwickeln.</p>
<p>Eine Grundidee hierbei ist, dass Personen aus verschiedenen Bereichen in diesem Prozess teilnehmen. Damit sollen möglichst viele Perspektiven zu einer Problemstellung erreicht werden.</p>
</section>
<section>
<p>Nach Definition benötigt es drei Kernaspekte, um gelungene Lösungen und Innovationen zu entwickeln:</p>
<ol>
<li>Es soll ein Nutzen für den Menschen entstehen</li>
<li>Die Lösung muss technisch umsetzbar sein</li>
<li>Aus Wirtschaftlicher Sicht benötigt die Lösung eine "Marktfähigkeit"</li>
</ol>
</section>
<section>
<p>Im Verlauf der Vorlesung werden wir einen "6-Schritte-Ansatz" des Hasso-Plattner Instituts verwenden um einen Design-Thinking-Zyklus zu bestreiten.</p>
<p>Mit dieser Methodik trennen wir unser "Projekt" in 2 grobe Bereiche auf: Ein Problemraum und einen Lösungsraum. Jeder dieser Räume enthält drei Schritte mit denen wir ans Ziel kommen sollen.</p>
</section>
<section>
<p>Wichtig ist zu beachten, dass jeder Schritt zu einem anderen Schritt springen kann, basierend welche Informationen wir haben oder noch benötigen.</p>
<p>Design Thinking ist ein iterativer Prozess (analog zur klassischen Software Entwicklung).</p>
</section>
</section>

View file

@ -0,0 +1,51 @@
<section>
<section>
<h4>Sichtweise definieren</h4>
</section>
<section>
<p>In diesem dritten Schritt treffen wir eine erste "Entscheidung": Wir definieren unsere Sichtweise. Dazu haben wir verschiedene "Tools".</p>
<p>Mithilfe der Sichtweise werden wir eine "Design Challenge" definieren, die in der 2. Hälfte des Prozesses als Zielsetzung dient.</p>
</section>
<section>
<h5>Personas</h5>
</section>
<section>
<p>Personas sind eine möglichst akurate Beschreibung einer fiktiven Person, die ein möglicher Nutzer für unsere Applikation ist.</p>
<p>Vorgestellte Eigenschaften sollen ein gesamtheitliches Bild des Nutzers für alle im Projekt teilnehmenden Personen darstellen. Dazu gehören klassische Demografische Merkmale wie das Alter. Zusätzlicher gehören aber auch z.B. Zitat zur Persona die deren Wünsche und Anforderungen an das zu entwickelnde Produkt abbilden.</p>
</section>
<section>
<p>Eine Persona sollte einen relevanten Teil einer Nutzergruppe für unsere zu entwickelnde Applikation abbilden.</p>
<p>Entsprechend sollte es auch mehrere Personas geben. Für jede Relevante Nutzergruppe eine Persona.</p>
</section>
<section>
<h5>User-Journey-Map</h5>
</section>
<section>
<p>Mit unserer Entwickelten Persona können wir nun auf eine "Reise" gehen. Genauer gesagt auf eine Nutzer-Reise (User-Journey).</p>
<p>Mit den bisher gesammelten Informationen können wir einen kompletten Anwendungsfall definieren, den eine Person in der Theorie hat.</p>
</section>
<section>
<p>Dabei wird nicht nur ein Fokus auf die unmittelbare Nutzung unseres Produktes gelegt, sondern wie die Persona dazu gekommen ist, das Produkt zu nutzen, sowie wie es nach der erfolgreichen unmittelbaren Nutzung weiter geht (optional).</p>
<p>Zusätzliche Informationen in einer User-Journey-Map sind z.B. die empfundenen Emotionen der Persona während der Journey, sowie mögliche Kommentare die getätigt werden könnten.</p>
</section>
<section>
<p>Bevor wir uns mit dem Lösungsraum auseinander setzten, können wir nun diese "Position" nutzen, um eine kurze Re-Iteration durchzuführen. Wir wollen nochmal betrachten, welches Problem nun wirklich gelöst werden soll.</p>
<p>Gängigerweise wird wird hier eine Fragestellung erarbeitet, um eine Prüfbare Situation zu erschaffen. Eine Solche Frage könnte lauten:</p>
</section>
<section>
<p>
<em>
Wie kann ich meinen Studenten dabei helfen, sich bestmöglich mit dem Modul Web-Engineering I auseinander zu setzen, auch wenn sie keine Karriere in dieser "Niche" anstreben?
</em>
</p>
</section>
</section>

View file

@ -0,0 +1,121 @@
<section>
<section>
<h4>Ideen finden</h4>
</section>
<section>
<p>Nun fängt der womöglich <em>intensivste</em> Teil im Design Thinking an. Wir entwickeln Ideen um das vorangegangene Problem zu lösen (Design-Challenge).</p>
<p>In diesem Schritt teilen sich die Projektmitglieder in kleinere Gruppen ein, Ziel ist es verschiedene Lösungen für das gleiche Problem zu erarbeiten.</p>
</section>
<section>
<p>Es werden verschiedene Methoden und Theorien verwendet um auf eine oder mehrere Lösungen zu kommen. Auch in diesem Abschnitt des Design Thinkings wird iterativ gearbeitet um Ideen und Lösungen zu verfeinern.</p>
<p>Je nachdem, was für ein Problem wir lösen, haben wir aus unserem UX-Koffer verschiedene Ansätze</p>
</section>
<section>
<p>Als Menschen haben wir einen "Bausatz" wie wir die Welt um uns herum wahrnehmen und verarbeiten. Die Psychologie hilft uns dabei diesen Bausatz zu verstehen.</p>
<p>Als Designer können wir dieses Wissen verwenden um intuitivere, Menschenzentrierte Produkte und Erfahrungen zu kreieren.</p>
<p>Anstatt einen Nutzer ein neues Verhalten aufzuzwingen, können wir Prinzipien der Psychologie dazu verwenden eine Unterbewusste "Führung" zu erstellen, die besser auf unseren Nutzer anspricht.</p>
</section>
<section>
<p>Nachfolgend möchte ich euch ein paar dieser Theorien vorstellen.</p>
</section>
<!-- --- -->
<section>
<h5>Jakob's Law</h5>
<p><em>Nutzer verbringen einen großteil der Zeit auf anderen Platformen. Der Nutzer tendiert also dazu UI-Paradigmen aus anderen Platformen zu bevorzugen, die er bereits kennt.</em></p>
</section>
<section>
<p><strong>Erwartung</strong></p>
<p>Die Nutzer werden ihre Erwartungen, die sie an ein vertrautes Produkt geknüpft haben, auf ein anderes übertragen, das ähnlich aussieht.</p>
</section>
<section>
<p><strong>Unstimmigkeiten minimieren</strong></p>
<p>Bei Änderungen sollte den Nutzern eine gewisse Zeit lang eine (alt)-bekannte Version zur Verfügung gestellt werden um Unstimmigkeiten bei großen Änderungen zu minimieren.</p>
</section>
<!-- --- -->
<section>
<h5>Parkinson's Law</h5>
<p><em>Jede Aufgabe "bläht" sich solange auf, bis die Verfügbare Zeit aufgebraucht ist.</em></p>
</section>
<section>
<p><strong>Zeitlimits</strong></p>
<p>Wir sollte die Zeit limitieren die es braucht eine Aufgabe zu beenden, die ein Nutzer erwarten würde.</p>
</section>
<section>
<p><strong>Dauer</strong></p>
<p>Wenn wir die tatsächliche Zeit um eine Aufgabe abzuschließen reduzieren, wird dies positiv aufgenommen.</p>
</section>
<section>
<p><strong>Autofill</strong></p>
<p>Funktionen wie "Autofill", die die Eingabezeit bei notwendigen Eingaben deutlich Reduzieren. Dies sorgt für eine schnellere Bearbeitung von Aufgaben wie der Kauf von Online-Produkten.</p>
</section>
<!-- --- -->
<section>
<h5>Stroop Effect</h5>
<p>Die Unstimmigkeit, die ensteht, wenn wir versuchen zwei widersprüchliche Attribute miteinander zu verknüpfen.</p>
</section>
<section>
<p><strong>Reaktionszeit</strong></p>
<p>Unsere Reaktionszeit verzögert sich, wenn wir mental versuchen Informationen zu verarbeiten, die in wiederspruch zueinander stehen.</p>
<p>Beispiele: (Grüner Himmel, Lila Gras, Rote Zitrone, Blauer Apfel)</p>
</section>
<section>
<p><strong>Kontext berücksichtigen</strong></p>
<p>Das Design von Interaktiven Elementen sollte im Kontext Sinn machen. Als Beispiel sollte eine "Jetzt Kaufen" Button auf einer Website nicht das gleiche Design wie ein "Abbrechen" Button aufweisen.</p>
</section>
<!-- --- -->
<section>
<h5>Hick's Law</h5>
<p>Die Zeit, die wir benötigen um eine Entscheidung zu treffen, erhöht sich mit der Anzahl und Komplexität der Auswahlmöglichkeiten.</p>
</section>
<section>
<p><strong>Auswahl Reduzieren</strong></p>
<p>Durch die Verringerung einer Auswahl, in Situationen in denen eine schnelle Entscheidung notwendig ist, reduziert die Entscheidungszeit.</p>
</section>
<section>
<p><strong>Kleinere Schritte</strong></p>
<p>Durch die Aufteilung von komplizierten Aufgaben in kleinere Schritte, können wie den mentalen Aufwand reduzieren.</p>
</section>
<section>
<p><strong>Empfehlungen</strong></p>
<p>Durch die Platzierung von Empfehlungen können wir eine Überforderung bei Nutzern vorbeugen.</p>
</section>
<!-- --- -->
<section>
<h5>Das Prinzip des geringsten Aufwandes</h5>
<p>Menschen tendieren dazu den Pfad auszuwählen, der den geringsten Mentalen und / oder Physischen Aufwand benötigt, um abgeschlossen zu werden.</p>
</section>
<section>
<p><strong>Zeigen, nicht erzählen</strong></p>
<p>Wenn es die notwendigkeit gibt, dem Nutzer etwas zu erklären, dann ist es besser konkrete Beispiele mit Erklärungen zu verwenden, anstatt es nur zu erklären.</p>
</section>
<section>
<p><strong>Schrittweise Erweiterung</strong></p>
<p>Es ist besser Inhalte Stück für Stück anzuzeigen, anstatt den gesamten Inhalt auf einmal zu präsentieren. Außerdem ist es gut, den Nutzern die Möglichkeit zu geben ob sie weitere Inhalte sehen wollen oder nicht.</p>
</section>
</section>

View file

@ -0,0 +1,98 @@
<section>
<section>
<h4>Beobachten</h4>
</section>
<section>
<p>Um unser Problem besser zu verstehen und um unsere Hypothesen zu bestätigen (oder zu widerlegen) müssen wir uns "harte Fakten" beschaffen.</p>
<p>Vorrangiges Ziel ist es auf Nutzer:innen und Betroffene zuzugehen und mit diesen zu sprechen. Dabei wollen wir auch auf empathischer Ebene ein Verständnis für den Nutzer und dessen Problem entwicklen.</p>
</section>
<section>
<p>Eine Aussage die im Laufe dieses Schritt entstehen soll ist, ob das Problem das zuvor benannt wurde, auch tatsächlich existiert.</p>
<p>Je nach Ergebnis ist es möglich kleinere Korrekturen an den Annahmen zu treffen, oder diese komplett zu überarbeiten.</p>
</section>
<section>
<h5>Nutzer-Interviews</h5>
</section>
<section>
<p>Interviews lassen sich in 3 Kategorien einteilen:</p>
<ol>
<li>Strukturiert</li>
<li>Semi-Strukturiert</li>
<li>Nicht-Strukturiert</li>
</ol>
</section>
<section>
<p>Mit Interviews wollen wir Daten generieren. Diese wollen wir interpretieren und daraus lernen.</p>
<p>Über Interviews haben wir die Möglichkeit Informationen folgender Art abzufragen:</p>
</section>
<section>
<ul>
<li>Gegenwärtige Fragen: Fragestellung zur Lösung eines Problems im gegenwärtigen Kontext</li>
<li>Vergangene Fragen: Wie sich die der Prozess zur Lösung eines Problems in der Zeit gewandelt hat</li>
<li>Zukünftige Fragen: Was sich der Nutzer zur Lösung eines Problems in der Zukunft vorstellen kann.</li>
</ul>
</section>
<section>
<p>Natürlich haben wir zu jedem Zeitpunkt auch die Möglichkeit andere oder vertiefende Fragen zu stellen:</p>
<ul>
<li>Pro's und Contra's zur aktuellen Lösung</li>
<li>"Haben Sie das immer schon so gemacht?"</li>
<li>Besondere Merkmale die dem Nutzer in den Sinn kommen</li>
<li>Fragen zur Findung des Lösungsweges</li>
<li>etc.</li>
</ul>
</section>
<section>
<h5>Fragebögen</h5>
</section>
<section>
<p>Während Interviews eine größtenteils Qualitative Informations-Generierung sind, sind Fragebögen vornehmlich auf quantitative Informationsgewinnung ausgelegt.</p>
<p>Wir haben hierbei auch die Problematik, dass wir nicht dynamisch auf Antworten eingehen können, sondern den Nutzer eher "durchleiten".</p>
<p>Auch ist es wichtig, dass Fragebögen nicht zu "viel" abfragen. Da ein Nutzer in einem Fragebogen jederzeit die Möglichkeit hat, diesen auch abzubrechen falls "es ihn langweilt" (eine Option).</p>
</section>
<section>
<p>Eine Option für Fragebögen ist die Verwendung von "Aussagebewertungen auf einer vorgegebenen Skala"</p>
<p>Wir können damit <i>relativ</i> genau Informationen Abfragen ohne dass sich der Nutzer des Fragebogens zu viele Gedanken machen muss.</p>
</section>
<section>
<h5>Feldforschung</h5>
</section>
<section>
<p>Ziel der Feldforschung ist es, den Nutzer in seinem "natürlichen" Nutzungskontext zu beobachten.</p>
<p>Stichpunkt ist hierbei <strong>beobachten</strong>. Wir wollen den Nutzer nicht ablenken sondern nur als passiver Beobachter Informationen sammeln.</p>
</section>
<section>
<h5>Quantitative Nutzungsdaten aus eigener Datenaggregation</h5>
</section>
<section>
<p>Mit dieser Methode werden Informationen beschrieben, die durch Tracking-Verfahren (nach Zustimmung des Nutzers) auf einer Website gesammelt werden.</p>
<p>Hierbei können wir verschiedene Arten von Informationen erhalten:</p>
</section>
<section>
<ul>
<li>Klick-Strecke eines Nutzers</li>
<li>Scroll-Heatmaps</li>
<li>Verweildauer auf Seiten</li>
<li>U.U. Mausbewegungen</li>
</ul>
</section>
<section>
<p>Mit der Beendigungen dieses zweiten Schrittes sollten wir echte Daten gewonnen haben. Diese Daten helfen uns ein tieferes Verständnis im Problemraum zu schaffen und zu testen ob die aufgestellen Annahmen korrekt waren.</p>
</section>
</section>

View file

@ -0,0 +1,20 @@
<section>
<section>
<h4>Prototyp entwickeln</h4>
</section>
<section>
<p>Mit den Entwickelten Lösungen können wir nun einen Prototypen erstellen. Damit ist aber nicht gemeint, das eine tatsächliche (Software) Lösung entwickelt werden muss.</p>
<p>Vielmehr kann man alles was es gibt verwenden um das erarbeitete Konzept visuell darzustellen.</p>
</section>
<section>
<ul>
<li>Lego</li>
<li>Papier</li>
<li>Weitere Materialien</li>
<li>Skizzen</li>
<li>Theateraufführung</li>
</ul>
</section>
</section>

View file

@ -0,0 +1,19 @@
<section>
<section>
<h4>Testen</h4>
</section>
<section>
<p>In direkten Versuchen dürfen die Gruppen untereinander die Lösungen "vertesten".</p>
<p>Dabei werden neue Einblicke als auch Feedback zur erarbeiteten Lösung gesammelt.</p>
</section>
<section>
<p>Weitere Schritte nach dem "vertesten" im Design-Thinking Prozess:</p>
</section>
<section>
<p>In der Theorie würde eine Erarbeitete Lösung von allen Personen ausgewählt und tatsächlich implementiert werden.</p>
<p>Zur Nutzerakzeptanz können Methoden wie "Feature Flags", Beta-Releases und "A/B-Tests" verwendet werden. Ziel ist, neue Features an einem bestimmten Anteil der Nutzerschaft zu testen, Fehler zu finden und Thesen zu prüfen, ehe die Lösung für alle Personen ausgerollt wird.</p>
</section>
</section>

View file

@ -0,0 +1,70 @@
<section>
<section>
<h3>Unser Problem verstehen: Analyse im Problemraum</h3>
</section>
<section>
<h4>Verstehen</h4>
</section>
<section>
<p>Da im Design Thinking mehrere Bereiche bei der Erarbeitung beteiligt sind, ist es wichtig, dass zu Beginn ein gemeinsames Verständnis für das Problem entsteht.</p>
<p>Mit dem erarbeiteten Verständnis für das Problem, sind wird dann auch in der Lage ein Ziel zu definieren, das erreicht werden will (mitunter mit Umwegen).</p>
</section>
<section>
<h5>Stakeholder Interviews</h5>
</section>
<section>
<p>In diesem Kontext verstehen wir Stakeholder als Personen oder Gruppen von Personen, die Teilhabe am Projekt (oder besser gesagt dem Resultat) haben. Dies sind unter anderem:</p>
<ul>
<li>Der Budget-Geber</li>
<li>Vorraussichtlicher Nutzer der Software oder Person(en) mit direktem Kundenkontakt</li>
<li>Das Entwicklungsteam</li>
<li>Vertrieb & Marketing</li>
<li>ggf. weitere Abteilungen / Personen</li>
</ul>
</section>
<section>
<p>All diese Personen werden auf die ein oder andere Art und Weise mit dem Produkt oder der Lösung arbeiten. Diese haben also auch ein berechtigtes Interesse am Erfolg des Projektes.</p>
<p>Nicht jede Personengruppe hat die gleiche "Macht". Es ist wichtig seine Stakeholder zu kategorisieren. Es wird auch unter anderem eine Matrix angelegt, in der festgelegt welche Personen und Bereiche auf welche Art und Weisen informiert werden, wenn es Fortschritte im Projekt gibt.</p>
</section>
<section>
<p><strong>Was ist das Ziel?</strong></p>
<p>Das Stakeholder ist ein bi-direktionaler Austausch. Wir als Designer wollen den (wichtigsten) Personen unser Projekt vorstellen und einen Organisatorischen Rahmen bilden.</p>
<p>Gleichzeitig wollen wir auch "Insider"-Informationen sammeln. Also Informationen die typischerweise nicht niedergeschrieben sind und nur einem kleinen Personenkreis bekannt sind, aber durchaus wichtig sein können.</p>
</section>
<section>
<h5>Marktanalyse</h5>
</section>
<section>
<p>In der Marktanalyse beobachten wir die Lösungen von Alternativen Produkten oder von Produkten von der Konkurrenz.</p>
<p>Wir betrachten dabei die für uns wichtigen Interaktionspunkte und schätzen dabei ein ob diese das Produkt besser oder schlechte machen.</p>
<p>Aus diesen Informationen können wir lernen, was wir gegenüber anderen Produkten verbessern können aber auch welche Fehler wir direkt vermeiden wollen.</p>
</section>
<section>
<h5>Sekundär-Forschung</h5>
</section>
<section>
<p>In der Sekundärforschung verwenden wir bereits existierende Informationen aus anderen Quellen um uns mit der Fragestellung des Problems auseinanderzusetzen.</p>
<p>Die Informationen können durchaus verschiedener Natur sein:</p>
<ul>
<li>Statistische Auswertungen</li>
<li>Forschungen</li>
<li>Weitere Datenquellen...</li>
</ul>
</section>
<section>
<p>Damit ist unser erster Schritt im Design Thinking Prozess (ggf. fürs erste) abgeschlossen.</p>
<p>Mit den gesammelten Informationen sollten wir nun Kernaussagen auf unsere Problemstellung definieren können. Aus den Kernaussagen können wir wiederum Hypothesen aufstellen.</p>
<p>Mit diesen Hypothesen gehen wir in den 2. Schritt, in dem diese auf die Probe gestellt werden.</p>
</section>
</section>

View file

@ -0,0 +1,27 @@
---
import Title from "./title.astro";
import UI from "./ui.astro";
import UX from './ux.astro';
// import Iso9241210 from "./iso-9241-210.astro";
import DesignThinkingIntro from "./design-thinking-intro.astro";
import Understand from './dt-understand.astro';
import Inspect from './dt-inspect.astro';
import DeclareView from './dt-declare-view.astro';
import Ideate from './dt-ideate.astro';
import Prototype from './dt-prototype.astro';
import Test from './dt-test.astro';
---
<div class="slides">
<Title />
<UI />
<UX />
<!-- <Iso9241210 /> -->
<DesignThinkingIntro />
<Understand />
<Inspect />
<DeclareView />
<Ideate />
<Prototype />
<Test />
</div>

View file

@ -0,0 +1,77 @@
<section>
<section>
<h2>Nutzerzentriertes Design nach ISO 9241-210</h2>
</section>
<section>
<p>Das Ziel der Norm ist es, interaktive System (Produkte sowie Dienstleistsungen) zu entwickeln, die eine gute Nutzbarkeit (Usability) erzielen.</p>
<p>Dabei werden aber nicht nur theoretische Informationen gesammelt. Durch verschiedene Methoden werden Informationen direkt von echten Nutzern der Applikation gesammelt.</p>
</section>
<section>
<p>Um ein gesamtheitliches Konzept zu erarbeiten, ist es dabei notwendig verschiedene Sichtweisen auf Probleme zu haben. Dies hilft dabei, verschiedene Bereiche einer Firma "abzuholen" und mit in die Gestaltung des Produktes einzubeziehen.</p>
</section>
<section>
<strong>Strategische Sichtweise</strong>
<p>Betrifft die Betrachtung aus der Gesamtheitlichen Firmenebene (Was sind die Geschäftsziele, was erwartet der Markt?)</p>
</section>
<section>
<strong>Entwicklerische Sichtweise</strong>
<p>Betrachtung von Trends in der Hardware- und Software-Entwicklung sowie langfristige Planung des Produktes auf Code-Ebene</p>
</section>
<section>
<strong>Vermarktungssichtweise</strong>
<p>Einbindung von Marketing und Sales in den Prozess zur verbesserten Vermarktung des Produktes</p>
</section>
<section>
<strong>Gestaltungssichtweise</strong>
<p>Fokus auf Nutzbarkeit für alle Nutzergruppen (Barrierefreiheit, Produkt-Design, Ergonomie)</p>
</section>
<section>
<strong>Experten-Sichtweise</strong>
<p>Konkrete Betrachtung von "Power-Nutzern". Dies betrifft sowohl die eigentlichen Nutzer eines Produktes als auch Personen mit Fachwissen in einer Domäne.</p>
</section>
<section>
<h3>Planung</h3>
</section>
<section>
<p>Ziel der Planung ist es, den Aufwand abzuschätzen um eine gute Usability und UX zu erreichen.</p>
<p>Nach der Aufwandsabschätzung, müssen die Inhalte geplant werden:</p>
<p>Identifikation von Methoden und Resourcen, Integration in andere Entwicklungstätigkeiten, Festlegung von Verantwortlichkeiten sowie von Informations und Rückmeldewegen, Definition von Meilensteinen und zeitliche Abgrenzung des Gestaltungsprozesses.</p>
</section>
<section>
<h3>Verstehen und Beschreiben des Nutzungskontextes</h3>
</section>
<section>
<p>In diesem Schritt versuchen wir eines zu verstehen: den IST-Zustand bei der Benutzung des Produktes (oder von verwandten Produkten).</p>
<p>Selbst wenn wir ein neues Produkt auf "den Martk bringen wollen", sind wir wahrscheinlich nicht die ersten die das machen - beziehungsweise es gibt schon Methoden die Versuchen ein Problem zu lösen, es aber noch keine gesamtheitliche Lösung gibt.</p>
</section>
<section>
<p>Wichtig sind dabei folgende Informationen:</p>
<ul>
<li>Wer sind die Stakeholder? (Nuzter und andere)</li>
<li>Gibt es bei den Nutzern auffällige Merkmale? (Alter, Berufsstand, etc.)</li>
<li>Welches Problem versucht der Nutzer zu lösen?</li>
<li>Wann und Wo wird das Produkt genutzt?</li>
</ul>
</section>
<section>
<h3>Nutzungsanforderungen Festlegen</h3>
</section>
<section>
<p>Mit den gesammelten Informationen wollen wir nun prüfbar formulieren. Diese prüfbaren Ziele müssen nicht unbedingt Änderungen in der Benutzeroberflächen enden.</p>
<p></p>
</section>
</section>

View file

@ -0,0 +1,3 @@
<section>
<h1>User Interface & Experience Design</h1>
</section>

View file

@ -0,0 +1,137 @@
<section>
<section>
<h2>User Interface Design</h2>
</section>
<section>
<p>Im bisherigen Verlauf dieser Vorlesung haben wir bereits ein paar Webseiten bauen können.</p>
<p>Die meisten würden sicherlich keinen Schönheitswettbewerb gewinnen. Wir schieben aber mal den Zeitmangel als Grund davor und belassen es dabei...</p>
<p>Diese Slides drehen sich aber genau primär darum: User Interface Design</p>
</section>
<section>
<p>Was macht für euch ein Gutes User Interface aus?</p>
<p>Wo begenen euch "User Interfaces"?</p>
</section>
<section>
<p>User Interfaces existieren nicht nur auf dem Bildschirm!</p>
<p>Alles was im weitesten Sinn bedient werden kann, hat ein User Interface, angefangen bei Alltäglichen Gegenständen wie Stiften bis hin zu hochkomplexen Systemen wie Autos.</p>
<p>
<a href="https://www.theuncomfortable.com" rel="noopener noreferrer">The Uncomfortable</a>
</p>
</section>
<section>
<p>Natürlich beziehen wir uns hier auf das Design von Web-Interfaces</p>
<p>Aber was zählt den nun in gutes User Interface Design ein?</p>
</section>
<section>
<ul>
<li>Ein logisches Farbkonzept</li>
<li>Negative Space (whitespace)</li>
<li>Interaktives Feedback</li>
<li>"Wiedererkennbares" Design</li>
<li>Durchdachtes Layout und Navigation</li>
</ul>
</section>
<section>
<h3>Farben</h3>
</section>
<section>
<p>Mit Farben können wir Informationen vermitteln, aber auch Gefühle hervorrufen.</p>
<p>Zusätzlich können wir mit bestimmten Farben auch Firmen oder ganze Bereiche identifizieren:</p>
<ul>
<li>Mit Blau kann man oft Technologie-Firmen assozieren</li>
<li>Grau-Silber deutet auf eine elegante und/oder teure Marke hin. (Stichwort "Zeitlos")</li>
<li>Mit Grün verknüpfen wir die Natur, Wachstum, Gesundheit, etc.</li>
</ul>
</section>
<section>
<p>Farben dienen aber auch dazu, Zustände anzuzeigen</p>
<ul>
<li>Rot zum darstellen von Fehlern</li>
<li>Grün für Erfolge</li>
<li>Blau für Informationen</li>
</ul>
</section>
<section>
<p>Auf was sollten wir bei der Anwendung von Farben achten?</p>
<ul>
<li>Farb-Kontrast (Hintergrund und Vordergrund)</li>
<li>Verwendung von Differenzierbaren Farben im "Grayscale"</li>
<li>Ggf Anwendung der 60:30:10 Regel</li>
</ul>
</section>
<section>
<h3>60:30:10 Regel</h3>
</section>
<section>
<p>Die 60:30:10 Regel ist ein Leitsatz / Guideline, in welchen "Mengen" Farben angewandt werden sollen.</p>
<ul>
<li>Die Basisfarbe soll in einem Umfang von 60% auf der Website verwendet werden</li>
<li>Eine "Unterstützende" (Sekundäre) Farbe soll in einem Umfang von 30% angewandt werden</li>
<li>Akzentfarben (Primärfarben) sollen zu 10% auf einer Website angewandt werden</li>
</ul>
</section>
<section>
<p>Basisfarben sind normalerweiße "großflächig" als Hintergrund verwendet und sollten neutral sein (hell oder dunkel, maximal ein leichter Farbstich)</p>
<p>Sekundärfarben sind in besonderen Bereichen anzuwenden (Sidebars, Karten, Menüs) und sind tendenziell eher "ausgewaschene" Farben</p>
<p>Primärfarben sind "Farbstark" und sollten der Farbe des Unternehmens (Brandfarbe) entsprechen</p>
</section>
<section>
<p>Weiterführende Informationen sind unter den Begriffen "Farblehre" und "Farbentheorie" zu finden.</p>
</section>
<section>
<h3>"Spacing"</h3>
</section>
<section>
<p>Unter dem Begriff "Spacing" sind auch die Begriffe "Whitespace" oder "Negative Space" zu sortieren. Alle Begriffe beschreiben letzten Endes das gleiche.</p>
</section>
<section>
<p>Mit Spacing können wir verschiedene Sachen erwirken:</p>
<ul>
<li>Typografie: Lesbarkeit verbessern</li>
<li>Visuelle Abtrennung von Inhalten</li>
<li>Verbesserung von Interaktiven Elementen - (44px Regel!)</li>
</ul>
</section>
<section>
<p>Aus Entwicklungs Sicht reicht es, ein ausführliches Set an Spacing-Variablen zu haben und ein Grid-System definiert zu haben</p>
<p>Hier hilft CSS-Grid!</p>
</section>
<section>
<h3>Typografie</h3>
</section>
<section>
<p>Die Wahl des Schriftsystems für die Webseite liegt im Normalfall im Aufgabenbereich der Designer, wir als Entwickler sind primär für die Einbindung auf der Webseite verantwortlich (sowie die Einstellung der Schrifteigenschaften)</p>
</section>
<section>
<p>Typografie an sich ist ein sehr komplexes Thema, eine schnelle Übersicht aller wichtigen Begrifflichkeiten lässt sich im <a href="https://m2.material.io/design/typography/understanding-typography.html#type-properties" rel="noreferrer noopener">Material Design</a> finden.</p>
</section>
<section>
<p>Je nach Zielgruppe und Inhalt der Website, sind verschiedene Schrift-Formen möglich.</p>
<ul>
<li>Serifen-Schrift</li>
<li>Monospace Schriften</li>
<li>viele weitere...</li>
</ul>
</section>
</section>

View file

@ -0,0 +1,54 @@
<!-- Self-Explanatory -->
<!-- User Guidance -->
<!-- Innovation vs Tradition -->
<!-- Tools to measure -->
<section>
<section>
<h2>User Experience Design</h2>
</section>
<section>
<b>Abgrenzung</b>
<p>Insgesamt bildet die User Experience ein deutlich breiteres Spektrum ab, als man durch das reine User-Interface abbildet.</p>
<p>Nüchtern betrachtet ist das UI-Design eine Unterdisziplin der UX. Im Umkehrschluss wirken sich Entscheidungen im UX-Design fast immer auf die UI aus.</p>
</section>
<section>
<p>Mit dem UX-Design haben wir deutlich mehr Tools, Konzepte und Möglichkeiten, um "Probleme" zu beheben.</p>
<p>Realistisch gesehen haben wir mehrere Hebel in der UX, in die gewirkt werden können. Primär achtet man beim UX-Design auf den Nutzer, der unser Produkt nutzt.</p>
<p>Ein weiterer wichtiger Stakeholder ist nun auch das Geschäft / die Firma, die das Produkt entwickelt.</p>
</section>
<section>
<p>Das heißt nun damit auch, dass wir das User Interface nicht nur für den Kunden verbessern, sondern auch darauf achten müssen, dass das "Produkt" profitabel sein muss. (Stichwort Dark UX Patterns)</p>
<p>Hauptziel sollte aber bestmöglich sein, die Erfahrung des Nutzers mit unserem Program (und im nächsten Schritt auch "außerhalb" unseres Programmes) zu verbessern.</p>
</section>
<section>
<p>Ähnlich zur uns bekannten Software-Entwicklung ist UX-Design auch ein iterativer Prozess und kann in Agilen Strukturen wie Scrum eingesetzt werden. (Je nach Firma sind dann auch gerne UX-Designer in Teams mit Entwicklern platziert.)</p>
<p>Zur Frage "Wie man 'korrekt' Designed", gibt es ein paar Möglichkeiten. Eine Möglichkeit ist hierbei die ISO-Norm 9241-210 (Menschenzentrierte Gestaltung)</p>
</section>
<section>
<p>Der komplette Prozess nach ISO 9241-210:</p>
<ol>
<li>Planen des Gestaltungsprozesses</li>
<li>Versehen und Beschreiben des Nutzungskontexts</li>
<li>Festlegen der Nutzungsanforderungen</li>
<li>Erarbeiten von Lösungen</li>
<li>Evaluierung der Lösung</li>
</ol>
</section>
<section>
<p>Eine alternative ist der Design-Thinking-Prozess:</p>
<ul>
<li>Verstehen</li>
<li>Beobachten</li>
<li>Sichtweise definieren</li>
<li>Ideen finden</li>
<li>Prototyp Entwickeln</li>
<li>Testen</li>
</ul>
</section>
</section>

View file

@ -14,6 +14,7 @@ const dhbwCollection = defineCollection({
tags: z.array(z.string()),
staticPath: z.string(),
show: z.optional(z.boolean()),
order: z.number(),
}),
});

View file

@ -0,0 +1,73 @@
---
title: "Checkliste Projekte 25% Anteil"
tags:
- "dhbw"
- "web"
- "engineering"
slug: "web-engineering-project-25-checklist"
staticPath: "knowledge-base/dhbw/"
show: true
order: 3
---
# Checkliste für die Benotung von Projektarbeiten
> (Anteil 25% an der Gesamtnote)
Aufgrund der breiten natur aller Punkte, kann ich schlecht auf spezifische "zu erfüllende" Anforderungen eingehen.
Die Nachfolgenden Punkte dienen als Rahmen, nicht jeder Punkt kann auf jede Projektarbeit angewandt werden.
Die Bepunktung geschieht auf Basis der Gruppe. Sollte es ersichtlich sein, dass jemand unzureichend Mitgearbeitet hat so können individuelle Punkte abgezogen werden. Ich gehe auf Personen zu bei denen ich das feststellen sollte um abzuklären, ob die Person an anderweitigen Sachen im Projekt mitgearbeitet hat.
Gewertet werden sowohl Frontend als auch Backend-Code. Viele Gängige Sprachen akzeptiere ich, falls etwas vorkommt was nachfolgend nicht aufgelistet ist, bitte einmal kurz nachfragen:
- JavaScript / TypeScript
- Go
- Java / Kotlin
- Rust
- Python
- C / C++
- Zig
## 10 Punkte: Beurteilung des Codes
- [ ] "Clean Code"
- Variablen und Funktionsbenamung macht Sinn
- Verwendung von `let` und `const` anstatt `var`
- [ ] Moderne Features Verwendet
- HTML-5 Elemente verwendet
- Moderne CSS-Features verwendet (z.B. CSS-Grid)
- Auf JavaScript kann man sehr schlecht eingehen. Die Sprache lässt viel zu. Wenn zu viel "BS / Schabernack" getrieben wird würde das zumindest negativ gewertet werden.
- [ ] HTML
- Korrekte Auszeichnung
- [ ] Lokale Entwicklung
- Projekt kann gecloned und (ggf mit Readme) gestartet werden.
## 10 Punkte: Beurteilung der eigentlichen Anwendung
- [ ] Allgemeine Funktionsfähigkeit
- Kann ich die Anwendung normal bedienen?
- Sind Funktionen und wie sie verwendet werden sollen ersichtlich?
- [ ] Nutzbarkeit abseits des "Happy Paths"
- z.B. Korrekte Behandlung von Fehleingaben
- 404 / Error-Seiten
- [ ] Nutzbarkeit
- Bei komplexer Problemstellung: Hilfestellung für den Nuzter
- Bei neuen UI-"Paradigmen": Hilfestellung für den Nutzer
## 5 Punkte: Zusatzpunkte
- [ ] Einsatz von Tooling zum Aufrechterhalten von JS-Formattierung
- Linter
- Formatter
- [ ] Ästhetik
- Gutes Design (Nutzbar)
- Accessibility gewährleistet?
- [ ] TypeScript
- Vermeidungen von Typen wie `any`
- [ ] Erste Aspekte der Sicherheit
- SQL-Injection falls DB verwendet wird
- [ ] Bibliotheken / Frameworks
- Frontend: React, Vue, oder andere
- Backend: Framework je nach Sprache
- [ ] Infrastruktur
- Verwendet z.B. Docker um einen Applikations-Container zu bauen und zu starten
<script>
const checkboxes = document.querySelectorAll('input[type=checkbox]');
checkboxes.forEach((box) => box.disabled = false);
</script>

View file

@ -0,0 +1,56 @@
---
title: "Web Engineering I (Semester 2)"
tags:
- "dhbw"
- "web"
- "engineering"
- "html"
- "css"
- "javascript"
slug: "web-engineering-i-s2"
staticPath: "knowledge-base/dhbw/"
show: true
order: 2
---
import Card from '../../../components/mdx/Card.astro';
import Spacer from '../../../components/mdx/Spacer.astro';
# Modul: Web Engineering I (2. Semester)
In diesem Abschnitt werden Inhalte aus dem Modul Web Engineering II im 2. Semester dargestellt.
---
## TypeScript
Mithilfe von TypeScript können wir zur Entwicklungszeit JavaScript-Variablen mit einem Typ versehen um so die Entwicklung fehlerfreier zu gestallten.
Diese Slides bieten eine Einführung in die Sprache.
<Card to="/slides/flexi-pool/01-typescript" color="blue">Zu den Slides</Card>
<Spacer />
## Server-Applikationen mit TypeScript und Fastify
Wir lernen kurz, wie wir einen rudimentären Backend-Server aufsetzen können und schauen uns dann ein Framework für NodeJS an, mit dem wir größere Server-Applikationen mühelos schreiben können.
<Card to="/slides/flexi-pool/02-backend-development" color="blue">Zu den Slides</Card>
<Spacer />
## Anbindung von Datenbanken
Ich gehen in diesen Slides sehr rudimentär auf Datenbanken und etwas vertiefend auf SQL ein. Wir lernen einfache SQL-Queries kennen und wie wir SQLite in unserem Backend Projekt verwenden können.
<Card to="/slides/flexi-pool/03-using-databases" color="blue">Zu den Slides</Card>
<Spacer />
## Lokal zu Cloud: Unsere Applikation Produktionsreif machen
In diesen Slides werden moderne Methoden vorgestellt, wie wir unseren JavaScript/TypeScript-Code für einen produktiven Einsatz in der Cloud vorbereiten können.
Wir betrachten ein paar Anbieter von Hosting-Lösungen und Nutzen einen solchen um unsere Website Testweise online zur Verfügung zu stellen.
<Card to="/slides/flexi-pool/04-deployment-and-production" color="blue">Zu den Slides</Card>
<Spacer />
## Der Nutzer im Zentrum: User Interface und User Experience Design
Dieser Teil ist noch in Arbeit...

View file

@ -10,6 +10,7 @@ tags:
slug: "web-engineering-i"
staticPath: "knowledge-base/dhbw/"
show: true
order: 1
---
import Card from '../../../components/mdx/Card.astro';
import Spacer from '../../../components/mdx/Spacer.astro';
@ -207,3 +208,27 @@ JavaScript liefert uns dafür im Web-Browser eine spezielle Schnittstelle an: di
<Card to="/slides/javascript/05-dom" color="blue">Zu den Slides</Card>
<Spacer />
### # JS - Klassen und Fehlerbehandlung
<Card to="/slides/javascript/06-classes-errors" color="blue">Zu den Slides</Card>
<Spacer />
### # JS - Promises und Async
<Card to="/slides/javascript/07-async" color="blue">Zu den Slides</Card>
<Spacer />
### # JS - Daten von Servern abfragen via "fetch"
<Card to="/slides/javascript/08-data-fetching" color="blue">Zu den Slides</Card>
<Spacer />
### # JS - Weitere API's im Web-Browser
<Card to="/slides/javascript/09-web-apis" color="blue">Zu den Slides</Card>
<Spacer />
### # JS - Externe Pakete mit NPM (AlpineJS)
<Card to="/slides/javascript/10-external-packages" color="blue">Zu den Slides</Card>

View file

@ -10,5 +10,6 @@ tags:
slug: "web-engineering-ii"
staticPath: "knowledge-base/dhbw/"
show: false
order: 3
---
# Hallo Welt!

View file

@ -8,7 +8,7 @@ export async function getStaticPaths() {
const blogEntries = await getCollection('dhbw');
return blogEntries.map(entry => ({
params: { slug: entry.slug }, props: { entry },
}));
}))
}
// 2. For your template, you can get the entry directly from the prop
const { entry } = Astro.props;

View file

@ -25,7 +25,9 @@ const dhbwModules = await getCollection('dhbw', (module) => module.data.show);
Hier findet Ihr alles rund um die Module die ich als Dozent für die DHBW verfasst habe.
</p>
<HorizontalSlider>
{dhbwModules.map((module) => (
{dhbwModules
.sort((e1, e2) => (e1.data.order - e2.data.order))
.map((module) => (
<Card to={`/${module.data.staticPath}${module.slug}`} class="module-card">
<span class="card-title">{module.data.title}</span>
<Icon name="arrow" class="arrow" />

View file

@ -0,0 +1,8 @@
---
import Reveal from "../../../layouts/Reveal.astro";
import Slides from "../../../components/slides/typescript-beginner/index.astro";
---
<Reveal title="TypeScript">
<Slides />
</Reveal>

View file

@ -0,0 +1,8 @@
---
import Reveal from "../../../layouts/Reveal.astro";
import Slides from "../../../components/slides/node-backend/index.astro";
---
<Reveal title="Backend Entwicklung mit NodeJS">
<Slides />
</Reveal>

View file

@ -0,0 +1,8 @@
---
import Reveal from "../../../layouts/Reveal.astro";
import Slides from "../../../components/slides/databases/index.astro";
---
<Reveal title="Datenbanken einbinden">
<Slides />
</Reveal>

View file

@ -0,0 +1,17 @@
---
import Reveal from "../../../layouts/Reveal.astro";
import Slides from "../../../components/slides/deployments/index.astro";
---
<Reveal title="Build and Deploy to Production">
<Slides />
</Reveal>
<!--
Hosting Files
NGINX
Modern Web
Containerization
Docker
-->

View file

@ -0,0 +1,8 @@
---
import Reveal from "../../../layouts/Reveal.astro";
import Slides from "../../../components/slides/ui-ux/index.astro";
---
<Reveal title="User Interface Design and User Experience Design">
<Slides />
</Reveal>

View file

@ -0,0 +1,8 @@
---
import Reveal from '../../../layouts/Reveal.astro';
import Slides from '../../../components/slides/javascript/10-external-alpinejs/index.astro';
---
<Reveal title="Externe Pakete in JavaScript verwenden">
<Slides />
</Reveal>