feat: working app
This commit is contained in:
24
.gitignore
vendored
Normal file
24
.gitignore
vendored
Normal file
@ -0,0 +1,24 @@
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
pnpm-debug.log*
|
||||
lerna-debug.log*
|
||||
|
||||
node_modules
|
||||
dist
|
||||
dist-ssr
|
||||
*.local
|
||||
|
||||
# Editor directories and files
|
||||
.vscode/*
|
||||
!.vscode/extensions.json
|
||||
.idea
|
||||
.DS_Store
|
||||
*.suo
|
||||
*.ntvs*
|
||||
*.njsproj
|
||||
*.sln
|
||||
*.sw?
|
572
App.tsx
Normal file
572
App.tsx
Normal file
@ -0,0 +1,572 @@
|
||||
import React, { useState, useEffect, useCallback, useMemo } from 'react';
|
||||
import { Dialog } from "@headlessui/react";
|
||||
import { Cog6ToothIcon } from "@heroicons/react/24/outline";
|
||||
import { Exercise, CheckResult, ExercisePartType } from './types';
|
||||
import { swaExercises1 } from './data/swa_exercises.1';
|
||||
import { swaExercises2 } from './data/swa_exercises.2';
|
||||
import { swaExercises3 } from './data/swa_exercises.3';
|
||||
import { swaExercises4 } from './data/swa_exercises.4';
|
||||
import { swaExercises5 } from './data/swa_exercises.5';
|
||||
import { swaExercises6 } from './data/swa_exercises.6';
|
||||
import { swaExercises7 } from './data/swa_exercises.7';
|
||||
import { swaExercisesExamples } from './data/swa_exercises.examples';
|
||||
import { GoogleGenAI, GenerateContentResponse } from "@google/genai";
|
||||
import ReactMarkdown from 'react-markdown';
|
||||
import remarkGfm from 'remark-gfm';
|
||||
import * as Diff from 'diff';
|
||||
import Editor from 'react-simple-code-editor';
|
||||
import Prism from 'prismjs/components/prism-core';
|
||||
import 'prismjs/components/prism-clike';
|
||||
import 'prismjs/components/prism-c';
|
||||
|
||||
|
||||
// --- GEMINI API SETUP & HELPERS ---
|
||||
// (ai instance will be created in App component)
|
||||
|
||||
const extractJson = (text: string): any => {
|
||||
let jsonString = text.trim();
|
||||
|
||||
const fenceRegex = /^```(?:json)?\s*\n?(.*?)\n?\s*```$/s;
|
||||
const match = jsonString.match(fenceRegex);
|
||||
if (match && match[1]) {
|
||||
jsonString = match[1].trim();
|
||||
}
|
||||
|
||||
const lastBracket = jsonString.lastIndexOf('}');
|
||||
if (lastBracket === -1) {
|
||||
throw new Error("No closing brace '}' found in API response.");
|
||||
}
|
||||
|
||||
let openBraceCount = 0;
|
||||
let firstBracket = -1;
|
||||
for (let i = lastBracket; i >= 0; i--) {
|
||||
if (jsonString[i] === '}') {
|
||||
openBraceCount++;
|
||||
} else if (jsonString[i] === '{') {
|
||||
openBraceCount--;
|
||||
}
|
||||
if (openBraceCount === 0) {
|
||||
firstBracket = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (firstBracket === -1) {
|
||||
throw new Error("Could not find matching opening brace '{' in API response.");
|
||||
}
|
||||
|
||||
const finalJsonString = jsonString.substring(firstBracket, lastBracket + 1);
|
||||
return JSON.parse(finalJsonString);
|
||||
};
|
||||
|
||||
|
||||
const evaluateWithGemini = async (
|
||||
ai: GoogleGenAI,
|
||||
userAnswer: string,
|
||||
solution: string,
|
||||
prompt: string
|
||||
): Promise<{ correct: boolean, explanation: string }> => {
|
||||
const systemInstruction = `You are an expert Software Architecture (SWA) tutor. Your task is to evaluate a user's answer against a model solution for a specific exercise. The user's answer might be phrased differently but contain the correct key concepts. Focus on semantic correctness, not just literal string matching. For Mermaid diagrams, check if the user's diagram correctly represents the required structures and relationships.\n\nThe exercise is: "${prompt}"\n\nAnalyze the user's answer and compare it to the provided model solution.\n\nRespond ONLY with a single JSON object in the format:\n{\n "correct": <boolean>,\n "explanation": "<string>"\n}\n\n- "correct": true if the user's answer is a valid and correct solution, false otherwise.\n- "explanation": If correct, provide a brief confirmation and elaborate on why the answer is good. If incorrect, provide a clear, concise explanation of what is wrong or missing. Use markdown for formatting. **Your entire explanation must be in German.**`;
|
||||
|
||||
const contents = `Here is the user's answer:
|
||||
\`\`\`
|
||||
${userAnswer}
|
||||
\`\`\`
|
||||
|
||||
Here is the model solution for reference:
|
||||
\`\`\`
|
||||
${solution}
|
||||
\`\`\`
|
||||
|
||||
Please evaluate the user's answer and provide your response in the specified JSON format.`;
|
||||
|
||||
const response: GenerateContentResponse = await ai.models.generateContent({
|
||||
model: "gemini-2.5-flash-preview-04-17",
|
||||
contents: contents,
|
||||
config: {
|
||||
systemInstruction: systemInstruction,
|
||||
responseMimeType: "application/json",
|
||||
},
|
||||
});
|
||||
|
||||
try {
|
||||
const jsonResponse = extractJson(response.text);
|
||||
if (typeof jsonResponse.correct !== 'boolean' || typeof jsonResponse.explanation !== 'string') {
|
||||
throw new Error("Invalid JSON structure from API.");
|
||||
}
|
||||
return jsonResponse;
|
||||
} catch (e) {
|
||||
console.error("Failed to parse Gemini response:", e);
|
||||
console.error("Raw response text:", response.text);
|
||||
throw new Error(`Could not parse the evaluation response.`);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// --- UI ICONS ---
|
||||
const CheckIcon: React.FC<{ className?: string }> = ({ className }) => (
|
||||
<svg xmlns="http://www.w3.org/2000/svg" className={className} fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M5 13l4 4L19 7" />
|
||||
</svg>
|
||||
);
|
||||
|
||||
const CrossIcon: React.FC<{ className?: string }> = ({ className }) => (
|
||||
<svg xmlns="http://www.w3.org/2000/svg" className={className} fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M6 18L18 6M6 6l12 12" />
|
||||
</svg>
|
||||
);
|
||||
|
||||
const WarningIcon: React.FC<{ className?: string }> = ({ className }) => (
|
||||
<svg xmlns="http://www.w3.org/2000/svg" className={className} fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}>
|
||||
<path strokeLinecap="round" strokeLinejoin="round" d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z" />
|
||||
</svg>
|
||||
);
|
||||
|
||||
|
||||
// --- SUB-COMPONENTS ---
|
||||
const CodeEditor: React.FC<{
|
||||
value: string;
|
||||
onChange: (value: string) => void;
|
||||
placeholder?: string;
|
||||
}> = ({ value, onChange, placeholder }) => {
|
||||
return (
|
||||
<div className="bg-slate-900 rounded-lg border-2 border-slate-700 focus-within:ring-2 focus-within:ring-blue-500 focus-within:border-blue-500 transition-all duration-200 overflow-hidden">
|
||||
<Editor
|
||||
value={value}
|
||||
onValueChange={onChange}
|
||||
highlight={code => Prism.highlight(code, Prism.languages.c, 'c')}
|
||||
padding={12}
|
||||
placeholder={placeholder}
|
||||
textareaClassName="code-editor-textarea"
|
||||
className="caret-white"
|
||||
style={{
|
||||
fontFamily: '"Roboto Mono", monospace',
|
||||
fontSize: 14,
|
||||
lineHeight: 1.5,
|
||||
minHeight: '12rem',
|
||||
color: '#f8f8f2',
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const TextAreaEditor: React.FC<{
|
||||
value: string;
|
||||
onChange: (value: string) => void;
|
||||
placeholder?: string;
|
||||
}> = ({ value, onChange, placeholder }) => {
|
||||
return (
|
||||
<textarea
|
||||
value={value}
|
||||
onChange={(e) => onChange(e.target.value)}
|
||||
placeholder={placeholder}
|
||||
className="w-full h-48 p-3 bg-slate-50 border-2 border-slate-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 transition-all duration-200 font-sans text-slate-800"
|
||||
aria-label="Answer text area"
|
||||
/>
|
||||
)
|
||||
};
|
||||
|
||||
|
||||
interface CodeDiffViewerProps {
|
||||
oldCode: string;
|
||||
newCode: string;
|
||||
}
|
||||
|
||||
const CodeDiffViewer: React.FC<CodeDiffViewerProps> = ({ oldCode, newCode }) => {
|
||||
const differences = Diff.diffLines(oldCode.trim(), newCode.trim());
|
||||
|
||||
return (
|
||||
<pre className="bg-slate-800 text-slate-200 p-3 rounded-md font-mono text-xs max-h-64 overflow-auto">
|
||||
<code>
|
||||
{differences.map((part, index) => {
|
||||
const className = part.added
|
||||
? 'bg-green-500/20'
|
||||
: part.removed
|
||||
? 'bg-red-500/20'
|
||||
: '';
|
||||
const prefix = part.added ? '+' : part.removed ? '-' : ' ';
|
||||
|
||||
const lines = part.value.replace(/\n$/, '').split('\n');
|
||||
|
||||
return (
|
||||
<span key={index} className={`block ${className}`}>
|
||||
{lines.map((line, i) => (
|
||||
<div key={i} className="flex">
|
||||
<span className={`w-5 flex-shrink-0 text-left pl-1 ${part.added ? 'text-green-400' : part.removed ? 'text-red-400' : 'text-slate-500'}`}>{prefix}</span>
|
||||
<span className="flex-grow whitespace-pre-wrap">{line}</span>
|
||||
</div>
|
||||
))}
|
||||
</span>
|
||||
);
|
||||
})}
|
||||
</code>
|
||||
</pre>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
interface ExerciseSheetProps {
|
||||
exercise: Exercise;
|
||||
onNext: () => void;
|
||||
ai: GoogleGenAI | null;
|
||||
}
|
||||
|
||||
const ExerciseSheet: React.FC<ExerciseSheetProps> = ({ exercise, onNext, ai }) => {
|
||||
const [inputs, setInputs] = useState<Record<string, string>>({});
|
||||
const [results, setResults] = useState<CheckResult[] | null>(null);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
setInputs({});
|
||||
setResults(null);
|
||||
setIsLoading(false);
|
||||
}, [exercise]);
|
||||
|
||||
const handleInputChange = (partId: string, value: string) => {
|
||||
setInputs(prev => ({ ...prev, [partId]: value }));
|
||||
};
|
||||
|
||||
const isCodeExercise = (type: ExercisePartType) => !['text', 'mermaid'].includes(type);
|
||||
|
||||
const handleSubmit = async () => {
|
||||
setIsLoading(true);
|
||||
setResults(null);
|
||||
|
||||
const partsToEvaluate = exercise.parts.map(part => ({
|
||||
...part,
|
||||
userInput: inputs[part.id] || '',
|
||||
}));
|
||||
|
||||
const evaluationPromises = partsToEvaluate.map(part => {
|
||||
if (!part.userInput.trim()) {
|
||||
return Promise.resolve({
|
||||
partId: part.id,
|
||||
isCorrect: false,
|
||||
explanation: "Keine Eingabe gemacht."
|
||||
});
|
||||
}
|
||||
if (!ai || typeof ai.models !== "object" || !ai.models.generateContent) {
|
||||
return Promise.resolve({
|
||||
partId: part.id,
|
||||
isCorrect: false,
|
||||
explanation: "API-Key fehlt oder ungültig, oder Gemini SDK nicht korrekt geladen.",
|
||||
error: "Gemini API nicht verfügbar"
|
||||
});
|
||||
}
|
||||
return evaluateWithGemini(ai, part.userInput, part.solution, part.prompt)
|
||||
.then(evalResult => ({
|
||||
partId: part.id,
|
||||
isCorrect: evalResult.correct,
|
||||
explanation: evalResult.explanation,
|
||||
}))
|
||||
.catch(e => ({
|
||||
partId: part.id,
|
||||
isCorrect: false,
|
||||
explanation: '',
|
||||
error: e instanceof Error ? e.message : 'An unknown error occurred.'
|
||||
}));
|
||||
});
|
||||
|
||||
const finalResults = await Promise.all(evaluationPromises);
|
||||
|
||||
setResults(finalResults);
|
||||
setIsLoading(false);
|
||||
};
|
||||
|
||||
const handleReset = () => {
|
||||
setInputs({});
|
||||
setResults(null);
|
||||
setIsLoading(false);
|
||||
}
|
||||
|
||||
const handleNextExercise = () => {
|
||||
onNext();
|
||||
}
|
||||
|
||||
const hasEvaluationErrors = results?.some(r => r.error);
|
||||
|
||||
return (
|
||||
<div className="bg-white rounded-lg shadow-xl p-8 md:p-12 font-sans w-full relative">
|
||||
{isLoading && (
|
||||
<div className="absolute inset-0 bg-white/80 backdrop-blur-sm flex flex-col items-center justify-center z-20 rounded-lg">
|
||||
<svg className="animate-spin h-12 w-12 text-blue-600" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
|
||||
<circle className="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" strokeWidth="4"></circle>
|
||||
<path className="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
|
||||
</svg>
|
||||
<p className="mt-4 font-serif text-lg text-slate-700">Evaluating your answer with Gemini...</p>
|
||||
</div>
|
||||
)}
|
||||
<header className="border-b pb-4 mb-6">
|
||||
<h1 className="font-serif text-2xl md:text-3xl font-bold text-slate-800">{exercise.title}</h1>
|
||||
<p className="text-slate-500">Softwarearchitektur Übung</p>
|
||||
</header>
|
||||
|
||||
{!results ? (
|
||||
// --- EXERCISE VIEW ---
|
||||
<div className="space-y-8">
|
||||
{exercise.parts.map(part => (
|
||||
<div key={part.id} className="flex flex-col md:flex-row gap-4">
|
||||
<div className="w-full md:w-2/5">
|
||||
<h2 className="font-serif font-bold text-lg text-slate-700">Frage {part.id.split('-').pop()}) ({part.points}P)</h2>
|
||||
<div className="mt-2 prose prose-slate prose-sm max-w-none prose-th:text-slate-900 prose-td:text-slate-800">
|
||||
<ReactMarkdown remarkPlugins={[remarkGfm]}>{part.prompt}</ReactMarkdown>
|
||||
</div>
|
||||
</div>
|
||||
<div className="w-full md:w-3/5">
|
||||
{isCodeExercise(part.type) ? (
|
||||
<CodeEditor
|
||||
value={inputs[part.id] || ''}
|
||||
onChange={(value) => handleInputChange(part.id, value)}
|
||||
placeholder={`// Code für Teil ${part.id}) hier eingeben...`}
|
||||
/>
|
||||
) : (
|
||||
<TextAreaEditor
|
||||
value={inputs[part.id] || ''}
|
||||
onChange={(value) => handleInputChange(part.id, value)}
|
||||
placeholder={part.type === 'mermaid' ? 'Antwort in Mermaid-Syntax hier eingeben...' : `Antwort hier eingeben...`}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
<div className="flex justify-end pt-6">
|
||||
<button onClick={handleSubmit} disabled={isLoading} className="px-8 py-3 bg-blue-600 text-white font-bold rounded-lg hover:bg-blue-700 transition-transform transform hover:scale-105 shadow-lg disabled:bg-slate-400 disabled:scale-100 disabled:cursor-not-allowed">
|
||||
Abgeben & Prüfen
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
// --- RESULT VIEW ---
|
||||
<div className="space-y-6">
|
||||
<h2 className="font-serif text-2xl font-bold text-slate-800 border-b pb-2">Auswertung</h2>
|
||||
{results.map(result => {
|
||||
const part = exercise.parts.find(p => p.id === result.partId)!;
|
||||
const userInput = inputs[part.id] || '';
|
||||
if (result.error) {
|
||||
return (
|
||||
<div key={result.partId} className="p-4 rounded-lg border border-amber-500 bg-amber-50">
|
||||
<div className="flex items-center gap-3 mb-2">
|
||||
<WarningIcon className="w-8 h-8 text-amber-600 bg-amber-100 rounded-full p-1" />
|
||||
<h3 className="font-serif font-bold text-xl text-amber-800">Frage {part.id.split('-').pop()}) - Evaluation Failed</h3>
|
||||
</div>
|
||||
<div className="pl-11">
|
||||
<p className="text-sm text-amber-700 mb-4">Error: {result.error}</p>
|
||||
<h4 className="font-bold text-sm mb-1 text-slate-600">Ihre Eingabe:</h4>
|
||||
<pre className="bg-slate-100 text-slate-800 p-3 rounded-md font-mono text-xs max-h-48 overflow-auto">{userInput || '(Keine Eingabe)'}</pre>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div key={result.partId} className={`p-4 rounded-lg border ${result.isCorrect ? 'border-green-500 bg-green-50' : 'border-red-500 bg-red-50'}`}>
|
||||
<div className="flex items-center gap-3 mb-2 flex-wrap">
|
||||
{result.isCorrect ? <CheckIcon className="w-6 h-6 text-green-600" /> : <CrossIcon className="w-6 h-6 text-red-600" />}
|
||||
<h3 className="font-serif font-bold text-xl text-slate-700">Frage {part.id.split('-').pop()})</h3>
|
||||
<span className={`px-3 py-1 text-xs font-bold rounded-full ${result.isCorrect ? 'bg-green-200 text-green-800' : 'bg-red-200 text-red-800'}`}>
|
||||
{result.isCorrect ? "Korrekt" : "Fehlerhaft"}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div className="prose prose-slate prose-sm max-w-none pl-9 prose-th:text-slate-900 prose-td:text-slate-800">
|
||||
<ReactMarkdown remarkPlugins={[remarkGfm]}>{result.explanation}</ReactMarkdown>
|
||||
</div>
|
||||
|
||||
{!result.isCorrect && userInput.trim() && (
|
||||
<div className="mt-4 pl-9">
|
||||
<h4 className="font-bold text-sm mb-2 text-slate-600">Vergleich Ihrer Eingabe mit der Musterlösung:</h4>
|
||||
<div className="flex gap-2 text-xs mb-2 items-center">
|
||||
<span className="flex items-center gap-1.5 px-2 py-0.5 bg-red-500/20 text-red-300 rounded"><span className="font-mono bg-red-900/50 text-red-200 rounded-full w-4 h-4 text-center leading-4">-</span> Ihre Eingabe</span>
|
||||
<span className="flex items-center gap-1.5 px-2 py-0.5 bg-green-500/20 text-green-300 rounded"><span className="font-mono bg-green-900/50 text-green-200 rounded-full w-4 h-4 text-center leading-4">+</span> Korrekte Lösung</span>
|
||||
</div>
|
||||
<CodeDiffViewer oldCode={userInput} newCode={part.solution} />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
|
||||
{hasEvaluationErrors && (
|
||||
<div className="mt-6 p-4 rounded-md bg-yellow-100 border border-yellow-300 text-center">
|
||||
<p className="text-yellow-800 font-medium">Bei einigen Teilen ist ein Fehler bei der Auswertung aufgetreten. Sie können es erneut versuchen.</p>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="mt-8 pt-6 border-t">
|
||||
<div className="prose prose-slate prose-sm max-w-none prose-th:text-slate-900 prose-td:text-slate-800">
|
||||
<ReactMarkdown remarkPlugins={[remarkGfm]}>{exercise.explanation}</ReactMarkdown>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex justify-between items-center pt-6 flex-wrap gap-4">
|
||||
<button onClick={handleReset} className="px-6 py-2 bg-slate-200 text-slate-800 font-bold rounded-lg hover:bg-slate-300 transition">
|
||||
Nochmal versuchen
|
||||
</button>
|
||||
{hasEvaluationErrors && (
|
||||
<button onClick={handleSubmit} disabled={isLoading} className="px-8 py-3 bg-amber-500 text-white font-bold rounded-lg hover:bg-amber-600 transition-transform transform hover:scale-105 shadow-lg disabled:bg-slate-400 disabled:scale-100">
|
||||
Erneut auswerten
|
||||
</button>
|
||||
)}
|
||||
<button onClick={handleNextExercise} className="px-8 py-3 bg-green-600 text-white font-bold rounded-lg hover:bg-green-700 transition-transform transform hover:scale-105 shadow-lg">
|
||||
Nächste Aufgabe
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
// --- MAIN APP COMPONENT ---
|
||||
const allSwaExercises: Exercise[] = [
|
||||
...swaExercises1,
|
||||
...swaExercises2,
|
||||
...swaExercises3,
|
||||
...swaExercises4,
|
||||
...swaExercises5,
|
||||
...swaExercises6,
|
||||
...swaExercises6,
|
||||
...swaExercises7,
|
||||
...swaExercisesExamples
|
||||
];
|
||||
|
||||
|
||||
export default function App() {
|
||||
const [exercises, setExercises] = useState<Exercise[]>([]);
|
||||
const [currentIndex, setCurrentIndex] = useState(0);
|
||||
|
||||
// --- API KEY STATE ---
|
||||
const [apiKey, setApiKey] = useState<string>('');
|
||||
const [showSettings, setShowSettings] = useState(false);
|
||||
const [inputKey, setInputKey] = useState('');
|
||||
|
||||
// On mount, load API key from localStorage or env
|
||||
useEffect(() => {
|
||||
const stored = localStorage.getItem('gemini_api_key');
|
||||
if (stored) {
|
||||
setApiKey(stored);
|
||||
} else if (process.env.API_KEY) {
|
||||
setApiKey(process.env.API_KEY);
|
||||
}
|
||||
}, []);
|
||||
|
||||
// Gemini API instance (re-created if key changes)
|
||||
const ai = useMemo(() => apiKey ? new GoogleGenAI({ apiKey }) : null, [apiKey]);
|
||||
|
||||
// Save API key to localStorage and state
|
||||
const handleSaveKey = () => {
|
||||
localStorage.setItem('gemini_api_key', inputKey);
|
||||
setApiKey(inputKey);
|
||||
setShowSettings(false);
|
||||
};
|
||||
|
||||
// UI logic
|
||||
useEffect(() => {
|
||||
if (showSettings) setInputKey(apiKey || '');
|
||||
}, [showSettings, apiKey]);
|
||||
|
||||
useEffect(() => {
|
||||
const shuffled = [...allSwaExercises].sort(() => Math.random() - 0.5);
|
||||
setExercises(shuffled);
|
||||
}, []);
|
||||
|
||||
const handleNext = useCallback(() => {
|
||||
setCurrentIndex(prev => (prev + 1) % (exercises.length || 1));
|
||||
}, [exercises.length]);
|
||||
|
||||
const currentExercise = useMemo(() => exercises[currentIndex], [exercises, currentIndex]);
|
||||
|
||||
if (exercises.length === 0 || !currentExercise) {
|
||||
return <div className="flex items-center justify-center h-screen text-xl font-serif">Lade Übungen...</div>;
|
||||
}
|
||||
|
||||
const getExerciseButtonTitle = (ex: Exercise) => {
|
||||
const parts = ex.title.split(/ - |: /);
|
||||
return (parts.pop() || ex.title).trim();
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="min-h-screen flex flex-col p-4 md:p-8">
|
||||
{/* Settings Button */}
|
||||
<button
|
||||
className="fixed top-4 right-4 z-50 bg-white rounded-full p-2 shadow hover:bg-slate-100 transition"
|
||||
aria-label="API Key Settings"
|
||||
onClick={() => setShowSettings(true)}
|
||||
>
|
||||
<Cog6ToothIcon className="w-7 h-7 text-slate-600" />
|
||||
</button>
|
||||
{/* Settings Modal */}
|
||||
<Dialog open={showSettings} onClose={() => setShowSettings(false)} className="relative z-50">
|
||||
<div className="fixed inset-0 bg-black/30" aria-hidden="true" />
|
||||
<div className="fixed inset-0 flex items-center justify-center p-4">
|
||||
<Dialog.Panel className="bg-white rounded-lg shadow-xl p-8 max-w-md w-full">
|
||||
<Dialog.Title className="font-bold text-lg mb-4 flex items-center gap-2">
|
||||
<Cog6ToothIcon className="w-6 h-6 text-blue-600" />
|
||||
Gemini API Key
|
||||
</Dialog.Title>
|
||||
<div className="mb-4">
|
||||
<input
|
||||
type="text"
|
||||
className="w-full border rounded px-3 py-2 text-slate-800"
|
||||
value={inputKey}
|
||||
onChange={e => setInputKey(e.target.value)}
|
||||
placeholder="Enter Gemini API Key"
|
||||
/>
|
||||
</div>
|
||||
<div className="flex justify-end gap-2">
|
||||
<button
|
||||
className="px-4 py-2 rounded bg-slate-200 text-slate-700 hover:bg-slate-300"
|
||||
onClick={() => setShowSettings(false)}
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
<button
|
||||
className="px-4 py-2 rounded bg-blue-600 text-white font-bold hover:bg-blue-700"
|
||||
onClick={handleSaveKey}
|
||||
disabled={!inputKey.trim()}
|
||||
>
|
||||
Save
|
||||
</button>
|
||||
</div>
|
||||
<div className="mt-4 text-xs text-slate-500">
|
||||
Your API key is stored in your browser only.
|
||||
</div>
|
||||
<div className="mt-2 text-xs text-slate-500">
|
||||
Get an API key at <a href="https://aistudio.google.com/" target="_blank" rel="noopener noreferrer" className="text-blue-600 hover:underline">https://aistudio.google.com/</a> - this app uses gemini-2.5-flash, which is free for 500 requests a day (as of 9.7.2025)
|
||||
</div>
|
||||
</Dialog.Panel>
|
||||
</div>
|
||||
</Dialog>
|
||||
<header className="mb-6 flex-shrink-0">
|
||||
<h1 className="text-3xl font-serif font-bold text-slate-800">SWA Trainer</h1>
|
||||
<p className="text-slate-500">Interaktive Übungen zur Vorbereitung auf die SWA-Klausur</p>
|
||||
</header>
|
||||
<div className="flex flex-col md:flex-row gap-8 flex-grow min-h-0">
|
||||
<aside className="w-full md:w-64 flex-shrink-0">
|
||||
<h2 className="font-serif text-xl font-bold text-slate-700 mb-4">Themengebiete</h2>
|
||||
<nav className="space-y-2">
|
||||
{exercises.map((ex, index) => (
|
||||
<button
|
||||
key={ex.id}
|
||||
onClick={() => setCurrentIndex(index)}
|
||||
className={`w-full text-left px-4 py-2 rounded-md transition duration-150 text-sm ${
|
||||
ex.id === currentExercise.id
|
||||
? 'bg-blue-600 text-white font-bold shadow'
|
||||
: 'bg-white text-slate-700 hover:bg-slate-200'
|
||||
}`}
|
||||
>
|
||||
{getExerciseButtonTitle(ex)}
|
||||
</button>
|
||||
))}
|
||||
</nav>
|
||||
</aside>
|
||||
<main className="flex-grow flex items-start justify-center min-w-0">
|
||||
<div className="w-full max-w-4xl">
|
||||
<ExerciseSheet key={currentExercise.id} exercise={currentExercise} onNext={handleNext} ai={ai} />
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
20
Dockerfile
Normal file
20
Dockerfile
Normal file
@ -0,0 +1,20 @@
|
||||
FROM node:21-slim AS base
|
||||
ENV PNPM_HOME="/pnpm"
|
||||
ENV PATH="$PNPM_HOME:$PATH"
|
||||
RUN corepack enable
|
||||
COPY . /app
|
||||
WORKDIR /app
|
||||
|
||||
FROM base AS prod-deps
|
||||
RUN --mount=type=cache,id=pnpm,target=/pnpm/store pnpm install --prod --frozen-lockfile
|
||||
|
||||
FROM base AS build
|
||||
RUN --mount=type=cache,id=pnpm,target=/pnpm/store pnpm install --frozen-lockfile
|
||||
RUN pnpm run build
|
||||
|
||||
FROM base
|
||||
COPY --from=prod-deps /app/node_modules /app/node_modules
|
||||
COPY --from=build /app/dist /app/dist
|
||||
RUN pnpm install -g serve
|
||||
EXPOSE 8000
|
||||
CMD [ "serve", "-s", "dist", "-l", "8000" ]
|
14
README.md
Normal file
14
README.md
Normal file
@ -0,0 +1,14 @@
|
||||
# Run and deploy your AI Studio app
|
||||
|
||||
This contains everything you need to run your app locally.
|
||||
|
||||
## Run Locally
|
||||
|
||||
**Prerequisites:** Node.js
|
||||
|
||||
|
||||
1. Install dependencies:
|
||||
`npm install`
|
||||
2. Set the `GEMINI_API_KEY` in [.env.local](.env.local) to your Gemini API key
|
||||
3. Run the app:
|
||||
`npm run dev`
|
4
data/exercises.ts
Normal file
4
data/exercises.ts
Normal file
@ -0,0 +1,4 @@
|
||||
|
||||
import { Exercise } from '../types';
|
||||
|
||||
export const exercises: Exercise[] = [];
|
4
data/programming_exercises.ts
Normal file
4
data/programming_exercises.ts
Normal file
@ -0,0 +1,4 @@
|
||||
|
||||
import { Exercise } from '../types';
|
||||
|
||||
export const programmingExercises: Exercise[] = [];
|
138
data/swa_exercises.1.ts
Normal file
138
data/swa_exercises.1.ts
Normal file
@ -0,0 +1,138 @@
|
||||
|
||||
import { Exercise } from '../types';
|
||||
|
||||
export const swaExercises1: Exercise[] = [
|
||||
{
|
||||
id: 'swa-1-1-einfuehrung',
|
||||
title: '1.1 - SWA - Einführung',
|
||||
parts: [
|
||||
{
|
||||
id: '1-1-a',
|
||||
prompt: 'Erläutern Sie den Begriff „Softwarearchitektur".',
|
||||
solution: 'Softwarearchitektur umfasst die Prinzipien und grundlegenden Design-Entscheidungen eines Systems. Für Informationssysteme beinhaltet dies insbesondere Komponenten/Bausteine/Services, deren Schnittstellen und Abhängigkeiten untereinander und zur Umgebung, dargestellt in verschiedenen Sichten ("Baupläne").\n\nVerschiedene Definitionen existieren, z.B. von Taylor, Reussner oder Booch, die alle die Organisation eines Systems durch seine Strukturelemente, deren Beziehungen und die leitenden Prinzipien betonen.',
|
||||
points: 4,
|
||||
type: 'text',
|
||||
},
|
||||
{
|
||||
id: '1-1-b',
|
||||
prompt: 'Was sind Ziele und Aufgaben von Softwarearchitektur? (Stichwort: Leitplanken)',
|
||||
solution: 'Die Ziele und Aufgaben von Softwarearchitektur sind:\n- Einem Softwareentwicklungsteam ermöglichen, sich schnell im System zurechtzufinden\n- Struktur und die dafür getroffenen Entscheidungen vorgeben\n- Möglichkeiten begrenzen, unnötige Komplexität einzubauen\n- Entwicklern "Leitplanken" bieten, an denen sie sich orientieren können\n- Den Designraum einschränken, um Entwicklungsentscheidungen in eine einheitliche Richtung zu lenken',
|
||||
points: 4,
|
||||
type: 'text',
|
||||
},
|
||||
{
|
||||
id: '1-1-c',
|
||||
prompt: 'Wozu dienen Sichten in der Softwarearchitektur?',
|
||||
solution: 'Sichten beleuchten unterschiedliche Aspekte der Architektur. Sie dienen dazu:\n- Zuständigkeiten zu trennen (z.B. Entwickler vs. Betreiber).\n- Die Architektur aus unterschiedlichen Perspektiven zu zeigen (z.B. statische Struktur vs. dynamisches Verhalten).\n- Für unterschiedliche Stakeholder verständlich zu sein.',
|
||||
points: 3,
|
||||
type: 'text',
|
||||
},
|
||||
{
|
||||
id: '1-1-d',
|
||||
prompt: 'Was bedeutet Architekturerosion?',
|
||||
solution: 'Architekturerosion beschreibt den schrittweisen Verfall der Struktur eines Softwaresystems. Sie tritt auf, wenn die Architektur nicht gepflegt wird, vom ursprünglichen Design abgewichen wird und Code-Entropie einsetzt. Dies führt mit der Zeit zum Aufbau von technischen Schulden.',
|
||||
points: 4,
|
||||
type: 'text',
|
||||
},
|
||||
{
|
||||
id: '1-1-e',
|
||||
prompt: 'Erläutere das Big-ball-of-Mud Antipattern.',
|
||||
solution: 'Das "Big Ball of Mud" ist ein Softwaresystem, das keine erkennbare Architektur aufweist. Es entsteht durch Architekturerosion und ist gekennzeichnet durch eine fehlende klare Struktur und stark vernetzte Komponenten. Solche Systeme sind extrem schwierig zu warten und zu erweitern.',
|
||||
points: 4,
|
||||
type: 'text',
|
||||
},
|
||||
{
|
||||
id: '1-1-f',
|
||||
prompt: 'Was bedeutet „Technische Schulden"?',
|
||||
solution: 'Technische Schulden beschreiben die Konsequenzen schlechter Design-Entscheidungen oder Abkürzungen in der Entwicklung. Wenn Architektur erodiert, werden Schritt für Schritt technische Schulden aufgebaut. Sie machen zukünftige Änderungen schwieriger und kostspieliger, ähnlich wie finanzielle Schulden mit Zinsen.',
|
||||
points: 4,
|
||||
type: 'text',
|
||||
},
|
||||
{
|
||||
id: '1-1-g',
|
||||
prompt: 'Nennen Sie vier Symptome schlechter Architektur nach Robert C. Martin.',
|
||||
solution: '1. Starrheit (Rigidity): Das System ist unflexibel gegenüber Änderungen; kaskadierende Anpassungen sind nötig.\n2. Zerbrechlichkeit (Fragility): Änderungen führen zu unerwarteten Fehlern in anderen Teilen des Systems.\n3. Unbeweglichkeit (Immobility): Bestehende Lösungen können nicht einfach in anderen Kontexten wiederverwendet werden.\n4. Zähigkeit (Viscosity): Ein "Hack" ist leichter zu implementieren als eine architekturkonforme Lösung.',
|
||||
points: 4,
|
||||
type: 'text',
|
||||
},
|
||||
{
|
||||
id: '1-1-h',
|
||||
prompt: 'Wie kommt man zu einer „guten" Architektur?',
|
||||
solution: 'Man erreicht eine gute Architektur durch einen methodischen Prozess:\n- Verständnis erlangen (Anforderungen, Randbedingungen, Risiken)\n- Entwurf: Strukturen schaffen (Bausteine, Ablauf), fachlich beginnen und technisch verfeinern\n- Konzepte für technische Querschnittsthemen entwickeln (Persistenz, UI, etc.)\n- Bedarfsgerechte, kontinuierliche Kommunikation und Dokumentation\nEs gibt keine "Silberkugel", sondern man verwendet Methoden und Heuristiken.',
|
||||
points: 5,
|
||||
type: 'text',
|
||||
},
|
||||
{
|
||||
id: '1-1-i',
|
||||
prompt: 'Wie funktioniert Architektur im agilen vs. klassischen Software-Entwicklungsprozess?',
|
||||
solution: 'Klassisch: Basiert auf einer vollständigen Anforderungsspezifikation. Führt zu einem "Big Design Up Front" (BDUF), bei dem die Architektur vor der Implementierung detailliert entworfen wird.\n\nAgil: Kein BDUF. Die Architektur entsteht evolutionär, Inkrement für Inkrement. Man strebt eine "Minimum Viable Architecture" an und trifft Entscheidungen so spät wie möglich (Least Reasonable Moment).',
|
||||
points: 5,
|
||||
type: 'text',
|
||||
},
|
||||
{
|
||||
id: '1-1-j',
|
||||
prompt: 'Erläutere das LRM = Least Reasonable Moment – Prinzip und wie es in agilen Projekten helfen kann. Nenne ein konkretes Beispiel.',
|
||||
solution: 'Das LRM (Least Reasonable Moment) ist ein Prinzip aus der Lean-Software-Entwicklung, das besagt, dass wichtige architektonische Entscheidungen so spät wie möglich getroffen werden sollten, um:\n- Mehr Informationen zu sammeln\n- Unsicherheiten zu reduzieren\n- Flexibilität zu bewahren\n\nBeispiel: Bei einem neuen E-Commerce-System muss das Team zwischen relationalen und NoSQL-Datenbanken wählen. Durch Warten auf den LRM können sie mehr Informationen über Datenmodelle und Leistungsanforderungen sammeln, bevor sie eine endgültige Entscheidung treffen.',
|
||||
points: 5,
|
||||
type: 'text',
|
||||
},
|
||||
{
|
||||
id: '1-1-k',
|
||||
prompt: 'Erläutere die Verantwortlichkeiten und Aufgaben einer Architektin.',
|
||||
solution: 'Verantwortlichkeiten und Aufgaben umfassen:\n- Analyse und Definition der Qualitätsanforderungen\n- Entwurf von Lösungsoptionen und Treffen von Entscheidungen\n- Dokumentation und Kommunikation der Architektur\n- Beratung von Projektleitung und Entwicklungsteam\n- Risikomanagement und Sicherstellung der Wirtschaftlichkeit',
|
||||
points: 4,
|
||||
type: 'text',
|
||||
},
|
||||
{
|
||||
id: '1-1-l',
|
||||
prompt: 'Welche Beziehungen zu Stakeholdern in einem Software Projekt hat eine ArchitektIn oftmals? (nenne zwei)',
|
||||
solution: 'Eine Architektin hat Beziehungen zu verschiedenen Stakeholdern, unter anderem:\n1. Fachbereich/Product Owner: Anforderungen klären und präzisieren.\n2. Entwicklung: Begleitung bei der Umsetzung und Vorgabe von Leitplanken.\n(Weitere: Projektleitung, Unternehmens-Architektur, Betrieb)',
|
||||
points: 3,
|
||||
type: 'text',
|
||||
}
|
||||
],
|
||||
explanation: '### Lösungserklärung\n\n**Softwarearchitektur** ist die grundlegende Organisation eines Systems. Sie definiert die Bausteine, deren Beziehungen und die Prinzipien, die den Entwurf leiten. Zur Kommunikation werden verschiedene **Sichten** (Perspektiven) verwendet.\n\n**Ziele & Aufgaben** ist es, **Leitplanken** für Entwickler zu bieten, um Komplexität zu managen und ein gemeinsames Verständnis zu schaffen. Ein **guter Architekturprozess** ist methodisch und iterativ.\n\n**Architekturerosion & Technische Schulden**: Wenn die Architektur nicht aktiv gepflegt wird, verfällt die Struktur (**Erosion**). Dies führt zum Aufbau **technischer Schulden**, die zukünftige Änderungen erschweren und verteuern. Ein Endzustand davon ist der **"Big Ball of Mud"**, ein System ohne erkennbare Struktur. **Symptome** dafür sind Starrheit, Zerbrechlichkeit, Unbeweglichkeit und Zähigkeit.\n\n**Prozesse**: Im **klassischen** Vorgehen wird die Architektur vorab detailliert geplant (**Big Design Up Front**). Im **agilen** Prozess entsteht sie **evolutionär**. Entscheidungen werden nach dem **LRM-Prinzip** (Least Reasonable Moment) so spät wie möglich getroffen, um auf Basis maximaler Informationen zu entscheiden.\n\n**Rolle des Architekten**: Die Architektin ist nicht nur für den technischen Entwurf zuständig, sondern agiert als Kommunikatorin und Vermittlerin zwischen verschiedenen **Stakeholdern** (Fachbereich, Entwicklung, Management, Betrieb).\n\n### Quellen\n*Folien 1.1, Seite 22-66*'
|
||||
},
|
||||
{
|
||||
id: 'swa-1-2-anforderungen',
|
||||
title: '1.2 - SWA - Anforderungen & Kontext',
|
||||
parts: [
|
||||
{
|
||||
id: '1-2-a',
|
||||
prompt: 'Analysieren Sie die folgende Anforderung und identifizieren Sie Probleme/Unklarheiten im Hinblick auf Softwarearchitektur: "Das System sendet innerhalb von 5 Minuten eine Fehlermeldung an den aktiven Betreiber, falls ein Fehlersignal vom Generator empfangen wird."',
|
||||
solution: 'Die Anforderung enthält mehrere Unklarheiten:\n- Was bedeutet "aktiver Betreiber"? Wie wird er identifiziert?\n- Welches Format hat das Fehlersignal?\n- Was soll die Fehlermeldung beinhalten?\n- Was passiert, wenn kein Betreiber aktiv ist?\n- Wie wird mit Netzwerkproblemen beim Senden umgegangen?\n- Sind 5 Minuten ein Maximalwert oder eine exakte Anforderung?',
|
||||
points: 6,
|
||||
type: 'text',
|
||||
},
|
||||
{
|
||||
id: '1-2-b',
|
||||
prompt: 'Formulieren Sie ein vollständiges Qualitätsszenario nach dem Schema aus der Vorlesung (Quelle→Stimulus→Artefakt→...).',
|
||||
solution: 'Ein Qualitätsszenario folgt dem Schema: Quelle → Stimulus → Artefakt → Umgebung → Antwort → Antwortmaß.\n\nBeispiel: "Wenn ein Redakteur (Quelle) mit der Stichwortsuche nach Artikeln sucht (Stimulus), findet das Artikelverzeichnis (Artefakt) auch bei hoher Systemlast (Umgebung) alle relevanten Artikel (Antwort) mit 100% Abdeckung (Antwortmaß)."',
|
||||
points: 6,
|
||||
type: 'text',
|
||||
},
|
||||
{
|
||||
id: '1-2-c',
|
||||
prompt: 'Nennen Sie 2 der 6 Hauptqualitätsmerkmale nach ISO/IEC 9126 und ordnen Sie jedem Merkmal ein konkretes Beispiel zu.',
|
||||
solution: 'Beispiele aus ISO/IEC 9126:\n1. Zuverlässigkeit: Ein System muss 99,9% Verfügbarkeit gewährleisten.\n2. Wartbarkeit: Modularer Code, der leicht erweitert werden kann.\n(Weitere: Funktionalität, Benutzbarkeit, Effizienz, Portierbarkeit)',
|
||||
points: 5,
|
||||
type: 'text',
|
||||
},
|
||||
{
|
||||
id: '1-2-d',
|
||||
prompt: 'Was sind Randbedingungen und was sind Risikofaktoren im Kontext der Softwarearchitektur?',
|
||||
solution: 'Randbedingungen sind nicht verhandelbare Vorgaben, die den Lösungsraum einschränken. Beispiele: Technische Vorgaben (z.B. "Das System muss Java verwenden"), organisatorische Vorgaben (Team, Budget, Zeit).\n\nRisikofaktoren sind Unsicherheiten, die den Erfolg des Projekts gefährden können. Beispiele: Technische Unsicherheiten (Einsatz neuer Technologien), komplexe Integrationen, Performance-Herausforderungen.',
|
||||
points: 5,
|
||||
type: 'text',
|
||||
},
|
||||
{
|
||||
id: '1-2-e',
|
||||
prompt: 'Erstellen Sie ein Systemkontextdiagramm für ein einfaches Online-Shopsystem in Mermaid-Syntax. Berücksichtigen Sie Kunden, ein externes Zahlungssystem und ein externes Versandsystem.',
|
||||
solution: '```mermaid\ngraph TD\n subgraph Systemkontext Online-Shop\n A[Online-Shop] -- Bestelldaten --> B(Kunde)\n B -- Bestellung/Zahlung --> A\n end\n\n A -- Zahlungsanfrage --> C{Zahlungssystem}\n C -- Zahlungsbestätigung --> A\n\n A -- Versandauftrag --> D(Versandsystem)\n D -- Sendungsstatus --> A\n```',
|
||||
points: 6,
|
||||
type: 'mermaid',
|
||||
}
|
||||
],
|
||||
explanation: '### Lösungserklärung\n\n**Anforderungsanalyse**: Eine Anforderung muss präzise sein, damit eine gute Architektur entworfen werden kann. Unklarheiten führen zu Fehlentwicklungen.\n\n**Qualitätsszenarien**: Ein Werkzeug, um nicht-funktionale Anforderungen konkret und messbar zu machen. Das Schema (Quelle, Stimulus, etc.) stellt sicher, dass alle relevanten Aspekte bedacht werden.\n\n**ISO/IEC 9126**: Ein Standard zur Klassifizierung von Softwarequalität, der hilft, Diskussionen über Qualität zu strukturieren.\n\n**Randbedingungen & Risikofaktoren**: **Randbedingungen** sind harte Fakten, die die Architektur von außen beeinflussen (z.B. Gesetze, bestehende IT-Infrastruktur). **Risikofaktoren** sind potenzielle Probleme, die aktiv gemanagt werden müssen (z.B. die Unerfahrenheit des Teams mit einer neuen Technologie).\n\n**Systemkontextdiagramm**: Zeigt das System als Blackbox und definiert seine Grenzen, indem es alle externen Akteure und die Datenflüsse zwischen ihnen darstellt. Dies schafft ein grundlegendes Verständnis darüber, was Teil des Systems ist und was nicht.\n\n### Quellen\n*Folien 1.2, Seite 15-58*'
|
||||
}
|
||||
];
|
138
data/swa_exercises.2.ts
Normal file
138
data/swa_exercises.2.ts
Normal file
@ -0,0 +1,138 @@
|
||||
|
||||
import { Exercise } from '../types';
|
||||
|
||||
export const swaExercises2: Exercise[] = [
|
||||
{
|
||||
id: 'swa-2-1-datenmodell',
|
||||
title: '2.1 - SWA - Fachliches Datenmodell',
|
||||
parts: [
|
||||
{
|
||||
id: '2-1-a',
|
||||
prompt: 'Was sollte ein fachliches Datenmodell beinhalten und welche Notation wird typischerweise verwendet?',
|
||||
solution: 'Ein fachliches Datenmodell sollte aus fachlicher Sicht (nicht Implementierungssicht) erstellt werden und die relevanten Konzepte, Beziehungen und Regeln der Domäne darstellen.\n\nEs enthält typischerweise:\n- Klassen, die fachliche Konzepte darstellen\n- Attribute mit fachlichen Datentypen (keine technischen Typen wie int, string)\n- Assoziationen zwischen den Klassen mit Kardinalitäten\n\nAls Notation wird oft ein UML-Klassendiagramm verwendet.',
|
||||
points: 6,
|
||||
type: 'text',
|
||||
},
|
||||
{
|
||||
id: '2-1-b',
|
||||
prompt: 'Worin besteht der Unterschied zwischen Entitäten und fachlichen Datentypen (Value Types)? Geben Sie jeweils ein Beispiel an.',
|
||||
solution: 'Entitäten haben eine eindeutige Identität und einen Lebenszyklus (z.B. Kunde mit Kundennummer). Fachliche Datentypen (Value Types) modellieren nur Werte ohne eigene Identität (z.B. ISBN, Adresse, Datum).',
|
||||
points: 5,
|
||||
type: 'text',
|
||||
},
|
||||
{
|
||||
id: '2-1-c',
|
||||
prompt: 'Worin besteht der Unterschied zwischen technischen und fachlichen Schlüsseln? Geben Sie außerdem jeweils ein Beispiel an.',
|
||||
solution: 'Technische Schlüssel werden aus technischen Gründen hinzugefügt, haben keine fachliche Bedeutung und werden oft automatisch generiert (z.B. ID = 42389). Fachliche Schlüssel haben eine fachliche Bedeutung und sind oft für Menschen lesbar (z.B. Kundennummer = "K-12345").',
|
||||
points: 5,
|
||||
type: 'text',
|
||||
},
|
||||
{
|
||||
id: '2-1-d',
|
||||
prompt: 'Nach welchen Kriterien sollte man ein fachliches Datenmodell bewerten?',
|
||||
solution: 'Ein gutes fachliches Datenmodell sollte:\n- Fachliche Konzepte korrekt abbilden\n- Fachliche (keine technischen) Datentypen verwenden\n- Sinnvolle Beziehungen mit korrekten Kardinalitäten darstellen\n- Verständlich für Fachexperten sein\n- Konsistent in der Namensgebung und redundanzfrei sein',
|
||||
points: 6,
|
||||
type: 'text',
|
||||
}
|
||||
],
|
||||
explanation: '### Lösungserklärung\n\n**Fachliches Datenmodell**: Dies ist ein zentrales Artefakt, um die Domäne zu verstehen. Es fokussiert auf das **Was** (fachliche Konzepte), nicht das **Wie** (technische Implementierung). Es ist eine Kommunikationsgrundlage zwischen Fachexperten und Entwicklern.\n\n**Entitäten vs. Fachliche Datentypen (Value Objects)**\n- **Entitäten** sind Objekte, die durch ihre **eindeutige Identität** definiert werden. Ein Kunde bleibt derselbe Kunde, auch wenn sich seine Adresse ändert.\n- **Fachliche Datentypen** (Value Objects) haben **keine Identität**. Sie werden durch ihre Werte definiert. Eine Adresse "Musterstraße 1" ist identisch mit jeder anderen Adresse "Musterstraße 1".\n\n**Technische vs. Fachliche Schlüssel**\n- **Fachliche Schlüssel** sind Identifikatoren aus der Domäne (z.B. ISBN). Nachteil: Sie können sich ändern.\n- **Technische Schlüssel** (Surrogatschlüssel) sind künstliche IDs ohne fachliche Bedeutung (z.B. eine auto-inkrementierte Zahl). Vorteil: Sie sind stabil.\n\n### Quellen\n*Folien 2.1, Seite 4-29*'
|
||||
},
|
||||
{
|
||||
id: 'swa-2-3-canvas',
|
||||
title: '2.3 - SWA - Architecture Inception Canvas',
|
||||
parts: [
|
||||
{
|
||||
id: '2-3-a',
|
||||
prompt: 'Was ist das primäre Ziel eines Canvas im Kontext der Softwareentwicklung?',
|
||||
solution: 'Das primäre Ziel eines Canvas ist, ein komplexes Thema (wie eine Geschäfts- oder Architektur-Idee) in klar abgegrenzte Bereiche aufzuteilen und visuell zu strukturieren. Es dient als Werkzeug zur Kommunikation und Analyse, um schnell Klarheit zu schaffen und die Zusammenarbeit im Team zu fördern.',
|
||||
points: 5,
|
||||
type: 'text',
|
||||
},
|
||||
{
|
||||
id: '2-3-b',
|
||||
prompt: 'Was ist das Architecture Inception Canvas?',
|
||||
solution: 'Das Architecture Inception Canvas ist ein strukturiertes Framework zur frühzeitigen Erfassung architekturrelevanter Informationen. Es dient als gemeinsame Grundlage für Architekturentscheidungen und hilft dabei, den Kontext und die wesentlichen Einflussfaktoren eines Projekts schnell zu verstehen.',
|
||||
points: 6,
|
||||
type: 'text',
|
||||
},
|
||||
{
|
||||
id: '2-3-c',
|
||||
prompt: 'Erklären Sie drei der Kategorien des Architecture Inception Canvas und geben Sie für jede Kategorie ein konkretes Beispiel an.',
|
||||
solution: '1. Functional Overview: Beschreibt die wichtigsten funktionalen Anforderungen. Beispiel: "Benutzer können Produkte suchen, in den Warenkorb legen und Bestellungen aufgeben".\n2. Quality Goals: Die drei wichtigsten Qualitätsziele für die Architektur. Beispiel: "Das System muss bei 10.000 gleichzeitigen Nutzern eine Antwortzeit unter 1 Sekunde gewährleisten".\n3. Technical Constraints: Technische Anforderungen, die die Freiheit einschränken. Beispiel: "Das System muss mit der bestehenden Oracle-Datenbank kompatibel sein".',
|
||||
points: 7,
|
||||
type: 'text',
|
||||
}
|
||||
],
|
||||
explanation: '### Lösungserklärung\n\n**Zweck des Canvas**\nEin Canvas ist ein Kommunikations- und Analysewerkzeug. Das **Architecture Inception Canvas** im Speziellen hilft Teams, zu Beginn eines Projekts schnell ein gemeinsames Verständnis ("Big Picture") für die wichtigsten architektonischen Treiber zu entwickeln. Es strukturiert die Diskussion und stellt sicher, dass wesentliche Aspekte nicht übersehen werden.\n\n**Ausgewählte Kategorien**\n- **Functional Overview:** Fasst den Kern dessen zusammen, was das System tun soll.\n- **Quality Goals:** Zwingt das Team zur Priorisierung der wichtigsten nicht-funktionalen Anforderungen (Qualitätsziele), die die Architektur maßgeblich beeinflussen.\n- **Technical Constraints:** Listet nicht verhandelbare technische Randbedingungen auf, die den Lösungsraum von vornherein einschränken.\n\n### Quellen\n*Folien 2.3, Seite 2-6*'
|
||||
},
|
||||
{
|
||||
id: 'swa-2-5-sichten',
|
||||
title: '2.5 - SWA - Entwurf der Sichten',
|
||||
parts: [
|
||||
{
|
||||
id: '2-5-a',
|
||||
prompt: 'Welche Sichten gibt es nach Starke und was zeigen sie?',
|
||||
solution: 'Nach Gernot Starke gibt es vier Sichten:\n1. Kontextabgrenzung: Zeigt die Systemgrenzen und Beziehungen zu Nachbarsystemen.\n2. Bausteinsicht: Zeigt die statische Struktur des Systems mit seinen Komponenten.\n3. Laufzeitsicht: Zeigt das dynamische Verhalten und Zusammenspiel der Bausteine.\n4. Verteilungssicht: Zeigt die technische Infrastruktur und das Mapping der Software darauf.',
|
||||
points: 5,
|
||||
type: 'text',
|
||||
},
|
||||
{
|
||||
id: '2-5-b',
|
||||
prompt: 'Erklären Sie den Unterschied zwischen T-Architektur und A-Architektur.',
|
||||
solution: 'T-Architektur (Technische Architektur) befasst sich mit dem technischen Rahmen und wiederverwendbaren Querschnittskonzepten (z.B. Logging, Authentifizierung).\n\nA-Architektur (Anwendungsarchitektur) befasst sich mit dem Entwurf der fachlich getriebenen, systemspezifischen Bausteine (z.B. Kundenverwaltung, Bestellprozess).',
|
||||
points: 5,
|
||||
type: 'text',
|
||||
},
|
||||
{
|
||||
id: '2-5-c',
|
||||
prompt: 'Nennen Sie drei wichtige Eigenschaften einer Komponente.',
|
||||
solution: '1. Versteckt Implementierung (Information Hiding): Die interne Realisierung ist nach außen nicht sichtbar.\n2. Bietet Schnittstellen: Definierte Schnittstellen ermöglichen die kontrollierte Kommunikation.\n3. Klar definierte Verantwortlichkeit: Jede Komponente hat einen spezifischen, klar abgegrenzten Zweck.',
|
||||
points: 4,
|
||||
type: 'text',
|
||||
},
|
||||
{
|
||||
id: '2-5-d',
|
||||
prompt: 'Was versteht man unter "loser" vs. "enger" Kopplung zwischen Komponenten? Welche ist zu bevorzugen und warum?',
|
||||
solution: 'Lose Kopplung bedeutet, dass Komponenten nur wenige Details voneinander kennen und über stabile Schnittstellen interagieren. Enge Kopplung bedeutet, dass Komponenten viele Details voneinander kennen. Lose Kopplung ist zu bevorzugen, da sie die Wartbarkeit, Testbarkeit und Flexibilität des Systems verbessert.',
|
||||
points: 5,
|
||||
type: 'text',
|
||||
},
|
||||
{
|
||||
id: '2-5-e',
|
||||
prompt: 'Skizzieren Sie sinnvolle Abhängigkeiten zwischen den Komponenten eines E-Commerce-Systems (OrderManagement, CustomerManagement, ProductCatalog, Payment, Shipping).',
|
||||
solution: 'Sinnvolle Abhängigkeiten wären:\n- OrderManagement → CustomerManagement (benötigt Kundendaten)\n- OrderManagement → ProductCatalog (benötigt Produktdaten)\n- OrderManagement → Payment (nutzt Zahlungsabwicklung)\n- OrderManagement → Shipping (nutzt Versandabwicklung)\n\nDie Abhängigkeiten zeigen von der zentralen Prozesskomponente (OrderManagement) auf die unterstützenden, stabileren Komponenten.',
|
||||
points: 6,
|
||||
type: 'text',
|
||||
},
|
||||
{
|
||||
id: '2-5-f',
|
||||
prompt: 'Nennen Sie drei Regeln für gutes API-Design.',
|
||||
solution: '1. Präzise und selbsterklärende Namen: Methodennamen und Parameter sollten ihre Funktion klar beschreiben.\n2. Geheimnisprinzip wahren: Die API sollte das "Was" beschreiben, nicht das "Wie". Keine Implementierungsdetails preisgeben.\n3. Klare Fehlerbeschreibung: Exceptions und Fehlerfälle sollten aussagekräftig und dokumentiert sein.',
|
||||
points: 5,
|
||||
type: 'text',
|
||||
},
|
||||
{
|
||||
id: '2-5-g',
|
||||
prompt: 'Erstellen Sie ein Sequenzdiagramm (Laufzeitsicht) für einen einfachen Bestellprozess in Mermaid-Syntax. Beteiligt sein sollen ein Client, ein OrderService und ein PaymentService.',
|
||||
solution: '```mermaid\nsequenceDiagram\n participant Client\n participant OrderService\n participant PaymentService\n\n Client->>OrderService: createOrder(orderDetails)\n activate OrderService\n OrderService->>PaymentService: processPayment(paymentDetails)\n activate PaymentService\n PaymentService-->>OrderService: paymentSuccessful\n deactivate PaymentService\n OrderService-->>Client: orderConfirmation\n deactivate OrderService\n```',
|
||||
points: 6,
|
||||
type: 'mermaid',
|
||||
},
|
||||
{
|
||||
id: '2-5-h',
|
||||
prompt: 'Beurteilen Sie kritisch: Ein Entwicklungsteam argumentiert: "Wir brauchen keine expliziten Sichten - unser Code ist gut strukturiert und selbsterklärend."',
|
||||
solution: 'Diese Aussage ist kritisch zu betrachten. Selbst gut strukturierter Code zeigt nur Implementierungsdetails, aber kein "Big Picture". Sichten bieten eine höhere Abstraktionsebene, die für die Kommunikation mit verschiedenen Stakeholdern (Nicht-Entwickler), die Einarbeitung neuer Teammitglieder und für grundlegende Architekturentscheidungen unerlässlich ist.',
|
||||
points: 6,
|
||||
type: 'text',
|
||||
},
|
||||
{
|
||||
id: '2-5-i',
|
||||
prompt: 'In welcher Reihenfolge geht man bei der Sichtenerstellung am besten vor?',
|
||||
solution: 'Eine empfohlene, iterative Reihenfolge ist:\n1. Start mit Kontextabgrenzung (definiert die Systemgrenzen).\n2. Erste Verfeinerung in einer Bausteinsicht (Grobentwurf).\n3. Plausibilisierung durch eine Laufzeitsicht (funktioniert der Entwurf für wichtige Szenarien?).\n4. Weitere Verfeinerung der Bausteinsicht und der anderen Sichten im Wechsel.',
|
||||
points: 5,
|
||||
type: 'text',
|
||||
}
|
||||
],
|
||||
explanation: '### Lösungserklärung\n\n**Architektursichten** sind verschiedene Perspektiven auf ein System. Die vier Sichten nach Starke (Kontext, Baustein, Laufzeit, Verteilung) sind ein Standard, um Architektur umfassend zu beschreiben. Die **Erstellung erfolgt iterativ**, oft beginnend mit dem Kontext.\n\nDie **Bausteinsicht** ist die statische "Landkarte" des Systems. Sie zeigt **Komponenten**, die idealerweise eine klare **Verantwortlichkeit** haben, ihre Implementierung verstecken (**Information Hiding**) und über definierte **Schnittstellen** kommunizieren. Das Ziel ist **lose Kopplung** (wenig Abhängigkeiten) und **hohe Kohäsion** (alles in einer Komponente gehört eng zusammen). Hierbei unterscheidet man **A-Architektur** (fachlich) von **T-Architektur** (technisch).\n\nEin gutes **API-Design** ist entscheidend für lose Kopplung. Es sollte präzise, selbsterklärend und stabil sein.\n\nDie **Laufzeitsicht** (z.B. als **Sequenzdiagramm**) zeigt das dynamische Zusammenspiel der statischen Bausteine zur Erfüllung eines Anwendungsfalls. Sie validiert, ob die Bausteinsicht funktioniert.\n\nAuch bei gutem Code sind **explizite Sichten** unverzichtbar. Sie bieten eine Abstraktionsebene über dem Code, die für Kommunikation und das Verständnis des Gesamtsystems ("Big Picture") notwendig ist.\n\n### Quellen\n*Folien 2.5, Seite 3-55*'
|
||||
}
|
||||
];
|
124
data/swa_exercises.3.ts
Normal file
124
data/swa_exercises.3.ts
Normal file
@ -0,0 +1,124 @@
|
||||
|
||||
import { Exercise } from '../types';
|
||||
|
||||
export const swaExercises3: Exercise[] = [
|
||||
{
|
||||
id: 'swa-3-1-backend',
|
||||
title: '3.1 - SWA - Technische Architektur Backend',
|
||||
parts: [
|
||||
{
|
||||
id: '3-1-a',
|
||||
prompt: 'Nennen Sie vier Aufgaben eines Backends.',
|
||||
solution: '1. Funktionalität über APIs exponieren\n2. Geschäftslogik implementieren\n3. Daten verwalten und persistieren\n4. Sicherheit gewährleisten (Authentifizierung & Autorisierung)',
|
||||
points: 4,
|
||||
type: 'text',
|
||||
},
|
||||
{
|
||||
id: '3-1-b',
|
||||
prompt: 'Erläutern Sie die Unterscheidung: „spezifiziertes und unspezifiziertes Verhalten" im Fehlerfall. Geben Sie konkrete Beispiele an.',
|
||||
solution: 'Spezifiziertes Verhalten ist Teil der API-Definition und für den Client erwartbar (z.B. CustomerNotFoundException). Unspezifiziertes Verhalten ist unerwartet und resultiert aus Programmierfehlern (z.B. NullPointerException).',
|
||||
points: 5,
|
||||
type: 'text',
|
||||
},
|
||||
{
|
||||
id: '3-1-c',
|
||||
prompt: 'Wo sollte unspezifiziertes Verhalten in der Architektur behandelt werden?',
|
||||
solution: 'Unspezifiziertes Verhalten (z.B. NullPointerException) sollte in einer zentralen Sicherheitsfacade an der Grenze des Backends behandelt werden. Diese fängt die Fehler ab, loggt sie und transformiert sie in eine generische Fehlermeldung (z.B. HTTP 500 Internal Server Error), um keine Implementierungsdetails nach außen preiszugeben.',
|
||||
points: 4,
|
||||
type: 'text',
|
||||
},
|
||||
{
|
||||
id: '3-1-d',
|
||||
prompt: 'Zeichnen Sie die traditionelle 3-Schichten-Architektur eines Backends in Mermaid-Syntax.',
|
||||
solution: '```mermaid\ngraph TD\n A[Facade Layer] --> B[Logic Layer]\n B --> C[Data Access Layer]\n```',
|
||||
points: 4,
|
||||
type: 'mermaid'
|
||||
},
|
||||
{
|
||||
id: '3-1-e',
|
||||
prompt: 'Zeichnen Sie die typische Schichten-Architektur eines Backends nach der Use Case Driven Schichtenarchitektur.',
|
||||
solution: '```mermaid\ngraph TD\n A[Service-Schicht] --> B[Use Case-Schicht]\n B --> C[DAO-Schicht]\n C --> D[Entity-Schicht]\n```',
|
||||
points: 4,
|
||||
type: 'mermaid'
|
||||
},
|
||||
{
|
||||
id: '3-1-f',
|
||||
prompt: 'Welche Abhängigkeiten sind zwischen den Schichten von zwei Komponenten (A und B) in einer Use Case Driven Architektur erlaubt und welche sollten vermieden werden?',
|
||||
solution: 'Erlaubt sind horizontale Abhängigkeiten auf der Service- und Use Case-Ebene (z.B. Service A ruft Service B auf). Zu vermeiden sind direkte Abhängigkeiten auf die unteren Schichten einer anderen Komponente (z.B. Service A ruft DAO B auf), da dies die Kapselung und Datenhoheit der Komponente B verletzen würde.',
|
||||
points: 6,
|
||||
type: 'text',
|
||||
},
|
||||
{
|
||||
id: '3-1-g',
|
||||
prompt: 'Vergleichen Sie klassische Schichtenarchitektur und Hexagonale Architektur hinsichtlich ihrer Abhängigkeitsrichtungen und Flexibilität.',
|
||||
solution: 'Klassische Schichtenarchitektur: Die Abhängigkeiten verlaufen streng von oben nach unten (Top-Down). Die Flexibilität ist mittel, da Änderungen an unteren Schichten (z.B. DB) obere Schichten beeinflussen können.\n\nHexagonale Architektur: Die Abhängigkeiten verlaufen immer von außen nach innen. Der Kern bleibt technologieunabhängig. Dies bietet hohe Flexibilität, da technische Adapter (z.B. für DB, UI) leicht ausgetauscht werden können, ohne den Kern zu ändern.',
|
||||
points: 6,
|
||||
type: 'text',
|
||||
},
|
||||
{
|
||||
id: '3-1-h',
|
||||
prompt: 'Wozu dient ein DTO und was bedeutet die Abkürzung? Geben Sie außerdem ein Beispiel an für einen Use Case "Bestellung anzeigen".',
|
||||
solution: 'DTO steht für Data Transfer Object. Es ist ein reiner Datencontainer für den Datentransfer zwischen Schichten oder Systemen, um Entkopplung zu schaffen.\nBeispiel für `OrderDetailsDTO`:\n```json\n{\n "orderId": "O-123",\n "customerName": "Max Mustermann",\n "orderDate": "2023-10-27",\n "totalAmount": 99.98,\n "items": [\n { "productId": "P-456", "productName": "Laptop", "quantity": 1, "unitPrice": 99.98 }\n ]\n}\n```',
|
||||
points: 6,
|
||||
type: 'text',
|
||||
}
|
||||
],
|
||||
explanation: '### Lösungserklärung\n\n**Aufgaben des Backends**: Das Backend führt die Kernlogik aus, verwaltet Daten und stellt Funktionalität über APIs bereit.\n\n**Fehlerverhalten**: **Spezifizierte Fehler** (z.B. "Benutzer nicht gefunden") sind Teil des API-Vertrags. **Unspezifizierte Fehler** (Bugs) dürfen nie direkt zum Client durchdringen. Eine **Sicherheitsfacade** fängt sie ab und gibt eine generische Meldung zurück.\n\n**Architekturmuster**:\n- **Traditionelle Schichtenarchitektur**: Strikte Top-Down-Abhängigkeiten (UI → Logik → Daten). Einfach zu verstehen, aber oft starr.\n- **Use Case Driven Architektur**: Eine Variante, die die Logik-Schicht in spezifische Anwendungsfälle (Use Cases) unterteilt.\n- **Hexagonale Architektur** (Ports & Adapters): Kehrt die Abhängigkeiten um. Äußere Adapter (UI, DB) hängen vom Kern (Logik) ab. Der Kern bleibt unabhängig von der Technologie, was die Austauschbarkeit und Testbarkeit massiv verbessert.\n\n**Data Transfer Object (DTO)**: Ein DTO ist ein Objekt nur zum Transport von Daten. Es entkoppelt die Schnittstelle von der internen Implementierung (z.B. Datenbank-Entities). Dies macht die Architektur flexibler und robuster gegenüber Änderungen.\n\n### Quellen\n*Folien 3.1, Seite 4-27*'
|
||||
},
|
||||
{
|
||||
id: 'swa-3-3-integration',
|
||||
title: '3.3 - SWA – Integrationstechnologien',
|
||||
parts: [
|
||||
{
|
||||
id: '3-3-a',
|
||||
prompt: 'Nennen Sie die zwei grundsätzlichen Muster für die Standardintegration zwischen zwei Systemen und beschreiben Sie jeweils zwei charakteristische Eigenschaften.',
|
||||
solution: '1. Synchrone Integration: Der Aufrufer wartet auf eine Antwort. Eigenschaften: engere Kopplung, direkte Rückmeldung.\n2. Asynchrone Integration: Der Sender schickt eine Nachricht und wartet nicht. Eigenschaften: losere Kopplung, höhere Resilienz bei Ausfällen.',
|
||||
points: 4,
|
||||
type: 'text',
|
||||
},
|
||||
{
|
||||
id: '3-3-b',
|
||||
prompt: 'Welche HTTP-Methoden werden bei REST APIs typischerweise für CRUD-Operationen (Create, Read, Update, Delete) verwendet?',
|
||||
solution: '- Erstellen (Create): POST\n- Lesen (Read): GET\n- Vollständige Aktualisierung (Update): PUT\n- Löschen (Delete): DELETE\n(Partielle Aktualisierung: PATCH)',
|
||||
points: 4,
|
||||
type: 'text',
|
||||
},
|
||||
{
|
||||
id: '3-3-c',
|
||||
prompt: 'Definieren Sie beispielhafte REST-API Endpunkte (Methode, URL, Statuscodes) für eine Kundenverwaltung.',
|
||||
solution: '- GET /customers (200 OK): Alle Kunden lesen\n- GET /customers/{id} (200 OK / 404 Not Found): Einen bestimmten Kunden lesen\n- POST /customers (201 Created): Einen neuen Kunden erstellen\n- PUT /customers/{id} (200 OK / 204 No Content): Einen Kunden vollständig aktualisieren\n- DELETE /customers/{id} (204 No Content): Einen Kunden löschen',
|
||||
points: 5,
|
||||
type: 'text',
|
||||
},
|
||||
{
|
||||
id: '3-3-d',
|
||||
prompt: 'Erläutern Sie den Begriff EDA (Event Driven Architecture).',
|
||||
solution: 'EDA (Event Driven Architecture) ist ein Architekturmuster, bei dem die Kommunikation und der Ablauf durch Ereignisse (Events) gesteuert werden. Komponenten (Producer, Consumer) sind lose über einen Event-Broker gekoppelt und kommunizieren asynchron. Dies fördert Skalierbarkeit und Resilienz.',
|
||||
points: 5,
|
||||
type: 'text',
|
||||
},
|
||||
{
|
||||
id: '3-3-e',
|
||||
prompt: 'Wozu dient das Protokoll AMQP und was sind seine Vorteile?',
|
||||
solution: 'AMQP (Advanced Message Queuing Protocol) ist ein offenes, plattformunabhängiges Protokoll für den asynchronen Nachrichtenaustausch.\n\nVorteile:\n- Interoperabilität zwischen verschiedenen Systemen und Sprachen.\n- Zuverlässige Nachrichtenübermittlung (mit Queues, Bestätigungen etc.).\n- Flexibles Routing von Nachrichten (z.B. Publish/Subscribe).',
|
||||
points: 5,
|
||||
type: 'text',
|
||||
},
|
||||
{
|
||||
id: '3-3-f',
|
||||
prompt: 'Ein E-Commerce-System soll mit einem Zahlungsdienstleister integriert werden. Welche Integrationsart (synchron/asynchron) und Technologie (REST/Messaging) würden Sie empfehlen und warum?',
|
||||
solution: 'Eine kombinierte Lösung ist oft am besten:\n- Synchrone Integration via REST für die initiale Zahlungsautorisierung, da der Kunde eine sofortige Rückmeldung benötigt.\n- Asynchrone Integration via Messaging (AMQP) für nachgelagerte Prozesse wie die finale Buchungsbestätigung oder Benachrichtigungen. Dies entkoppelt die Systeme und erhöht die Resilienz, falls der Zahlungsdienstleister temporär nicht sofort antwortet.',
|
||||
points: 6,
|
||||
type: 'text',
|
||||
},
|
||||
{
|
||||
id: '3-3-g',
|
||||
prompt: 'Warum wird Systemintegration in Projekten häufig unterschätzt? Nennen Sie zwei Faktoren, die sie komplex machen.',
|
||||
solution: 'Systemintegration wird oft unterschätzt, weil sie mehr als nur das Verbinden von zwei APIs ist.\n\nKomplexe Faktoren sind:\n1. Heterogene Systemlandschaften: Unterschiedliche Technologien, Plattformen und Datenformate müssen überbrückt werden.\n2. Fehlerbehandlung und Konsistenz: Robuste Strategien für den Umgang mit Ausfällen und die Sicherstellung der Datenkonsistenz über Systemgrenzen hinweg sind schwierig zu implementieren.',
|
||||
points: 5,
|
||||
type: 'text',
|
||||
},
|
||||
],
|
||||
explanation: '### Lösungserklärung\n\n**Integrationsmuster**: **Synchron** (z.B. REST-Aufruf) bedeutet, der Aufrufer wartet auf eine Antwort. **Asynchron** (z.B. Messaging) bedeutet, der Sender schickt eine Nachricht und wartet nicht.\n\n**REST**: Ein Architekturstil, der HTTP-Methoden für CRUD-Operationen auf Ressourcen nutzt. Die URL-Struktur (`/customers/{id}`) identifiziert die Ressource, die Methode (`GET`, `POST`, etc.) die Operation.\n\n**EDA (Event Driven Architecture)**: Ein Paradigma, das auf asynchroner Kommunikation durch **Events** basiert. Es führt zu extrem loser Kopplung und ist die Grundlage für viele moderne Microservice-Systeme.\n\n**AMQP**: Ein standardisiertes Protokoll für asynchrones Messaging, das von Brokern wie RabbitMQ implementiert wird. Es sorgt für Interoperabilität und zuverlässige Nachrichtenübermittlung.\n\n**Komplexität der Integration**: Die Herausforderung liegt oft im Detail: unterschiedliche Datenmodelle, die gemappt werden müssen; komplexe Fehlerbehandlungsstrategien für den Fall, dass ein System nicht erreichbar ist; und die Gewährleistung von Datenkonsistenz über verteilte Systeme hinweg.\n\n### Quellen\n*Folien 3.3, Seite 5-42*'
|
||||
}
|
||||
];
|
61
data/swa_exercises.4.ts
Normal file
61
data/swa_exercises.4.ts
Normal file
@ -0,0 +1,61 @@
|
||||
|
||||
import { Exercise } from '../types';
|
||||
|
||||
export const swaExercises4: Exercise[] = [
|
||||
{
|
||||
id: 'swa-3-5-moderne-architekturen',
|
||||
title: '3.5 - SWA - Moderne Architekturen',
|
||||
parts: [
|
||||
{
|
||||
id: '3-5-a',
|
||||
prompt: 'Was kennzeichnet einen Modulith (Modularer Monolith)?',
|
||||
solution: 'Ein Modulith ist ein monolithisches System, das intern in gut strukturierte Module mit klaren Grenzen und Schnittstellen aufgeteilt ist. Es wird als eine einzige Einheit deployed, kombiniert aber die Einfachheit eines Monolithen mit besserer interner Struktur und Wartbarkeit.',
|
||||
points: 5,
|
||||
type: 'text',
|
||||
},
|
||||
{
|
||||
id: '3-5-b',
|
||||
prompt: 'Nenne drei der fünf wesentlichen Eigenschaften für „Cloud" nach NIST.',
|
||||
solution: 'Drei von fünf Eigenschaften sind:\n1. On-Demand self-service: Nutzer können Ressourcen bei Bedarf automatisch bereitstellen.\n2. Broad network access: Dienste sind über Standard-Netzwerke zugänglich.\n3. Resource pooling: Ressourcen des Anbieters werden für mehrere Kunden gebündelt.\n(Weitere: Rapid elasticity, Measured service)',
|
||||
points: 5,
|
||||
type: 'text',
|
||||
},
|
||||
{
|
||||
id: '3-5-c',
|
||||
prompt: 'Beschreiben Sie das "Conway\'s Law" und erklären Sie, warum es bei Microservices-Architekturen besonders relevant ist.',
|
||||
solution: 'Conway\'s Law besagt, dass die Architektur eines Systems die Kommunikationsstruktur der entwickelnden Organisation widerspiegelt. Bei Microservices ist dies relevant, weil es nahelegt, Teams entsprechend der Service-Grenzen zu organisieren ("Inverse Conway Maneuver"), um autonome Teams zu fördern, die für ihre jeweiligen Services End-to-End verantwortlich sind.',
|
||||
points: 6,
|
||||
type: 'text',
|
||||
},
|
||||
{
|
||||
id: '3-5-d',
|
||||
prompt: 'Ein E-Commerce-Unternehmen möchte sein monolithisches System modernisieren. Welche Vor- und Nachteile haben die Ansätze Modulith und Microservices in diesem Kontext?',
|
||||
solution: 'Modulith:\n(+) Geringere Betriebskomplexität, einfachere Transaktionen, schrittweises Refactoring.\n(-) Gemeinsame Release-Zyklen, begrenzte technologische Freiheit.\n\nMicroservices:\n(+) Unabhängige Entwicklung & Deployment, bessere Skalierbarkeit, technologische Freiheit.\n(-) Höhere Komplexität (verteiltes System), aufwändigere Infrastruktur, Herausforderungen bei Datenkonsistenz.',
|
||||
points: 7,
|
||||
type: 'text',
|
||||
},
|
||||
{
|
||||
id: '3-5-e',
|
||||
prompt: 'Erläutern Sie zwei der vier vorgestellten Resilienz-Patterns (Timeouts, Bulkheads, Fallbacks, Circuit Breaker).',
|
||||
solution: '1. Timeouts: Kapselt Aufrufe mit einer maximalen Wartezeit, um zu verhindern, dass ein langsamer Service das ganze System blockiert ("Fail fast").\n2. Circuit Breaker: Stoppt nach einer bestimmten Anzahl von Fehlern weitere Aufrufe an einen gestörten Dienst, um diesem Zeit zur Erholung zu geben und Kaskadenausfälle zu vermeiden. Nach einer Wartezeit wird der Zustand geprüft (halb-offen).',
|
||||
points: 6,
|
||||
type: 'text',
|
||||
},
|
||||
{
|
||||
id: '3-5-f',
|
||||
prompt: 'Welche Probleme kann es bei einer Migration eines Monolithen zu Microservices geben? Nennen Sie zwei.',
|
||||
solution: '1. Falscher Service-Schnitt: Ein ungeeigneter Schnitt führt zu stark gekoppelten Services ("verteilter Monolith"), was die Komplexität erhöht, anstatt sie zu reduzieren.\n2. Datenabhängigkeiten: Die Aufteilung einer gemeinsamen Datenbank in separate Datenbanken pro Service ist extrem komplex und birgt Risiken für die Datenkonsistenz (Stichwort: verteilte Transaktionen).',
|
||||
points: 6,
|
||||
type: 'text',
|
||||
},
|
||||
{
|
||||
id: '3-5-g',
|
||||
prompt: 'Entwerfen Sie eine Event-getriebene Architektur (EDA) für einen Online-Buchungsservice. Definieren Sie zwei Komponenten und zwei nötige Events für die Kommunikation.',
|
||||
solution: 'Komponenten:\n1. BookingService: Verwaltet Buchungen.\n2. PaymentService: Wickelt Zahlungen ab.\n\nEvents:\n1. BookingCreatedEvent: Ausgelöst vom BookingService, um den Zahlungsprozess im PaymentService zu initiieren.\n2. PaymentCompletedEvent: Ausgelöst vom PaymentService, um den BookingService zu informieren, dass die Buchung nun bestätigt werden kann.',
|
||||
points: 7,
|
||||
type: 'text',
|
||||
},
|
||||
],
|
||||
explanation: '### Lösungserklärung\n\n**Modulith**: Ein Kompromiss zwischen Monolith und Microservices. Er behält den einfachen Betrieb eines Monolithen bei, erzwingt aber eine saubere, modulare Innenstruktur, was die Wartbarkeit verbessert.\n\n**Cloud-Eigenschaften (NIST)**: Die fünf Merkmale (On-demand self-service, Broad network access, Resource pooling, Rapid elasticity, Measured service) definieren, was einen echten Cloud-Service von traditionellem Hosting unterscheidet.\n\n**Conway\'s Law**: Stellt einen Zusammenhang zwischen Organisations- und Systemstruktur her. Für Microservices wird es oft umgekehrt genutzt (**Inverse Conway Maneuver**): Man gestaltet die Organisation (kleine, autonome Teams), damit die gewünschte Architektur (kleine, unabhängige Services) entsteht.\n\n**Resilienz-Patterns**: In verteilten Systemen sind Teilausfälle normal. Patterns wie **Timeouts** (verhindern ewiges Warten) und **Circuit Breakers** (schützen vor Überlastung gestörter Dienste) sind essenziell, um die Stabilität des Gesamtsystems zu gewährleisten.\n\n**Migration zum Microservice**: Eine "Big Bang"-Migration ist extrem riskant. Ein evolutionärer Ansatz ist besser. Die größten **Probleme** bei der Migration sind oft das Finden der richtigen Service-Grenzen (Schnitt) und der Umgang mit der gemeinsamen Datenbank.\n\n**EDA**: Eine Event-getriebene Architektur entkoppelt Komponenten stark voneinander. Ein Service sendet ein **Event** (z.B. "Buchung erstellt"), andere Services reagieren darauf. Dies erhöht die Resilienz und Skalierbarkeit.\n\n### Quellen\n*Folien 3.5, Seite 4-39*'
|
||||
}
|
||||
];
|
138
data/swa_exercises.5.ts
Normal file
138
data/swa_exercises.5.ts
Normal file
@ -0,0 +1,138 @@
|
||||
|
||||
import { Exercise } from '../types';
|
||||
|
||||
export const swaExercises5: Exercise[] = [
|
||||
{
|
||||
id: 'swa-4-1-doku',
|
||||
title: '4.1 - SWA – Architekturdokumentation',
|
||||
parts: [
|
||||
{
|
||||
id: '4-1-a',
|
||||
prompt: 'Warum sollte man Architektur dokumentieren? Was sind die Herausforderungen?',
|
||||
solution: 'Gründe für Dokumentation:\n- Kommunikation: Gemeinsames Verständnis für alle Stakeholder schaffen.\n- Wissensbewahrung: Architekturwissen langfristig sichern.\n- Nachvollziehbarkeit: Die Rationale hinter Designentscheidungen festhalten.\n\nHerausforderungen:\n- Veraltete Dokumentation: Die Doku wird nicht synchron mit dem Code gehalten.\n- Falscher Detailgrad: Entweder zu viel oder zu wenig Information.\n- Hoher Aufwand: Dokumentation wird als Belastung empfunden.',
|
||||
points: 6,
|
||||
type: 'text',
|
||||
},
|
||||
{
|
||||
id: '4-1-b',
|
||||
prompt: 'Beschreiben Sie vier wichtige Kapitel des arc42-Templates.',
|
||||
solution: '1. Einführung und Ziele: Definiert die Aufgabenstellung, Qualitätsziele und Stakeholder.\n2. Kontextabgrenzung: Zeigt die Grenzen des Systems und seine Schnittstellen zu Nachbarsystemen.\n3. Bausteinsicht: Beschreibt die statische Struktur des Systems in Form von Komponenten und deren Beziehungen.\n4. Architekturentscheidungen: Hält wichtige Designentscheidungen und deren Begründungen fest (z.B. als ADRs).',
|
||||
points: 6,
|
||||
type: 'text',
|
||||
},
|
||||
{
|
||||
id: '4-1-c',
|
||||
prompt: 'Was ist ein ADR? Was sind die Vorteile?',
|
||||
solution: 'Ein ADR (Architecture Decision Record) ist ein Dokument, das eine einzelne, wichtige Architekturentscheidung, ihren Kontext und ihre Konsequenzen festhält.\n\nVorteile:\n1. Transparenz: Macht die Gründe für Entscheidungen nachvollziehbar ("Was haben wir uns dabei gedacht?").\n2. Wissensbewahrung: Erleichtert das Onboarding neuer Teammitglieder und verhindert, dass Wissen verloren geht.',
|
||||
points: 5,
|
||||
type: 'text',
|
||||
},
|
||||
{
|
||||
id: '4-1-d',
|
||||
prompt: 'Nach welchen Kriterien kann man einen ADR bewerten?',
|
||||
solution: '1. Vollständigkeit: Enthält der ADR alle wichtigen Abschnitte (Kontext, Entscheidung, Konsequenzen)?\n2. Klarheit des Problems: Wird das zugrundeliegende Problem klar beschrieben?\n3. Alternativen: Wurden vernünftige Alternativen betrachtet und analysiert?\n4. Begründung: Ist die Begründung für die gewählte Lösung nachvollziehbar?\n5. Konsequenzen: Werden sowohl positive als auch negative Konsequenzen dargestellt?',
|
||||
points: 6,
|
||||
type: 'text',
|
||||
}
|
||||
],
|
||||
explanation: '### Lösungserklärung\n\n**Architekturdokumentation** ist kein Selbstzweck, sondern dient der **Kommunikation**, **Wissensbewahrung** und **Nachvollziehbarkeit**. Die größte **Herausforderung** ist, sie aktuell, relevant und mit angemessenem Aufwand zu pflegen.\n\n**arc42** ist eine praxiserprobte Vorlage, die eine Standardstruktur für Architekturdokumentation bietet und sicherstellt, dass alle wichtigen Aspekte (Ziele, Kontext, Bausteine, Entscheidungen etc.) abgedeckt werden.\n\n**ADRs (Architecture Decision Records)** sind leichtgewichtige Dokumente, die jeweils eine wichtige Architekturentscheidung festhalten. Sie dokumentieren nicht nur das *Was*, sondern vor allem das *Warum*. Eine Sammlung von ADRs bildet ein wertvolles Protokoll der Architektur-Evolution.\n\n### Quellen\n*Folien 4.1, Seite 5-26*'
|
||||
},
|
||||
{
|
||||
id: 'swa-4-3-wissen',
|
||||
title: '4.3 - SWA – Wissenstransfer',
|
||||
parts: [
|
||||
{
|
||||
id: '4-3-a',
|
||||
prompt: 'Warum ist expliziter Wissenstransfer in Softwareprojekten wichtig?',
|
||||
solution: 'Reine Dokumentation reicht nicht aus. Expliziter Wissenstransfer (z.B. in Architektur-Sessions) ist wichtig, um:\n- Die Motivation hinter Regeln und Entscheidungen zu erklären ("Beraten statt vorschreiben").\n- Ein gemeinsames Verständnis zu schaffen und Diskussionen zu ermöglichen.\n- Architekturerosion zu vermeiden, indem das Team die Architektur versteht und mitträgt.\n- Neue Teammitglieder effektiv einzuarbeiten.',
|
||||
points: 6,
|
||||
type: 'text',
|
||||
},
|
||||
{
|
||||
id: '4-3-b',
|
||||
prompt: 'Was ist ein Architecture Communication Canvas? Erläutern Sie zwei Felder daraus.',
|
||||
solution: 'Der Architecture Communication Canvas ist ein Werkzeug, um Softwarearchitektur klar und strukturiert zu kommunizieren.\n\nBeispiel-Felder:\n1. System Purpose & Key Features: Beschreibt den Hauptzweck und die wichtigsten Funktionen des Systems.\n2. Key Challenges & Solutions: Listet die wichtigsten Herausforderungen und deren architektonische Lösungsansätze auf.',
|
||||
points: 6,
|
||||
type: 'text',
|
||||
},
|
||||
{
|
||||
id: '4-3-c',
|
||||
prompt: 'Wie kann man den Architecture Communication Canvas in einen agilen Entwicklungsprozess integrieren?',
|
||||
solution: 'Der Canvas kann in agilen Ritualen genutzt werden:\n- Im Sprint Planning: Zur Kommunikation architekturrelevanter User Stories und deren Abhängigkeiten.\n- Im Sprint Review: Zur Visualisierung der umgesetzten Architekturaspekte für Stakeholder.\n- In Retrospektiven: Zur Bewertung und Verbesserung von Architekturentscheidungen.',
|
||||
points: 6,
|
||||
type: 'text',
|
||||
},
|
||||
{
|
||||
id: '4-3-d',
|
||||
prompt: 'Welche spezifischen Herausforderungen sehen Sie beim Wissenstransfer in agilen Teams und wie begegnen Sie diesen?',
|
||||
solution: 'Herausforderungen in agilen Teams:\n- Zeitdruck: Schnelle Sprints lassen wenig Zeit für formelle Dokumentation.\n- Sich ändernde Anforderungen: Architektur und Wissen müssen sich kontinuierlich anpassen.\n\nLösungsansätze:\n- Leichtgewichtige Formate wie ADRs und Canvases verwenden.\n- Wissenstransfer in agile Rituale integrieren (z.B. Sprint-Reviews, Dailies).\n- "Living Documentation", die sich teils automatisch generiert.',
|
||||
points: 7,
|
||||
type: 'text',
|
||||
}
|
||||
],
|
||||
explanation: '### Lösungserklärung\n\n**Wissenstransfer**: Dokumentation allein ist passiv. Aktiver **Wissenstransfer** in Sessions und Meetings ist entscheidend, um die "Warum"-Fragen zu klären und sicherzustellen, dass die Architektur vom Team verstanden und gelebt wird. Nur so lässt sich Architekturerosion wirksam bekämpfen.\n\n**Architecture Communication Canvas**: Ein Werkzeug, das hilft, das "Big Picture" einer Architektur auf einer Seite zu visualisieren. Es ist ideal für die Kommunikation mit verschiedenen Stakeholdern und lässt sich gut in **agile Prozesse** integrieren.\n\n**Agiler Wissenstransfer**: In **agilen Teams** ist der formale Dokumentationsaufwand oft geringer. Umso wichtiger sind kontinuierliche Kommunikation und leichtgewichtige Werkzeuge. Wissen muss fließen. Statt großer Dokumente werden oft kleine, fokussierte Artefakte (ADRs) und regelmäßige Austauschformate bevorzugt, um mit der hohen Änderungsgeschwindigkeit Schritt zu halten.\n\n### Quellen\n*Folien 4.3, Seite 2-5*'
|
||||
},
|
||||
{
|
||||
id: 'swa-4-5-bewertung',
|
||||
title: '4.5 - SWA - Architekturbewertung mit LASR',
|
||||
parts: [
|
||||
{
|
||||
id: '4-5-a',
|
||||
prompt: 'Warum sind Architektur-Reviews wichtig?',
|
||||
solution: 'Architektur-Reviews sind wichtig, weil sie:\n- Risiken frühzeitig identifizieren, bevor sie teuer zu beheben sind.\n- Die Architektur gegen die definierten Qualitätsziele validieren.\n- Den Wissenstransfer und ein gemeinsames Verständnis im Team fördern.\n- Helfen, technische Schulden zu erkennen und zu vermeiden.',
|
||||
points: 5,
|
||||
type: 'text',
|
||||
},
|
||||
{
|
||||
id: '4-5-b',
|
||||
prompt: 'Erläutern Sie den Unterschied zwischen quantitativer und qualitativer Analyse bei Architektur-Reviews.',
|
||||
solution: 'Quantitative Analyse ist Tool-basiert und liefert messbare, objektive Metriken (z.B. Kopplungsmetriken, Code Coverage). Qualitative Analyse ist Workshop-basiert (z.B. LASR, ATAM) und basiert auf der subjektiven Bewertung und Diskussion durch Experten. Sie erfasst komplexe Zusammenhänge, die Metriken nicht sehen.',
|
||||
points: 6,
|
||||
type: 'text',
|
||||
},
|
||||
{
|
||||
id: '4-5-c',
|
||||
prompt: 'Nennen Sie ein Beispiel für eine Architekturmetrik.',
|
||||
solution: 'Ein Beispiel ist die ACD/NCCD Metrik (Average Component Dependency). Sie misst die durchschnittliche Anzahl direkter und indirekter Abhängigkeiten einer Komponente. Ein hoher Wert kann auf eine zu starke Kopplung oder zyklische Abhängigkeiten im System hinweisen.',
|
||||
points: 4,
|
||||
type: 'text',
|
||||
},
|
||||
{
|
||||
id: '4-5-d',
|
||||
prompt: 'Erläutern Sie den Begriff „Pre-Mortem" im Rahmen der Architekturbewertung.',
|
||||
solution: 'Pre-Mortem ist eine Bewertungstechnik, bei der man sich vorstellt, das Projekt sei in der Zukunft gescheitert. Das Team analysiert dann rückblickend die möglichen Gründe für dieses Scheitern. Dies hilft, proaktiv Risiken zu identifizieren und Gegenmaßnahmen einzuleiten, bevor die Probleme tatsächlich eintreten.',
|
||||
points: 5,
|
||||
type: 'text',
|
||||
},
|
||||
{
|
||||
id: '4-5-e',
|
||||
prompt: 'Beschreiben Sie die vier Hauptschritte der LASR-Methodik.',
|
||||
solution: '1. Mission Statement: Ein gemeinsames Verständnis des Systemzwecks schaffen.\n2. Bewertungsmaßstab: Die Top 3-5 Qualitätsziele definieren und als Spinnennetz-Diagramm visualisieren.\n3. Basis-Review: Mit einem strukturierten Pre-Mortem (Risikokarten) Schwächen und Risiken identifizieren, die das Erreichen der Ziele gefährden.\n4. Zielorientierte Analyse: Die Ergebnisse validieren und durch gezielte Diskussionen schärfen.',
|
||||
points: 8,
|
||||
type: 'text',
|
||||
},
|
||||
{
|
||||
id: '4-5-f',
|
||||
prompt: 'Erklären Sie, wie das Spinnennetz-Diagramm in LASR verwendet wird.',
|
||||
solution: 'Das Spinnennetz-Diagramm ist das zentrale Visualisierungselement in LASR. Jede seiner Achsen repräsentiert ein priorisiertes Qualitätsziel. Zuerst wird die Ziellinie (grün) eingetragen, die die angestrebten Werte darstellt. Nach der Risikoanalyse wird die Ergebnislinie eingetragen, die die geschätzte tatsächliche Erreichung zeigt. Die Lücken zwischen den Linien machen Problembereiche sofort sichtbar.',
|
||||
points: 6,
|
||||
type: 'text',
|
||||
},
|
||||
{
|
||||
id: '4-5-g',
|
||||
prompt: 'Nennen Sie zwei Vorteile von LASR gegenüber ATAM.',
|
||||
solution: '1. Geringerer Aufwand: LASR ist schlanker und kann mit dem eigenen Team in kürzerer Zeit durchgeführt werden.\n2. Selbstdurchführbarkeit: LASR kann ohne externe, speziell geschulte Moderatoren durchgeführt werden.',
|
||||
points: 4,
|
||||
type: 'text',
|
||||
},
|
||||
{
|
||||
id: '4-5-h',
|
||||
prompt: 'Diskutieren Sie Vor- und Nachteile der LASR-Methodik gegenüber rein quantitativen Ansätzen.',
|
||||
solution: 'Vorteile von LASR (qualitativ): Erfasst ganzheitlich qualitative Aspekte und Risiken, die Metriken nicht sehen. Fördert das gemeinsame Verständnis im Team.\nNachteile von LASR: Ergebnisse sind subjektiv und hängen von den Teilnehmern ab. Der Aufwand ist höher als bei automatisierten Messungen.\n\nEin idealer Ansatz kombiniert oft beide Methoden.',
|
||||
points: 6,
|
||||
type: 'text',
|
||||
},
|
||||
],
|
||||
explanation: '### Lösungserklärung\n\n**Architektur-Reviews** sind ein entscheidendes Werkzeug zur Qualitätssicherung. Sie helfen, Risiken frühzeitig zu finden und die Architektur zu validieren.\n\nMan unterscheidet **quantitative** (Tool-basierte Metriken) und **qualitative** (Workshop-basierte Diskussion) Analysen. Beide Ansätze ergänzen sich gut.\n\n**Pre-Mortem** ist eine proaktive Risikoanalysetechnik, bei der man ein Scheitern des Projekts annimmt, um dessen mögliche Ursachen zu ergründen.\n\n**LASR (Lean Architecture-System-Review)** ist eine schlanke, qualitative Review-Methode. Die vier Schritte bauen aufeinander auf:\n1. **Mission Statement**: Klärt das "Warum" des Systems.\n2. **Bewertungsmaßstab**: Definiert die wichtigsten Qualitätsziele als messbare Achsen im **Spinnennetz-Diagramm**.\n3. **Basis-Review**: Findet mittels eines strukturierten **Pre-Mortems** Risiken, die diese Ziele gefährden.\n4. **Zielorientierte Analyse**: Vertieft die Diskussion zu den kritischsten Risiken.\n\nIm Vergleich zu umfassenderen Methoden wie **ATAM** ist **LASR** deutlich leichtgewichtiger und schneller durchführbar.\n\n### Quellen\n*Folien 4.5, Seite 2-24*'
|
||||
}
|
||||
];
|
82
data/swa_exercises.6.ts
Normal file
82
data/swa_exercises.6.ts
Normal file
@ -0,0 +1,82 @@
|
||||
|
||||
import { Exercise } from '../types';
|
||||
|
||||
export const swaExercises6: Exercise[] = [
|
||||
{
|
||||
id: 'swa-6-1-ddd-strategic',
|
||||
title: '6.1 - SWA - Domain-Driven Design (Strategic)',
|
||||
parts: [
|
||||
{
|
||||
id: '6-1-a',
|
||||
prompt: 'Was ist Domain-Driven Design (DDD) und was sind seine Kernprinzipien?',
|
||||
solution: 'DDD ist ein Ansatz zur Entwicklung komplexer Software, bei dem die Geschäftsdomäne im Mittelpunkt steht. Kernprinzipien sind: \n1. Fokus auf die Geschäftsdomäne und ihre Logik.\n2. Entwicklung einer gemeinsamen "Ubiquitous Language" zwischen Fachexperten und Entwicklern.\n3. Ein modellgetriebenes Design, bei dem das Domänenmodell direkt in Code umgesetzt wird.',
|
||||
points: 5,
|
||||
type: 'text',
|
||||
},
|
||||
{
|
||||
id: '6-1-b',
|
||||
prompt: 'Erläutern Sie die Ubiquitous Language. Warum ist sie wichtig?',
|
||||
solution: 'Die Ubiquitous Language ist eine gemeinsame, eindeutige Sprache, die von allen Projektbeteiligten (Entwickler, Fachexperten, etc.) verwendet wird. Sie ist wichtig, weil sie Missverständnisse reduziert und sicherstellt, dass das Domänenmodell im Code exakt die Geschäftsdomäne widerspiegelt.',
|
||||
points: 4,
|
||||
type: 'text',
|
||||
},
|
||||
{
|
||||
id: '6-1-c',
|
||||
prompt: 'Definieren Sie Bounded Context und erklären Sie dessen Bedeutung für DDD.',
|
||||
solution: 'Ein Bounded Context definiert die Grenzen, innerhalb derer ein bestimmtes Domänenmodell und die Ubiquitous Language gültig sind. Die Bedeutung liegt in der Komplexitätsbewältigung: Große, komplexe Domänen werden in handhabbare, autonome Teile zerlegt. Dies ermöglicht unabhängige Teamarbeit und technische Entscheidungen pro Kontext.',
|
||||
points: 5,
|
||||
type: 'text',
|
||||
},
|
||||
{
|
||||
id: '6-1-d',
|
||||
prompt: 'Erstellen Sie drei mögliche Bounded Contexts für ein Universitätsverwaltungssystem.',
|
||||
solution: '1. Student Management Context: Verwaltung von Studentendaten, Einschreibungen, Studienverläufe.\n2. Course Management Context: Kursplanung, Vorlesungsverzeichnis, Raumbelegung.\n3. Examination Context: Prüfungsorganisation, Notenverwaltung, Prüfungstermine.',
|
||||
points: 6,
|
||||
type: 'text',
|
||||
},
|
||||
{
|
||||
id: '6-1-e',
|
||||
prompt: 'Was ist eine Context Map und wofür wird sie verwendet?',
|
||||
solution: 'Eine Context Map visualisiert die Beziehungen und Integrationsmuster zwischen verschiedenen Bounded Contexts. Sie wird verwendet, um Abhängigkeiten zu verstehen und die Teamkoordination zu organisieren. Sie definiert, wie die Kontexte technisch und organisatorisch zusammenarbeiten.',
|
||||
points: 5,
|
||||
type: 'text',
|
||||
},
|
||||
{
|
||||
id: '6-1-f',
|
||||
prompt: 'Erklären Sie die Relationship Patterns: Shared Kernel, Customer-Supplier und Anti-Corruption Layer.',
|
||||
solution: '- Shared Kernel: Zwei oder mehr Kontexte teilen sich einen gemeinsamen Codebereich, den sie gemeinsam entwickeln. Führt zu enger Kopplung.\n- Customer-Supplier: Ein Kontext (Supplier) liefert Services für einen anderen (Customer), wobei der Customer die Entwicklung des Suppliers beeinflusst.\n- Anti-Corruption Layer: Eine Übersetzungsschicht schützt den eigenen Kontext vor den Modellen und der Komplexität eines anderen (oft externen oder veralteten) Kontexts.',
|
||||
points: 6,
|
||||
type: 'text',
|
||||
},
|
||||
{
|
||||
id: '6-1-g',
|
||||
prompt: 'Klassifizieren Sie die Bereiche eines E-Commerce-Systems (Order Management, Payment, Inventory Management) in Core, Supporting oder Generic und begründen Sie Ihre Entscheidung.',
|
||||
solution: '- Order Management (Core Domain): Dies ist der Kerngeschäftsprozess, der direkt Umsatz generiert und einen Wettbewerbsvorteil darstellt.\n- Inventory Management (Supporting Subdomain): Es unterstützt das Kerngeschäft, ist aber selbst kein Differenzierungsmerkmal.\n- Payment (Generic Subdomain): Dies ist eine allgemeine Funktionalität, die oft von externen Dienstleistern (z.B. Stripe, PayPal) als Standardlösung eingekauft wird.',
|
||||
points: 6,
|
||||
type: 'text',
|
||||
},
|
||||
{
|
||||
id: '6-1-h',
|
||||
prompt: 'Erklären Sie „Conway\'s Law" und dessen Relevanz für das Design von Bounded Contexts.',
|
||||
solution: 'Conway\'s Law besagt, dass die Architektur eines Systems die Kommunikationsstruktur der Organisation widerspiegelt. Für Bounded Contexts ist dies relevant, da die Grenzen eines Contexts idealerweise den Grenzen eines Entwicklungsteams entsprechen sollten. Dies fördert die Autonomie und reduziert den Kommunikationsaufwand zwischen den Teams.',
|
||||
points: 5,
|
||||
type: 'text',
|
||||
},
|
||||
{
|
||||
id: '6-1-i',
|
||||
prompt: 'Inwiefern sind Team Topologies eine Alternative zu Strategic Design?',
|
||||
solution: 'Team Topologies bieten eine alternative, oft praktischere Herangehensweise. Während Strategic Design sich stark auf die fachliche Domäne konzentriert, um Grenzen zu finden, berücksichtigen Team Topologies auch andere "Fracture Planes" (Aufteilungskriterien) wie Technologie, Compliance oder geografische Verteilung. Sie sind explizit organisationsgetrieben.',
|
||||
points: 6,
|
||||
type: 'text',
|
||||
},
|
||||
{
|
||||
id: '6-1-j',
|
||||
prompt: 'Nennen Sie zwei der vier Team-Typen von Team Topologies und beschreiben Sie kurz deren Funktion.',
|
||||
solution: '1. Stream-Aligned Teams: Arbeiten an einem kontinuierlichen Wertstrom, der auf ein Geschäftsergebnis ausgerichtet ist. Sie sind End-to-End für einen Service verantwortlich.\n2. Platform Teams: Stellen interne Services und Tools als Self-Service-Plattform bereit, um die Produktivität der Stream-Aligned Teams zu erhöhen und deren kognitive Last zu reduzieren.',
|
||||
points: 5,
|
||||
type: 'text',
|
||||
}
|
||||
],
|
||||
explanation: '### Lösungserklärung\n\n**Strategic Design** ist der Teil von DDD, der sich mit der "großen" Architektur befasst: der Zerlegung eines komplexen Systems in handhabbare Teile.\n\n- **Ubiquitous Language**: Das Fundament. Eine gemeinsame Sprache zwischen Entwicklern und Fachexperten, um Missverständnisse zu vermeiden.\n- **Bounded Context**: Eine explizite Grenze, innerhalb derer ein Modell und eine Sprache gültig sind. Ein Begriff wie "Produkt" kann im Verkaufs-Kontext etwas anderes bedeuten als im Lager-Kontext. Bounded Contexts sind die Blaupause für Microservices.\n- **Context Map**: Eine Visualisierung der Beziehungen zwischen Bounded Contexts. Sie zeigt, wie die Teams und Systeme zusammenarbeiten müssen (z.B. über ein **Shared Kernel**, **Customer-Supplier** oder einen **Anti-Corruption Layer**).\n- **Core/Supporting/Generic Subdomains**: Diese Klassifizierung hilft, den Fokus zu lenken. Die meiste Energie sollte in die **Core Domain** fließen, da sie den einzigartigen Geschäftswert schafft. **Generic Subdomains** (z.B. Authentifizierung) sollten als Standardlösung eingekauft werden.\n- **Conway\'s Law & Team Topologies**: Diese Gesetze und Modelle untermauern das Design. Die Systemarchitektur (Bounded Contexts) und die Organisationsstruktur (Teams) sollten aufeinander abgestimmt sein, um die Kommunikation zu optimieren und Autonomie zu ermöglichen. **Team Topologies** bietet hierzu ein praktisches Vokabular (z.B. **Stream-Aligned Teams**, **Platform Teams**).\n\n### Quellen\n*Folien 6.1, Seite 3-19*'
|
||||
}
|
||||
];
|
124
data/swa_exercises.7.ts
Normal file
124
data/swa_exercises.7.ts
Normal file
@ -0,0 +1,124 @@
|
||||
|
||||
import { Exercise } from '../types';
|
||||
|
||||
export const swaExercises7: Exercise[] = [
|
||||
{
|
||||
id: 'swa-6-1-ddd-tactical',
|
||||
title: '6.1 - SWA - Domain-Driven Design (Tactical)',
|
||||
parts: [
|
||||
{
|
||||
id: '6-1-a',
|
||||
prompt: 'Erklären Sie die Unterschiede zwischen Entities und Value Objects.',
|
||||
solution: 'Entities haben eine eindeutige, überdauernde Identität (ID) und ihr Zustand kann sich ändern. Zwei Entities sind gleich, wenn ihre IDs gleich sind. Beispiel: Ein Kunde.\n\nValue Objects haben keine Identität und sind unveränderlich (immutable). Ihre Gleichheit basiert auf dem Wert ihrer Attribute. Beispiel: Eine Adresse oder ein Geldbetrag.',
|
||||
points: 6,
|
||||
type: 'text',
|
||||
},
|
||||
{
|
||||
id: '6-1-b',
|
||||
prompt: 'Was ist ein „Aggregate" und welche Rolle spielt das Aggregate Root?',
|
||||
solution: 'Ein Aggregat ist eine Gruppe von fachlich zusammengehörenden Objekten (Entities und Value Objects), die als eine Konsistenz- und Transaktionsgrenze behandelt wird. Das Aggregate Root ist die einzige Entity innerhalb des Aggregats, über die von außen zugegriffen werden darf. Es ist dafür verantwortlich, die Geschäftsregeln (Invarianten) des gesamten Aggregats sicherzustellen.',
|
||||
points: 7,
|
||||
type: 'text',
|
||||
},
|
||||
{
|
||||
id: '6-1-c',
|
||||
prompt: 'Erklären Sie, warum Referenzen zwischen Aggregaten nur über ihre IDs erfolgen sollten.',
|
||||
solution: 'Referenzen zwischen Aggregaten sollten nur über IDs erfolgen, um lose Kopplung zu gewährleisten und die Transaktionsgrenzen zu wahren. Eine direkte Objektreferenz würde eine starke Kopplung erzeugen und das Laden großer Objektgraphen provozieren. Zudem könnten so die Konsistenzregeln eines Aggregats von außen umgangen werden.',
|
||||
points: 6,
|
||||
type: 'text',
|
||||
},
|
||||
{
|
||||
id: '6-1-d',
|
||||
prompt: 'Erklären Sie Domain Events und deren Rolle in der Kommunikation zwischen Aggregaten.',
|
||||
solution: 'Domain Events sind Objekte, die ein wichtiges Geschäftsereignis repräsentieren (z.B. "OrderPlaced"). Sie ermöglichen eine lose, asynchrone Kommunikation zwischen Aggregaten. Ein Aggregat publiziert ein Event, und andere Aggregate können darauf reagieren, ohne dass eine direkte Kopplung entsteht. Dies wahrt die Transaktionsgrenzen der Aggregate und ermöglicht eine eventually consistent Architektur.',
|
||||
points: 6,
|
||||
type: 'text',
|
||||
},
|
||||
{
|
||||
id: '6-1-e',
|
||||
prompt: 'Beschreiben Sie die vier Schichten der DDD-Layered-Architecture und deren Verantwortlichkeiten.',
|
||||
solution: '1. User Interface Layer: Präsentation und Benutzerinteraktion.\n2. Application Layer: Koordination der Use Cases, enthält keine Geschäftslogik.\n3. Domain Layer: Das Herz der Anwendung. Enthält die Geschäftslogik, Entities, Value Objects und Aggregate.\n4. Infrastructure Layer: Technische Details wie Datenbankzugriffe, Messaging, externe APIs.',
|
||||
points: 7,
|
||||
type: 'text',
|
||||
},
|
||||
{
|
||||
id: '6-1-f',
|
||||
prompt: 'Was ist ein Repository in DDD?',
|
||||
solution: 'Ein Repository kapselt die Logik für den Zugriff auf Aggregate und abstrahiert die Persistierung. Es stellt eine domänenorientierte Schnittstelle zur Verfügung (z.B. `findCustomerById`), deren Implementierung (z.B. via SQL) in der Infrastructure-Schicht liegt. Pro Aggregat-Root gibt es ein Repository.',
|
||||
points: 5,
|
||||
type: 'text',
|
||||
},
|
||||
{
|
||||
id: '6-1-g',
|
||||
prompt: 'Was ist ein Anemic Domain Model und warum ist es ein Anti-Pattern?',
|
||||
solution: 'Ein Anemic Domain Model ist ein Modell, bei dem die Domänenobjekte (Entities) nur Daten und Getter/Setter enthalten, aber keine Geschäftslogik. Die Logik wird stattdessen in Service-Klassen ausgelagert. Es ist ein Anti-Pattern, weil es die Prinzipien der Objektorientierung (Kapselung von Daten und Verhalten) verletzt, zu verstreuter Logik führt und die Wartbarkeit erschwert.',
|
||||
points: 6,
|
||||
type: 'text',
|
||||
}
|
||||
],
|
||||
explanation: '### Lösungserklärung\n\n**Tactical Design** ist der Teil von DDD, der sich mit den konkreten Entwurfsmustern innerhalb eines Bounded Context befasst.\n\n- **Entities & Value Objects**: Die grundlegenden Bausteine. **Entities** haben eine ID, **Value Objects** nicht.\n- **Aggregate**: Eine Gruppe von Objekten, die eine **Konsistenzgrenze** bilden. Änderungen am Aggregat geschehen immer in einer einzigen Transaktion. Der **Aggregate Root** ist der einzige Einstiegspunkt und schützt die Geschäftsregeln (Invarianten) des Aggregats.\n- **Referenzen per ID**: Um Aggregate lose zu koppeln, verweisen sie aufeinander nur über IDs, nicht über direkte Objektreferenzen.\n- **Domain Events**: Werden für die **asynchrone Kommunikation** zwischen Aggregaten genutzt, um die lose Kopplung aufrechtzuerhalten.\n- **Layered Architecture**: Die klassische DDD-Architektur trennt UI, Application, Domain und Infrastructure. Die Abhängigkeiten zeigen immer nach innen zur Domain.\n- **Repository**: Abstrahiert den Datenbankzugriff für ein Aggregat. Die Schnittstelle gehört zur Domäne, die Implementierung zur Infrastruktur.\n- **Anemic Domain Model**: Ein Anti-Pattern, bei dem Objekte nur Datenhalter sind und die Logik in Services ausgelagert wird. Dies widerspricht dem DDD-Ziel, Daten und Verhalten zu kapseln.\n\n### Quellen\n*Folien 6.1, Seite 23-48*'
|
||||
},
|
||||
{
|
||||
id: 'swa-7-1-heuristiken',
|
||||
title: '7.1 - SWA - Heuristiken',
|
||||
parts: [
|
||||
{
|
||||
id: '7-1-a',
|
||||
prompt: 'Was bedeutet Heuristik im Kontext von Softwarearchitektur?',
|
||||
solution: 'Eine Heuristik ist eine erfahrungsbasierte Regel oder ein Prinzip, das eine Abkürzung zu einer guten, aber nicht notwendigerweise optimalen Lösung bietet. In der Softwarearchitektur sind dies bewährte Praktiken und Entwurfsmuster, die bei Trade-off-Entscheidungen helfen.',
|
||||
points: 4,
|
||||
type: 'text',
|
||||
},
|
||||
{
|
||||
id: '7-1-b',
|
||||
prompt: 'Erläutern Sie die Heuristiken "Low Coupling" und "High Cohesion".',
|
||||
solution: 'Low Coupling (Lose Kopplung) bedeutet, die Abhängigkeiten zwischen Komponenten zu minimieren. Komponenten sollten so unabhängig wie möglich sein. \nHigh Cohesion (Hoher Zusammenhalt) bedeutet, dass die Elemente innerhalb einer Komponente fachlich eng zusammengehören und eine klar definierte, gemeinsame Aufgabe erfüllen. Beide Prinzipien zusammen führen zu gut strukturierten, wartbaren Systemen.',
|
||||
points: 6,
|
||||
type: 'text',
|
||||
},
|
||||
{
|
||||
id: '7-1-c',
|
||||
prompt: 'Erläutern Sie das "Single Responsibility Principle" (SRP) und "Information Hiding".',
|
||||
solution: 'SRP besagt, dass eine Klasse/Modul nur einen einzigen Grund zur Änderung haben sollte, also nur eine Verantwortlichkeit. \nInformation Hiding (Geheimnisprinzip) besagt, dass Implementierungsdetails einer Komponente nach außen verborgen sein sollten und die Interaktion nur über eine definierte Schnittstelle erfolgt. Dies reduziert die Kopplung.',
|
||||
points: 5,
|
||||
type: 'text',
|
||||
},
|
||||
{
|
||||
id: '7-1-d',
|
||||
prompt: 'Erläutern Sie das "Open Closed Principle" (OCP) und das "Liskov Substitution Principle" (LSP).',
|
||||
solution: 'OCP: Software-Einheiten sollen offen für Erweiterungen, aber geschlossen für Modifikationen sein. Man fügt neues Verhalten hinzu, ohne bestehenden Code zu ändern (z.B. durch Interfaces).\nLSP: Unterklassen müssen ihre Oberklassen jederzeit ersetzen können, ohne das Programmverhalten zu ändern. "is-a"-Beziehung.',
|
||||
points: 6,
|
||||
type: 'text',
|
||||
},
|
||||
{
|
||||
id: '7-1-e',
|
||||
prompt: 'Erläutern Sie das "Interface Segregation Principle" (ISP) und das "Dependency Inversion Principle" (DIP).',
|
||||
solution: 'ISP: Bevorzuge kleine, client-spezifische Interfaces statt großer, allgemeiner Interfaces. Clients sollen nicht von Methoden abhängen, die sie nicht verwenden.\nDIP: High-level-Module sollen nicht von Low-level-Modulen abhängen. Beide sollen von Abstraktionen (Interfaces) abhängen. Dies kehrt die Abhängigkeitsrichtung um.',
|
||||
points: 6,
|
||||
type: 'text',
|
||||
},
|
||||
{
|
||||
id: '7-1-f',
|
||||
prompt: 'Erläutern Sie die Heuristik "Don\'t Repeat Yourself" (DRY) und "Keep It Small and Simple" (KISS).',
|
||||
solution: 'DRY: Jede Information oder Logik sollte nur eine einzige, eindeutige Repräsentation im System haben. Vermeide Redundanz.\nKISS: Bevorzuge Einfachheit. Wende komplexe Muster nicht an, wo eine einfache Lösung ausreicht. Finde die richtige Balance zwischen guter Struktur und unnötiger Komplexität.',
|
||||
points: 5,
|
||||
type: 'text',
|
||||
},
|
||||
{
|
||||
id: '7-1-g',
|
||||
prompt: 'Was ist das „God-Object"-Antipattern und welche Heuristik verletzt es primär?',
|
||||
solution: 'Ein God-Object ist ein Anti-Pattern, bei dem eine einzelne Klasse oder Komponente zu viele, nicht zusammengehörende Verantwortlichkeiten übernimmt. Sie wird riesig und ist schwer zu warten und zu testen.\n\nEs verletzt primär das "Single Responsibility Principle" (bzw. die Heuristik der "Separation of Concerns" und "High Cohesion").',
|
||||
points: 5,
|
||||
type: 'text',
|
||||
},
|
||||
{
|
||||
id: '7-1-h',
|
||||
prompt: 'Bei Quasar sollte AT-Software vermieden werden. Wieso?',
|
||||
solution: 'AT-Software (Application + Technology) ist Software, die Fachlogik (A) und technische Belange (T) stark vermischt. Dies sollte vermieden werden, weil es zu hoher Kopplung führt, die Wartbarkeit erschwert und die Wiederverwendbarkeit praktisch unmöglich macht. Fachliche Änderungen wirken sich auf technische Teile aus und umgekehrt.',
|
||||
points: 5,
|
||||
type: 'text',
|
||||
},
|
||||
],
|
||||
explanation: '### Lösungserklärung\n\n**Heuristiken** sind bewährte "Faustregeln", die Architekten helfen, gute Designentscheidungen zu treffen.\n\n- **Low Coupling & High Cohesion**: Das wichtigste Duo. **Hohe Kohäsion** innerhalb einer Komponente (alles darin gehört zusammen), **lose Kopplung** zwischen den Komponenten (sie sind möglichst unabhängig voneinander).\n- **SOLID-Prinzipien** sind eine Sammlung von fünf zentralen Heuristiken:\n - **S**ingle Responsibility Principle (SRP): Eine Klasse, eine Aufgabe.\n - **O**pen-Closed Principle (OCP): Erweiterbar, ohne den Code zu ändern.\n - **L**iskov Substitution Principle (LSP): Unterklassen müssen ihre Oberklassen vollständig ersetzen können.\n - **I**nterface Segregation Principle (ISP): Lieber viele kleine, spezifische Interfaces als ein großes.\n - **D**ependency Inversion Principle (DIP): Abhängigkeiten sollten auf Abstraktionen (Interfaces) zeigen, nicht auf konkrete Implementierungen.\n- **Weitere wichtige Heuristiken**: **DRY** (vermeide Redundanz), **KISS** (halte es einfach) und **Information Hiding** (verberge Implementierungsdetails).\n\nDas **God-Object**-Antipattern ist ein klassisches Beispiel für die Verletzung des **SRP** und der **hohen Kohäsion**. Es ist eine Klasse, die zu viele Verantwortungen an sich reißt, was zu einem schwer wartbaren System führt. Ähnlich problematisch ist **AT-Software**, die Fach- und Technik-Code vermischt.\n\n### Quellen\n*Folien 7.1, Seite 3-37*'
|
||||
}
|
||||
];
|
89
data/swa_exercises.examples.ts
Normal file
89
data/swa_exercises.examples.ts
Normal file
@ -0,0 +1,89 @@
|
||||
|
||||
import { Exercise } from '../types';
|
||||
|
||||
export const swaExercisesExamples: Exercise[] = [
|
||||
{
|
||||
id: 'swa-ex-datamodel-1',
|
||||
title: 'Beispiel: Fachliches Datenmodell (Bibliothek)',
|
||||
parts: [
|
||||
{
|
||||
id: 'a',
|
||||
prompt: 'Erstellen Sie ein fachliches Datenmodell in Mermaid-Syntax für ein einfaches öffentliches Bibliothekssystem. Das System soll Bücher, Mitglieder und Ausleihen verwalten. Berücksichtigen Sie die folgenden Konzepte:\n- **Buch**: mit ISBN, Titel und Autor.\n- **Mitglied**: mit Mitglieds-ID, Name und Adresse.\n- **Ausleihe**: die ein Mitglied mit einem Buch verbindet und ein Ausleih- sowie ein Rückgabedatum hat.',
|
||||
solution: '```mermaid\nclassDiagram\n class Buch {\n +ISBN isbn\n +Titel titel\n +Autor autor\n }\n class Mitglied {\n +MitgliedsID id\n +Name name\n +Adresse adresse\n }\n class Ausleihe {\n +Datum ausleihdatum\n +Datum rueckgabedatum\n }\n\n Mitglied "1" -- "0..*" Ausleihe : leiht aus\n Buch "1" -- "0..*" Ausleihe : wird ausgeliehen\n```',
|
||||
points: 8,
|
||||
type: 'mermaid',
|
||||
}
|
||||
],
|
||||
explanation: '### Lösungserklärung\n\nEin gutes fachliches Datenmodell konzentriert sich auf die **Konzepte der Domäne** und deren Beziehungen, nicht auf technische Details. Es verwendet fachliche Datentypen, um die Bedeutung der Daten zu verdeutlichen.\n\n**Bestandteile des Modells:**\n\n1. **Entitäten**: Dies sind die zentralen Objekte mit einer eindeutigen Identität.\n * `Buch`: Jedes Buch ist einzigartig, auch wenn es den gleichen Titel wie ein anderes hat (z.B. durch eine Inventarnummer, die hier durch die `ISBN` repräsentiert wird).\n * `Mitglied`: Jedes Mitglied ist durch seine `MitgliedsID` eindeutig identifizierbar.\n * `Ausleihe`: Jede einzelne Ausleihe ist ein einzigartiges Ereignis, selbst wenn dasselbe Mitglied dasselbe Buch später erneut ausleiht.\n\n2. **Fachliche Datentypen (Value Objects)**: Anstatt primitive Typen wie `string` oder `int` zu verwenden, definieren wir spezifischere Typen, die eine fachliche Bedeutung tragen. Im Diagramm sind dies z.B. `ISBN`, `Titel`, `MitgliedsID`, `Adresse`, `Datum`. Dies hat mehrere Vorteile:\n * **Verständlichkeit**: Es ist sofort klar, was gemeint ist.\n * **Validierung**: Ein `ISBN`-Typ könnte eine eingebaute Validierungslogik enthalten, um sicherzustellen, dass nur gültige ISBNs gespeichert werden.\n * **Typsicherheit**: Man kann nicht versehentlich eine `MitgliedsID` einem `ISBN`-Feld zuweisen.\n\n3. **Beziehungen und Kardinalitäten**:\n * `Mitglied "1" -- "0..*" Ausleihe`: Ein Mitglied kann keine, eine oder viele Ausleihen haben. Jede Ausleihe gehört aber zu genau einem Mitglied.\n * `Buch "1" -- "0..*" Ausleihe`: Ein spezifisches Exemplar eines Buches kann über die Zeit mehrfach ausgeliehen werden (oder auch nie). Jede Ausleihe bezieht sich auf genau ein Buch.\n\nDieses Modell bildet die statische Struktur der Bibliotheksdomäne klar und verständlich ab und dient als exzellente Grundlage für weitere Diskussionen und die Implementierung.'
|
||||
},
|
||||
{
|
||||
id: 'swa-ex-datamodel-2',
|
||||
title: 'Beispiel: Fachliches Datenmodell (Tierheim)',
|
||||
parts: [
|
||||
{
|
||||
id: 'a',
|
||||
prompt: 'Erstellen Sie ein fachliches Datenmodell in Mermaid-Syntax für eine Tiervermittlungs-Agentur. Das Modell soll Tiere, Interessenten und Adoptionsanträge abbilden.\n- **Tier**: mit Tier-ID, Name, Spezies (z.B. Hund, Katze) und Status (z.B. verfügbar, reserviert, adoptiert).\n- **Interessent**: mit Interessenten-ID, Name und Kontaktdaten.\n- **Antrag**: der einen Interessenten mit einem Tier verbindet und einen Antragsstatus (z.B. eingereicht, geprüft, genehmigt, abgelehnt) hat.',
|
||||
solution: '```mermaid\nclassDiagram\n class Tier {\n +TierID id\n +Name name\n +Spezies spezies\n +TierStatus status\n }\n class Interessent {\n +InteressentID id\n +Name name\n +Kontaktdaten kontaktdaten\n }\n class Antrag {\n +AntragsID id\n +AntragsStatus status\n }\n\n Interessent "1" -- "1..*" Antrag : stellt\n Tier "1" -- "1..*" Antrag : ist Gegenstand von\n```',
|
||||
points: 8,
|
||||
type: 'mermaid',
|
||||
}
|
||||
],
|
||||
explanation: '### Lösungserklärung\n\nDieses Modell fokussiert auf den Prozess der Tiervermittlung. Es trennt klar die Konzepte und deren Zustände, was für einen prozessorientierten Anwendungsfall entscheidend ist.\n\n**Bestandteile des Modells:**\n\n1. **Entitäten**:\n * `Tier`: Ein Tier ist eine Entität mit einem Lebenszyklus innerhalb des Tierheims. Sein `TierStatus` ist ein zentrales Attribut, das den Vermittlungsprozess steuert.\n * `Interessent`: Eine Person, die an einer Adoption interessiert ist. Ihre Identität ist wichtig, um Anträge zuzuordnen.\n * `Antrag`: Der Antrag selbst ist eine Entität, da jeder Antrag einzigartig ist und einen eigenen Lebenszyklus mit verschiedenen Phasen (`AntragsStatus`) durchläuft.\n\n2. **Fachliche Datentypen (Value Objects)**:\n * `TierID`, `InteressentID`, `AntragsID`: Spezifische Identifikatoren, die Typsicherheit gewährleisten.\n * `TierStatus`, `AntragsStatus`: Dies sind Enums oder Value Objects, die einen festen Satz von Zuständen repräsentieren. Dies ist viel besser als die Verwendung von `int` oder `string`, da es Fehleingaben verhindert und die Geschäftsregeln explizit macht.\n * `Spezies`, `Name`, `Kontaktdaten`: Weitere fachliche Datentypen, die Konzepte bündeln und für Klarheit sorgen.\n\n3. **Beziehungen und Kardinalitäten**:\n * `Interessent "1" -- "1..*" Antrag`: Ein Interessent muss mindestens einen Antrag stellen, um im System relevant zu sein, und kann sich für mehrere Tiere bewerben. Jeder Antrag gehört zu genau einem Interessenten.\n * `Tier "1" -- "1..*" Antrag`: Für ein Tier kann es mehrere Anträge geben. Jeder Antrag bezieht sich aber auf genau ein Tier.\n\n**Besonderheiten des Modells**:\nDas Modell trennt die Entität `Interessent` von einem potenziellen `Adoptant`. Ein Interessent wird erst zum Adoptanten, wenn ein `Antrag` den Status `genehmigt` erreicht. Diese prozessuale Logik wäre der nächste Schritt, der auf diesem statischen Modell aufbaut (z.B. in einer Laufzeitsicht).'
|
||||
},
|
||||
{
|
||||
id: 'swa-ex-bewertung-1',
|
||||
title: 'Beispiel: Bewertung eines Datenmodells (E-Shop)',
|
||||
parts: [
|
||||
{
|
||||
id: 'a',
|
||||
prompt: 'Bewerten Sie das folgende, fehlerhafte fachliche Datenmodell für einen E-Commerce-Shop. Identifizieren Sie mindestens drei signifikante Schwächen und schlagen Sie Verbesserungen vor.\n\n```mermaid\nclassDiagram\n class User {\n int id\n string name\n string address_line_1\n string address_line_2\n string city\n }\n class Order {\n int id\n List~Product~ items\n }\n class Product {\n int id\n string name\n float price\n }\n User "1" -- "0..*" Order : places\n```',
|
||||
solution: 'Dieses Datenmodell hat mehrere signifikante Schwächen:\n\n1. **Verwendung technischer Primitive**: Das Modell verwendet technische Datentypen wie `int` und `string` anstelle von fachlichen. `string address_line_1` sollte Teil eines `Adresse`-Value-Objects sein. `int id` sollte ein fachlicher Typ wie `KundenID` oder `BestellID` sein.\n\n2. **Verletzung der Datenhoheit / Starke Kopplung**: Die `Order`-Klasse enthält eine `List<Product>`. Das bedeutet, eine Bestellung enthält die vollständigen Produktobjekte. Dies ist problematisch, weil sich Produktinformationen (z.B. der Preis) ändern können. Eine Bestellung sollte den Zustand zum Zeitpunkt des Kaufs "einfrieren". Besser wäre es, wenn `Order` eine Liste von `OrderItem`-Value-Objects enthält, die nur die `ProduktID`, den gekauften Preis und die Menge speichern.\n\n3. **Fehlende fachliche Konzepte**: Das Konzept "Kunde" wird nur als `User` bezeichnet, was unpräzise ist. Wichtige Attribute wie das Bestelldatum, der Bestellstatus oder die Gesamtsumme fehlen in der `Order`-Klasse komplett.',
|
||||
points: 9,
|
||||
type: 'text',
|
||||
}
|
||||
],
|
||||
explanation: '### Detaillierte Erklärung der Bewertung\n\nEin gutes fachliches Datenmodell soll die Realität der Geschäftsdomäne so präzise wie möglich abbilden. Dieses Modell scheitert daran in mehreren Punkten.\n\n**1. Mangelnde fachliche Typisierung (Primitive Obsession)**\n- **Problem**: Die Verwendung von `string`, `int`, `float` verschleiert die eigentliche Bedeutung der Daten. Eine Adresse ist mehr als nur drei Strings; sie ist ein zusammengehöriges Konzept. Ein Preis ist nicht nur eine Fließkommazahl; er hat auch eine Währung.\n- **Verbesserung**: Führen Sie fachliche Datentypen (Value Objects) ein. Erstellen Sie eine `Adresse`-Klasse, die Straße, Stadt, PLZ etc. bündelt. Erstellen Sie eine `Geldbetrag`-Klasse, die Wert und Währung kombiniert. Definieren Sie spezifische ID-Typen wie `KundenID`, `ProduktID`, `BestellID`, um Typsicherheit zu erhöhen.\n\n**2. Starke Kopplung und falsche Komposition**\n- **Problem**: `Order` enthält eine Liste von `Product`-Objekten. Das bedeutet, wenn man eine Bestellung lädt, lädt man auch alle zugehörigen, aktuellen Produktinformationen. Was passiert, wenn sich der Preis eines Produkts ändert, nachdem die Bestellung aufgegeben wurde? Der alte Preis in der Bestellung würde überschrieben, was falsch ist. Die Bestellung ist an die *aktuelle* Produktdefinition gekoppelt.\n- **Verbesserung**: Entkoppeln Sie die Bestellung vom Produktkatalog. Eine `Order` sollte eine Liste von `OrderItem` (Bestellposition) enthalten. Ein `OrderItem` ist ein Value Object, das die `ProduktID` als Referenz, die `Menge` und den `PreisZumKaufzeitpunkt` speichert. So bleibt die Bestellung historisch korrekt und ist von späteren Änderungen im Produktkatalog unabhängig.\n\n**3. Unvollständigkeit und unpräzise Benennung**\n- **Problem**: Dem Modell fehlen essenzielle Informationen. Eine Bestellung ohne Datum, Status oder Summe ist unbrauchbar. Der Begriff `User` ist zu generisch. Ist jeder User ein Kunde? Was ist mit Administratoren?\n- **Verbesserung**: Benennen Sie `User` in `Kunde` um, um die Rolle im Kontext des E-Shops klar zu machen. Fügen Sie der `Order`-Klasse wichtige Attribute wie `bestelldatum: Datum`, `status: BestellStatus` und `gesamtsumme: Geldbetrag` hinzu.'
|
||||
},
|
||||
{
|
||||
id: 'swa-ex-adr-1',
|
||||
title: 'Beispiel: ADR für Datenbankwahl (Social App)',
|
||||
parts: [
|
||||
{
|
||||
id: 'a',
|
||||
prompt: 'Sie entwerfen eine neue Social-Media-App mit einem Newsfeed. Die Leselast (Feed abrufen) wird extrem hoch sein, während die Schreiblast (neue Posts) moderat ist. Horizontale Skalierbarkeit und hohe Verfügbarkeit sind die wichtigsten Qualitätsziele. Formulieren Sie einen ADR (nur Kontext, Entscheidung, Konsequenzen) für die Wahl der primären Datenbank.',
|
||||
solution: '**Kontext:**\nWir benötigen eine Datenbank für unsere Social-Media-App, die einen Newsfeed mit Posts, Kommentaren und Likes verwaltet. Die erwarteten Zugriffsmuster sind leselastig (viele Benutzer scrollen durch Feeds). Die wichtigsten Qualitätsanforderungen sind extrem hohe Verfügbarkeit und die Fähigkeit zur horizontalen Skalierung, um Millionen von Benutzern zu unterstützen. Starke Konsistenz (ACID) ist für Feed-Daten nicht zwingend erforderlich; eventual consistency ist akzeptabel. Die Daten sind semi-strukturiert und haben oft Beziehungen (Benutzer -> Posts -> Kommentare).\n\n**Entscheidung:**\nWir entscheiden uns für die Verwendung einer Wide-Column NoSQL-Datenbank wie Apache Cassandra oder Amazon DynamoDB als primären Datenspeicher. Wir werden unsere Daten denormalisieren und für die primären Lese-Queries optimieren (z.B. Posts eines Benutzers in einer einzigen Partition speichern).\n\n**Konsequenzen:**\n* **Positiv:**\n * Exzellente horizontale Skalierbarkeit und hohe Schreib- und Leseleistung unter Last.\n * Hohe Verfügbarkeit durch die verteilte und replizierte Natur der Datenbank.\n * Flexibles Schema erleichtert zukünftige Feature-Entwicklungen.\n* **Negativ:**\n * Eventual Consistency: Daten sind möglicherweise nicht sofort nach dem Schreiben auf allen Knoten sichtbar. Dies muss in der Anwendungslogik berücksichtigt werden.\n * Keine komplexen Joins: Ad-hoc-Abfragen sind schwierig. Daten müssen für spezifische Abfragen modelliert (denormalisiert) werden, was zu Datenredundanz führt.\n * Das Entwicklungsteam muss neue Datenmodellierungstechniken erlernen, die sich von relationalen Datenbanken unterscheiden.',
|
||||
points: 10,
|
||||
type: 'text',
|
||||
}
|
||||
],
|
||||
explanation: '### Detaillierte Erklärung des ADR\n\nEin guter ADR (Architecture Decision Record) ist präzise, nachvollziehbar und ehrlich über die Konsequenzen. Dieser ADR zur Datenbankwahl ist ein klassisches Beispiel für eine grundlegende Architekturentscheidung.\n\n**Analyse des Kontexts:**\nDer Kontext beschreibt klar das **Problem** und die **Rahmenbedingungen (Constraints)**. Hier sind die Schlüsselwörter: "leselastig", "horizontale Skalierbarkeit", "hohe Verfügbarkeit" und "eventual consistency ist akzeptabel". Diese Treiber deuten stark auf eine NoSQL-Lösung hin und schließen traditionelle relationale Datenbanken für diesen speziellen Anwendungsfall eher aus, da diese oft schwerer horizontal zu skalieren sind.\n\n**Analyse der Entscheidung:**\nDie Entscheidung ist **konkret** und **begründet**. Sie benennt nicht nur eine Technologiekategorie ("NoSQL"), sondern eine spezifische Art ("Wide-Column") und sogar Beispiele ("Cassandra", "DynamoDB"). Wichtig ist auch der Zusatz, *wie* die Technologie genutzt werden soll: "denormalisieren und für Lese-Queries optimieren". Das zeigt, dass die Konsequenzen der Entscheidung verstanden wurden.\n\n**Analyse der Konsequenzen:**\nDies ist oft der wichtigste Teil eines ADR. Er zeigt, dass ein **Trade-off** gemacht wurde und die Nachteile bewusst in Kauf genommen werden. \n- Die **positiven** Konsequenzen mappen direkt auf die im Kontext genannten Qualitätsziele (Skalierbarkeit, Verfügbarkeit).\n- Die **negativen** Konsequenzen sind ehrlich und realistisch. Sie sprechen die größten Herausforderungen von NoSQL-Datenbanken an: \n 1. **Eventual Consistency**: Dies ist ein fundamentaler Unterschied zu ACID und hat Auswirkungen auf das gesamte Systemdesign. Der ADR macht klar, dass dies bedacht wurde.\n 2. **Datenmodellierung**: Der Hinweis auf Denormalisierung und fehlende Joins ist entscheidend. Er zeigt, dass der damit verbundene Mehraufwand und die andere Denkweise erkannt wurden.\n 3. **Team-Fähigkeiten**: Die Notwendigkeit, dass das Team umlernen muss, ist eine wichtige organisatorische Konsequenz.\n\nInsgesamt dokumentiert dieser ADR eine fundierte, abgewogene Entscheidung, die für jeden (auch für neue Teammitglieder in der Zukunft) nachvollziehbar ist.'
|
||||
},
|
||||
{
|
||||
id: 'swa-ex-contextmap-1',
|
||||
title: 'Beispiel: Context Map (Lernplattform)',
|
||||
parts: [
|
||||
{
|
||||
id: 'a',
|
||||
prompt: 'Entwerfen Sie eine Context Map in Mermaid-Syntax für eine Online-Lernplattform. Berücksichtigen Sie die Bounded Contexts: `CourseCatalog` (Verwaltung der Kurse), `StudentEnrollment` (Einschreibung der Studenten), `Forum` (Diskussionsforum) und `Payment` (externe Zahlungsabwicklung). Definieren Sie plausible Beziehungen zwischen den Kontexten.',
|
||||
solution: '```mermaid\ngraph TD\n subgraph "Online-Lernplattform"\n A(CourseCatalog) -- "Open Host Service" --> B(StudentEnrollment)\n D(Forum) -- "Shared Kernel (User Profile)" --> B\n end\n\n B -- "Anti-Corruption Layer" --> C{Payment Gateway}\n\n linkStyle 0 stroke-width:2px,fill:none,stroke:green;\n linkStyle 1 stroke-width:2px,fill:none,stroke:orange;\n linkStyle 2 stroke-width:2px,fill:none,stroke:red;\n```\n*Legende: Grün=OHS, Orange=Shared Kernel, Rot=ACL*',
|
||||
points: 9,
|
||||
type: 'mermaid',
|
||||
}
|
||||
],
|
||||
explanation: '### Detaillierte Erklärung der Context Map\n\nEine Context Map ist ein strategisches DDD-Werkzeug, das die Beziehungen zwischen verschiedenen Bounded Contexts visualisiert. Sie ist entscheidend für das Verständnis der Systemlandschaft und die Organisation der Team-Zusammenarbeit.\n\n**Beziehungs-Patterns und ihre Begründung:**\n\n1. **CourseCatalog -- "Open Host Service" --> StudentEnrollment**\n * **Pattern**: Open Host Service (OHS). Der `CourseCatalog`-Kontext bietet eine stabile, gut dokumentierte API (den "Service") an, die von anderen Kontexten (den "Clients") genutzt werden kann. \n * **Begründung**: Der Kurs-Katalog ist eine zentrale, stabile Ressource. Das `StudentEnrollment`-Team muss wissen, welche Kurse existieren, um Einschreibungen zu ermöglichen. Der Katalog diktiert das Modell, `StudentEnrollment` konsumiert es. Dies ist eine sehr typische Beziehung für eine zentrale, datenliefernde Komponente.\n\n2. **Forum -- "Shared Kernel (User Profile)" --> StudentEnrollment**\n * **Pattern**: Shared Kernel. Zwei Teams einigen sich darauf, einen kleinen Teil ihres Modells (und des Codes) zu teilen. \n * **Begründung**: Sowohl für die Einschreibung als auch für das Forum wird ein Benutzerprofil mit Namen, E-Mail etc. benötigt. Anstatt dieses Modell in beiden Kontexten redundant zu entwickeln, wird es in einer gemeinsamen Bibliothek (dem Shared Kernel) gepflegt. \n * **Trade-off**: Dies führt zu einer engen Kopplung. Jede Änderung am Shared Kernel muss von beiden Teams abgesegnet und koordiniert werden. Es sollte nur für sehr stabile, wirklich gemeinsame Konzepte verwendet werden.\n\n3. **StudentEnrollment -- "Anti-Corruption Layer" --> Payment Gateway**\n * **Pattern**: Anti-Corruption Layer (ACL). Eine explizite Übersetzungsschicht, die das eigene Domänenmodell vor den Einflüssen eines externen oder veralteten Systems schützt.\n * **Begründung**: Der `Payment Gateway` ist ein externes System mit seinem eigenen, oft komplizierten und nicht idealen Datenmodell (z.B. `TransactionRequest`, `PaymentResponse`). Der ACL übersetzt zwischen dem Modell des `StudentEnrollment`-Kontextes (z.B. `Invoice`, `PaymentConfirmation`) und dem externen Modell. Dadurch bleibt unser eigenes Domänenmodell sauber und unabhängig. Wenn der Zahlungsanbieter gewechselt wird, muss nur der ACL angepasst werden, nicht der gesamte `StudentEnrollment`-Kontext.'
|
||||
},
|
||||
{
|
||||
id: 'swa-ex-lod-1',
|
||||
title: 'Beispiel: Heuristik (Law of Demeter)',
|
||||
parts: [
|
||||
{
|
||||
id: 'a',
|
||||
prompt: 'Der folgende Java-Code verletzt die "Law of Demeter" (LoD) Heuristik: \n`String strasse = bestellung.getKunde().getAdresse().getStrasse();`\n\n1. Erklären Sie, warum dieser Code die Heuristik verletzt.\n2. Schreiben Sie eine refaktorisierte Version, die die Heuristik einhält.',
|
||||
solution: '1. **Verletzung**: Der Code verletzt die LoD, weil die aufrufende Methode tief in die interne Struktur des `bestellung`-Objekts greift. Sie "spricht nicht nur mit ihrem direkten Freund" (`bestellung`), sondern auch mit "Freunden von Freunden" (`kunde` und `adresse`). Dies erzeugt eine starke Kopplung an die Implementierungsdetails von `Kunde` und `Adresse`. Ändert sich die Struktur der `Adresse`-Klasse, muss dieser Aufruf ebenfalls geändert werden.\n\n2. **Refaktorisierung**:\nAuf der `Bestellung`-Klasse wird eine neue Methode hinzugefügt, die die interne Navigation verbirgt:\n```java\n// In der aufrufenden Methode:\nString strasse = bestellung.getKundenStrasse();\n\n// Neue Methode in der Klasse Bestellung:\npublic String getKundenStrasse() {\n return kunde.getAdresse().getStrasse();\n}\n```',
|
||||
points: 8,
|
||||
type: 'text',
|
||||
}
|
||||
],
|
||||
explanation: '### Detaillierte Erklärung zur Law of Demeter (LoD)\n\nDie "Law of Demeter" ist eine Heuristik für lose Kopplung. Sie wird oft als "Prinzip des geringsten Wissens" oder "Sprich nur mit deinen direkten Freunden" zusammengefasst. Das Ziel ist es, die Abhängigkeiten zwischen Objekten zu minimieren.\n\n**Warum ist die ursprüngliche Version schlecht?**\nDer Aufruf `bestellung.getKunde().getAdresse().getStrasse()` erzeugt eine **Wissenskette (Train Wreck)**. Die aufrufende Methode muss wissen:\n- dass eine `Bestellung` einen `Kunde` hat.\n- dass ein `Kunde` eine `Adresse` hat.\n- dass eine `Adresse` eine `Strasse` hat.\n\nDiese Kette von Abhängigkeiten macht den Code **fragil**. Wenn das `Kunde`-Objekt morgen entscheidet, seine Adress-Struktur zu ändern (z.B. in eine Liste von Adressen), bricht der aufrufende Code, obwohl er sich eigentlich nur für die `Bestellung` interessiert. Er ist zu stark an die *Implementierung* seiner Nachbarn gekoppelt.\n\n**Warum ist die refaktorisierte Version besser?**\nDie neue Methode `bestellung.getKundenStrasse()` ist eine Form von **Delegation**. Die `Bestellung`-Klasse übernimmt die Verantwortung, die Information zu beschaffen. Der Aufrufer muss die interne Struktur nicht mehr kennen. Er spricht nur noch mit seinem direkten Freund, der `bestellung`.\n\n- **Vorteile**:\n - **Lose Kopplung**: Der Aufrufer hängt nur noch von der `Bestellung`-Schnittstelle ab, nicht mehr von `Kunde` oder `Adresse`.\n - **Bessere Kapselung**: Die interne Struktur von `Bestellung` und seinen Bestandteilen ist verborgen (**Information Hiding**).\n - **Einfachere Wartung**: Wenn sich die Adress-Struktur ändert, muss nur die Methode `getKundenStrasse()` innerhalb der `Bestellung`-Klasse angepasst werden. Alle Aufrufer bleiben unberührt.\n\nDie Einhaltung der LoD führt zu wartbareren, flexibleren und robusteren Systemen.'
|
||||
},
|
||||
];
|
5
docker-compose.yml
Normal file
5
docker-compose.yml
Normal file
@ -0,0 +1,5 @@
|
||||
services:
|
||||
web:
|
||||
build: .
|
||||
ports:
|
||||
- 8123:8000
|
55
index.html
Normal file
55
index.html
Normal file
@ -0,0 +1,55 @@
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>SWA Trainer</title>
|
||||
<script src="https://cdn.tailwindcss.com?plugins=typography"></script>
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Roboto+Slab:wght@400;700&family=Roboto:wght@400;500;700&family=Roboto+Mono:wght@400;500&display=swap" rel="stylesheet">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/themes/prism-tomorrow.min.css" />
|
||||
<style>
|
||||
.code-editor-textarea::placeholder {
|
||||
color: #64748b; /* slate-500 */
|
||||
opacity: 1; /* Firefox */
|
||||
-webkit-text-fill-color: #64748b;
|
||||
}
|
||||
</style>
|
||||
<script>
|
||||
tailwind.config = {
|
||||
theme: {
|
||||
extend: {
|
||||
fontFamily: {
|
||||
sans: ['Roboto', 'sans-serif'],
|
||||
serif: ['Roboto Slab', 'serif'],
|
||||
mono: ['Roboto Mono', 'monospace'],
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
<script type="importmap">
|
||||
{
|
||||
"imports": {
|
||||
"react": "https://esm.sh/react@^19.1.0",
|
||||
"react/": "https://esm.sh/react@^19.1.0/",
|
||||
"react-dom/": "https://esm.sh/react-dom@^19.1.0/",
|
||||
"@google/genai": "https://esm.sh/@google/genai",
|
||||
"react-markdown": "https://esm.sh/react-markdown@9?bundle",
|
||||
"remark-gfm": "https://esm.sh/remark-gfm@4?bundle",
|
||||
"diff": "https://esm.sh/diff@5?bundle",
|
||||
"react-simple-code-editor": "https://esm.sh/react-simple-code-editor@0.13.1",
|
||||
"prismjs/": "https://esm.sh/prismjs@1.29.0/"
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<link rel="stylesheet" href="/index.css">
|
||||
</head>
|
||||
<body class="bg-slate-100">
|
||||
<div id="root"></div>
|
||||
<script type="module" src="/index.tsx"></script>
|
||||
</body>
|
||||
</html>
|
16
index.tsx
Normal file
16
index.tsx
Normal file
@ -0,0 +1,16 @@
|
||||
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom/client';
|
||||
import App from './App';
|
||||
|
||||
const rootElement = document.getElementById('root');
|
||||
if (!rootElement) {
|
||||
throw new Error("Could not find root element to mount to");
|
||||
}
|
||||
|
||||
const root = ReactDOM.createRoot(rootElement);
|
||||
root.render(
|
||||
<React.StrictMode>
|
||||
<App />
|
||||
</React.StrictMode>
|
||||
);
|
6
metadata.json
Normal file
6
metadata.json
Normal file
@ -0,0 +1,6 @@
|
||||
{
|
||||
"name": "SWA Practice App",
|
||||
"description": "An interactive web application to practice for the Software Architecture (SWA) lecture through a series of guided exercises. Users can write answers, submit them for validation, and get feedback.",
|
||||
"requestFramePermissions": [],
|
||||
"prompt": ""
|
||||
}
|
32
package.json
Normal file
32
package.json
Normal file
@ -0,0 +1,32 @@
|
||||
{
|
||||
"name": "swa-practice-app",
|
||||
"private": true,
|
||||
"version": "0.0.0",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "vite build",
|
||||
"preview": "vite preview"
|
||||
},
|
||||
"dependencies": {
|
||||
"@google/genai": "latest",
|
||||
"@headlessui/react": "^2.2.4",
|
||||
"@heroicons/react": "^2.2.0",
|
||||
"diff": "5.2.0",
|
||||
"prismjs": "1.29.0",
|
||||
"react": "^19.1.0",
|
||||
"react-dom": "^19.1.0",
|
||||
"react-markdown": "9.1.0",
|
||||
"react-simple-code-editor": "0.13.1",
|
||||
"remark-gfm": "4.0.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/diff": "^8.0.0",
|
||||
"@types/node": "^22.14.0",
|
||||
"@types/prismjs": "^1.26.5",
|
||||
"@types/react": "^19.1.8",
|
||||
"@types/react-dom": "^19.1.6",
|
||||
"typescript": "~5.7.2",
|
||||
"vite": "^6.2.0"
|
||||
}
|
||||
}
|
2057
pnpm-lock.yaml
generated
Normal file
2057
pnpm-lock.yaml
generated
Normal file
File diff suppressed because it is too large
Load Diff
30
tsconfig.json
Normal file
30
tsconfig.json
Normal file
@ -0,0 +1,30 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "ES2020",
|
||||
"experimentalDecorators": true,
|
||||
"useDefineForClassFields": false,
|
||||
"module": "ESNext",
|
||||
"lib": ["ES2020", "DOM", "DOM.Iterable"],
|
||||
"skipLibCheck": true,
|
||||
|
||||
/* Bundler mode */
|
||||
"moduleResolution": "bundler",
|
||||
"allowImportingTsExtensions": true,
|
||||
"isolatedModules": true,
|
||||
"moduleDetection": "force",
|
||||
"noEmit": true,
|
||||
"allowJs": true,
|
||||
"jsx": "react-jsx",
|
||||
|
||||
/* Linting */
|
||||
"strict": true,
|
||||
"noUnusedLocals": true,
|
||||
"noUnusedParameters": true,
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
"noUncheckedSideEffectImports": true,
|
||||
|
||||
"paths": {
|
||||
"@/*" : ["./*"]
|
||||
}
|
||||
}
|
||||
}
|
24
types.ts
Normal file
24
types.ts
Normal file
@ -0,0 +1,24 @@
|
||||
|
||||
export type ExercisePartType = 'struct-definition' | 'typedef-struct' | 'array-initialization' | 'pointer-manipulation' | 'printf' | 'function-definition' | 'main-function' | 'text' | 'mermaid';
|
||||
|
||||
export interface ExercisePart {
|
||||
id: string;
|
||||
prompt: string;
|
||||
solution: string;
|
||||
points: number;
|
||||
type: ExercisePartType;
|
||||
}
|
||||
|
||||
export interface Exercise {
|
||||
id: string;
|
||||
title: string;
|
||||
parts: ExercisePart[];
|
||||
explanation: string;
|
||||
}
|
||||
|
||||
export interface CheckResult {
|
||||
partId: string;
|
||||
isCorrect: boolean;
|
||||
explanation: string;
|
||||
error?: string; // To hold API or parsing errors
|
||||
}
|
17
vite.config.ts
Normal file
17
vite.config.ts
Normal file
@ -0,0 +1,17 @@
|
||||
import path from 'path';
|
||||
import { defineConfig, loadEnv } from 'vite';
|
||||
|
||||
export default defineConfig(({ mode }) => {
|
||||
const env = loadEnv(mode, '.', '');
|
||||
return {
|
||||
define: {
|
||||
'process.env.API_KEY': JSON.stringify(env.GEMINI_API_KEY),
|
||||
'process.env.GEMINI_API_KEY': JSON.stringify(env.GEMINI_API_KEY)
|
||||
},
|
||||
resolve: {
|
||||
alias: {
|
||||
'@': path.resolve(__dirname, '.'),
|
||||
}
|
||||
}
|
||||
};
|
||||
});
|
Reference in New Issue
Block a user