From ac192b50779cf4d8403d0933d7c37f21495972f7 Mon Sep 17 00:00:00 2001
From: Thimo Froehner <thimo.froehner@student.reutlingen-university.de>
Date: Mon, 29 Apr 2024 13:30:35 +0200
Subject: [PATCH] SECOND SET UP REST SERVICES

---
 docu.txt                         | 235 +++++++++++++++++++++++++++++++
 src/myaktion/db/db.go            |  33 +++++
 src/myaktion/go.mod              |  12 ++
 src/myaktion/go.sum              |  27 ++++
 src/myaktion/handler/campaign.go | 124 ++++++++++++++++
 src/myaktion/handler/donation.go |   1 +
 src/myaktion/handler/health.go   |  11 ++
 src/myaktion/handler/utils.go    |  32 +++++
 src/myaktion/main.go             |  43 ++++--
 src/myaktion/model/account.go    |   6 +-
 src/myaktion/model/campaign.go   |  18 ++-
 src/myaktion/model/donation.go   |  14 +-
 src/myaktion/service/campaign.go |  81 +++++++++++
 src/myaktion/service/donation.go |   1 +
 14 files changed, 612 insertions(+), 26 deletions(-)
 create mode 100644 docu.txt
 create mode 100644 src/myaktion/db/db.go
 create mode 100644 src/myaktion/go.sum
 create mode 100644 src/myaktion/handler/campaign.go
 create mode 100644 src/myaktion/handler/donation.go
 create mode 100644 src/myaktion/handler/health.go
 create mode 100644 src/myaktion/handler/utils.go
 create mode 100644 src/myaktion/service/campaign.go
 create mode 100644 src/myaktion/service/donation.go

diff --git a/docu.txt b/docu.txt
new file mode 100644
index 0000000..e0d4f7e
--- /dev/null
+++ b/docu.txt
@@ -0,0 +1,235 @@
+-- scripts --
+
+// start-mariadb.sh //
+
+#!/usr/bin/env bash:         Dies ist ein sogenanntes Shebang, das in Unix-ähnlichen Systemen verwendet wird, um dem System mitzuteilen, 
+                             welcher Interpreter für das Ausführen des Skripts verwendet werden soll. In diesem Fall wird Bash verwendet.
+docker run:                  Dieser Befehl wird verwendet, um einen Docker-Container zu erstellen und auszuführen.
+-d:                          Startet den Container im Hintergrund (detached mode).
+-p 3306:3306:                Leitet den Port 3306 vom Host auf den Port 3306 im Container weiter. Port 3306 ist der Standardport für MySQL/MariaDB.
+--name database:             Benennt den Container als "database".
+-e MYSQL_ROOT_PASSWORD=root: Setzt das Root-Passwort für die MySQL/MariaDB-Datenbank im Container auf "root".
+-e MYSQL_DATABASE=myaktion:  Erstellt eine neue Datenbank mit dem Namen "myaktion" beim Start des Containers.
+mariadb:10.5:                Das ist das Docker-Image, das verwendet wird, um den Container zu erstellen. 
+                             In diesem Fall wird das MariaDB-Image mit der Version 10.5 verwendet.
+
+// stop-mariadb.sh //
+
+docker kill database:        Dieser Befehl sendet ein SIGKILL-Signal an den Docker-Container mit dem Namen "database", um ihn sofort zu stoppen. 
+                             Dies ist äquivalent zum Befehl docker stop, jedoch wird der Container sofort gestoppt, ohne auf eine Reaktion des Containers zu warten.
+&&:                          Dies ist ein logischer Operator in Bash, der als "und" fungiert. Er führt den Befehl auf der rechten Seite nur dann aus, 
+                             wenn der Befehl auf der linken Seite erfolgreich ausgeführt wurde. In diesem Fall wird der Befehl auf der rechten Seite nur ausgeführt, 
+                             wenn der Befehl docker kill database erfolgreich war.
+docker rm database:          Dieser Befehl entfernt den Docker-Container mit dem Namen "database" aus dem System. 
+                             Es löscht den Container dauerhaft, so dass er nicht mehr in der Liste der Docker-Container vorhanden ist.
+
+-- Mandantory files -- 
+
+// go.mod //
+
+Modulname:                   Die Zeile module <modulname> gibt den Namen des Moduls an, zu dem das aktuelle Projekt gehört.
+Go-Version:                  Die Zeile go <go-version> spezifiziert die minimale Go-Version, die für das Projekt benötigt wird.
+Module (require):            Eine Liste der direkten Abhängigkeiten des Projekts, jede mit dem Modulnamen und der Versionsnummer.
+
+Die go.mod-Datei stellt sicher, dass das Go-Tool die richtigen Abhängigkeiten herunterlädt und die Versionskompatibilität überwacht, während das Projekt entwickelt wird.
+
+// go.sum //
+
+Die go.sum-Datei enthält die Summen der Prüfsummen für die Module und deren Versionen, die in einem Go-Projekt verwendet werden. 
+Diese Prüfsummen dienen dazu, sicherzustellen, dass die heruntergeladenen Module nicht manipuliert wurden und dass die Versionen, die im Projekt verwendet werden, korrekt sind.
+
+Jede Zeile in der go.sum-Datei enthält drei Felder:
+
+Modulname: Der Name des Moduls, für das die Prüfsumme berechnet wurde.
+Version: Die Version des Moduls, für das die Prüfsumme berechnet wurde.
+Prüfsumme: Die Prüfsumme des Moduls und seiner Dateien.
+
+// main.go //
+
+-- db -- 
+
+// db.go //
+
+package db:           Dies definiert das Paket "db", in dem dieser Code enthalten ist.
+Die importierten Pakete werden verwendet, um auf externe Funktionen und Bibliotheken zuzugreifen:
+fmt:                  Bietet Formatierungsfunktionen für Zeichenketten.
+os:                   Erlaubt den Zugriff auf Betriebssystemfunktionen.
+log:                  Ein Log-Paket, hier speziell logrus, das erweiterte Logging-Funktionen bietet.
+gorm.io/driver/mysql: Treiber für MySQL-Datenbankverbindungen.
+gorm.io/gorm:         GORM ist ein Go-ORM (Object-Relational Mapping) Framework für die Interaktion mit der Datenbank.
+
+var DB *gorm.DB:      Dies definiert eine globale Variable namens "DB", die eine Verbindung zur Datenbank repräsentiert.
+                      gorm.DB ist ein Typ in der GORM-Bibliothek, der eine Datenbankverbindung repräsentiert. In Go ist *gorm.DB ein Zeiger auf ein Objekt des Typs gorm.DB. 
+                      Der Typ *gorm.DB bietet eine Vielzahl von Methoden und Funktionen zum Ausführen von Datenbankabfragen, -transaktionen und -operationen.
+                      Durch die Verwendung eines Zeigers können Änderungen am gorm.DB-Objekt direkt an der zugrunde liegenden Datenbankverbindung durchgeführt werden, ohne eine Kopie des Objekts zu erstellen. Das bedeutet, dass Operationen auf dem *gorm.DB-Zeiger direkt auf die Datenbank angewendet werden.
+
+func init() { ... }:  Dies ist eine Funktion namens "init", die automatisch beim Start des Programms aufgerufen wird. Hier wird die Datenbank initialisiert und Migrationen durchgeführt.
+dsn := fmt.Sprintf("root:root@tcp(%s)/myaktion?charset=utf8&parseTime=True&loc=Local", os.Getenv("DB_CONNECT")): Ist dafür verantwortlich, eine Datenquellenzeichenfolge (DSN) zu erstellen, die von der Datenbankverbindungs-API verwendet wird, um eine Verbindung zur MySQL-Datenbank herzustellen.
+nochmal aufgschlüsselt: 
+os.Getenv("DB_CONNECT"): Diese Funktion ruft den Wert der Umgebungsvariable "DB_CONNECT" ab. Umgebungsvariablen sind in der Regel Konfigurationsparameter, die außerhalb des Codes festgelegt werden können. Hier wird erwartet, dass "DB_CONNECT" die Verbindungsinformationen zur Datenbank enthält, wie z. B. den Hostnamen, die Portnummer und andere erforderliche Parameter.
+fmt.Sprintf():           Diese Funktion formatiert eine Zeichenkette basierend auf einem Formatstring und optionalen Argumenten. Hier wird der Formatstring verwendet, um die DSN-Zeichenfolge zu erstellen.
+"root:root@tcp(%s)/myaktion?charset=utf8&parseTime=True&loc=Local": Dies ist der Formatstring, der die DSN-Zeichenfolge definiert. Es enthält die erforderlichen Verbindungsinformationen wie Benutzername (root), Passwort (root), Protokoll (tcp), Datenbankname (myaktion), 
+                                                                    Zeichensatz (utf8), und Optionen wie parseTime=True (um Zeitangaben automatisch in Go-Zeitobjekte zu parsen) und loc=Local (um die Zeitzone zu setzen).
+%s:                      Dies ist ein Platzhalter im Formatstring, der durch den Wert der Umgebungsvariable "DB_CONNECT" ersetzt wird. So wird die tatsächliche Verbindungsinformation in die DSN-Zeichenfolge eingefügt.
+dsn := fmt.Sprintf(...): Schließlich wird die DSN-Zeichenfolge in der Variablen dsn gespeichert, die dann für die Herstellung der Verbindung zur Datenbank verwendet wird.
+&gorm.Config{}:          Dies ist ein Konfigurationsobjekt für GORM, das optional ist. Hier wird ein leeres Konfigurationsobjekt übergeben, was bedeutet, dass die Standardeinstellungen von GORM verwendet werden.
+
+
+log.Info("Using DSN for DB:", dsn):                   Eine Log-Nachricht wird ausgegeben, die die verwendete Datenbankverbindungsinformationen anzeigt.
+DB, err = gorm.Open(mysql.Open(dsn), &gorm.Config{}): Hier wird eine Verbindung zur Datenbank hergestellt und in der globalen Variable "DB" gespeichert. Wenn ein Fehler auftritt, wird eine Panik ausgelöst.
+nochmal aufgschlüsselt:
+gorm.Open(...):  Dies ist eine Funktion von GORM, die eine Verbindung zur Datenbank herstellt. Sie akzeptiert zwei Argumente: den Datenbanktreiber und die Konfigurationsoptionen.
+mysql.Open(dsn): Dies ist eine Funktion aus dem GORM-Paket für MySQL-Datenbankverbindungen. Sie akzeptiert die DSN-Zeichenfolge (dsn) als Argument, die die Verbindungsinformationen zur Datenbank enthält.
+Die mysql.Open()-Funktion analysiert die übergebene DSN-Zeichenfolge und verwendet sie, um eine Verbindung zur angegebenen MySQL-Datenbank herzustellen. Wenn die Verbindung erfolgreich hergestellt werden kann, wird ein Objekt zurückgegeben, das die Verbindung repräsentiert, oder es wird ein Fehler zurückgegeben, wenn die Verbindung fehlschlägt.
+
+DB.Debug().AutoMigrate(&model.Campaign{}):            Automatische Migration wird für das "Campaign"-Modell durchgeführt, um sicherzustellen, dass die Datenbanktabellen mit den Modellstrukturen übereinstimmen. 
+                                                      Wenn ein Fehler auftritt, wird eine Panik ausgelöst.
+nochmal aufgschlüsselt:
+DB:                              Dies ist die Variable, die eine Verbindung zur Datenbank repräsentiert. Sie wurde zuvor initialisiert und konfiguriert, um auf eine MySQL-Datenbank zuzugreifen.
+.Debug():                        Dies ist eine Methode, die auf dem DB-Objekt aufgerufen wird und den Debug-Modus von GORM aktiviert. Wenn der Debug-Modus aktiviert ist, gibt GORM detaillierte Informationen über die 
+                                 ausgeführten SQL-Abfragen und die Ergebnisse dieser Abfragen aus. Dies ist besonders nützlich zum Debuggen und zur Überprüfung der ausgeführten Datenbankoperationen.
+.AutoMigrate(&model.Campaign{}): Diese Methode führt eine automatische Migration für das angegebene Modell durch. Eine Migration ist ein Prozess, bei dem die Datenbanktabellen entsprechend den Definitionen der Modelle aktualisiert werden. 
+                                 Hier wird das Modell Campaign als Argument übergeben, was bedeutet, dass die Tabellen in der Datenbank entsprechend der Struktur des Campaign-Modells aktualisiert werden sollen.
+                                 - Wenn die Tabelle noch nicht existiert, wird sie automatisch erstellt.
+                                 - Wenn die Tabelle bereits existiert, werden Änderungen vorgenommen, um sicherzustellen, dass sie den aktuellen Definitionen des Modells entspricht. Das bedeutet, dass Spalten hinzugefügt, entfernt oder geändert werden können, je nachdem, wie sich das Modell seit der letzten Migration geändert hat.
+
+DB.Debug().AutoMigrate(&model.Donation{}):            Gleiches wie oben, aber für das "Donation"-Modell.
+Log-Nachrichten werden ausgegeben, um den start und Abschluss der automatischen Migrationen anzuzeigen.
+
+-- model --
+
+// donation //
+
+- gorm.Model: ist ein in GORM eingebettetes Strukturfeld, das automatisch ID, CreatedAt, UpdatedAt und DeletedAt in der Datenbank verfolgt.
+- Amount ist ein Fließkommazahlenfeld, das den Spendenbetrag repräsentiert. Das Tag gorm:"notNull;check:amount >= 1.0" gibt an, dass das Feld nicht null sein darf und der Betrag mindestens 1.0 sein muss.
+- DonorName ist ein Zeichenfolgenfeld, das den Namen des Spenders enthält. Das Tag gorm:"notNull;size:40" gibt an, dass das Feld nicht null sein darf und eine maximale Länge von 40 Zeichen hat.
+- ReceiptRequested ist ein boolesches Feld, das angibt, ob eine Spendenquittung angefordert wurde. Das Tag gorm:"notNull" gibt an, dass das Feld nicht null sein darf.
+- Account ist ein eingebettetes Feld, das ein Kontoobjekt enthält. Das Tag gorm:"embedded;embeddedPrefix:account_" gibt an, dass die Attribute des Kontos in der Datenbank in die Spenden-Tabelle eingebettet werden sollen, und der Präfix "account_" wird verwendet, um Kollisionen mit anderen Feldern zu vermeiden.
+- Status ist ein Feld vom Typ "Status", der den Status der Spende angibt. Das Tag gorm:"notNull;type:ENUM('TRANSFERRED','IN_PROCESS')" gibt an, dass das Feld nicht null sein darf und nur die Werte "TRANSFERRED" oder "IN_PROCESS" annehmen kann.
+
+// campaign //
+
+- AmountDonatedSoFar ist ein Fließkommazahlenfeld, das den Betrag speichert, der bisher für die Kampagne gespendet wurde. Das Tag gorm:"-" bedeutet, dass dieses Feld nicht in der Datenbanktabelle gespeichert wird. 
+  Es wird wahrscheinlich verwendet, um temporäre Berechnungen oder Statusverfolgungen in der Anwendung durchzuführen, ohne sie persistent in der Datenbank zu speichern.
+- Donations ist ein Slice von Donation-Strukturen, das die Spenden enthält, die für diese Kampagne geleistet wurden. Das Tag gorm:"foreignKey:CampaignID;constraint:OnUpdate:CASCADE,OnDelete:CASCADE" 
+  gibt an, dass das Feld "CampaignID" als Fremdschlüssel verwendet wird und dass bei Aktualisierung oder Löschung der Kampagne auch die zugehörigen Spenden aktualisiert oder gelöscht werden sollen.
+
+-- service --
+
+// campaign //
+
+Dieser Code definiert eine Funktion namens CreateCampaign, die dazu dient, eine neue Kampagne in der Datenbank zu erstellen:
+
+result := db.DB.Create(campaign): Diese Zeile ruft die Methode Create auf dem Datenbankobjekt (db.DB) auf, um die übergebene Kampagne in der Datenbank zu erstellen. Die Funktion Create gibt ein Ergebnis zurück, das in der Variable result gespeichert wird.
+Create: In diesem Kontext wird die Create-Funktion dazu verwendet, eine neue Kampagne in der Datenbank zu erstellen, basierend auf dem übergebenen Kampagnenobjekt. 
+        Sie übernimmt die Aufgabe, die Daten aus dem übergebenen model.Campaign-Objekt in die entsprechende Tabelle in der Datenbank zu schreiben und eine eindeutige ID für die Kampagne zu generieren.
+if result.Error != nil { return result.Error }: Diese Zeile überprüft, ob beim Erstellen der Kampagne ein Fehler aufgetreten ist. Wenn result.Error nicht nil ist, bedeutet dies, dass ein Fehler aufgetreten ist. In diesem Fall wird der Fehler direkt zurückgegeben, und die Funktion wird beendet.
+Das result.Error-Feld wird verwendet, um auf den Fehler zurückzugreifen, der von der Datenbankoperation zurückgegeben wird.
+log.Infof("Successfully stored new campaign with ID %v in database.", campaign.ID): Diese Zeile protokolliert eine Erfolgsmeldung mit der ID der neu erstellten Kampagne, falls keine Fehler aufgetreten sind. Es wird die Information ausgegeben, dass die Kampagne erfolgreich in der Datenbank gespeichert wurde.
+log.Tracef("Stored: %v", campaign): Diese Zeile protokolliert detaillierte Informationen über die gespeicherte Kampagne auf Trace-Ebene, was für Debugging-Zwecke nützlich sein kann.
+return nil: Wenn das Erstellen der Kampagne erfolgreich war und keine Fehler aufgetreten sind, wird nil zurückgegeben, um anzuzeigen, dass die Funktion erfolgreich abgeschlossen wurde.
+
+Diese Funktion, GetCampaigns(), hat den Zweck, alle Kampagnen aus der Datenbank abzurufen und zurückzugeben:
+
+var campaigns []model.Campaign: Hier wird eine leere Slice (eine Art dynamisches Array) von Kampagnen erstellt, die später mit den aus der Datenbank abgerufenen Kampagnen gefüllt wird.
+if result.Error != nil { return nil, result.Error }: Diese Zeile überprüft, ob bei der Datenbankabfrage ein Fehler aufgetreten ist. 
+                                                     Wenn ein Fehler aufgetreten ist (das Error-Feld von result nicht nil ist), wird dieser Fehler zusammen mit nil zurückgegeben, um anzuzeigen, dass die Funktion nicht erfolgreich war.
+                                                     Nil steht hierbei, dass keine campaigns gefunden wurden
+
+result := db.DB.Preload("Donations").Find(&campaigns): Diese Zeile führt eine Datenbankabfrage durch, um alle Kampagnen aus der Datenbank zu laden. Durch das & werden die gefunden Daten direkt in das campaign slice geschrieben. 
+Das Preload("Donations") sorgt dafür, dass auch die Spenden, die mit den Kampagnen verbunden sind, im Voraus geladen werden. Das Ergebnis wird in der Variable result gespeichert.
+
+-- handler --
+
+// health //
+
+Die Funktion Health nimmt zwei Parameter entgegen: w http.ResponseWriter und r *http.Request. w ist ein Objekt, das es der Funktion ermöglicht, 
+Daten an den Client zu senden, und r ist das HTTP-Anfrageobjekt, das Informationen über die eingehende Anfrage enthält.
+
+w.Header().Set("Content-Type", "application/json"): Diese Zeile setzt den Content-Type-Header der HTTP-Antwort auf "application/json", 
+                                                    was bedeutet, dass der Client erwartet, dass die Antwort im JSON-Format vorliegt. Schreibt eine HTTP-Antwort an den Client.
+io.WriteString(w, {"alive": true}): Diese Zeile sendet den JSON-String {"alive": true} als Antwort an den Client. Dieser String gibt an, dass der Server "lebendig" ist. 
+                                    io.WriteString schreibt den angegebenen String in den http.ResponseWriter, der die Antwort an den Client sendet.
+                                    Die Methode WriteString gehört zur io-Paketbibliothek in Go und wird verwendet, um einen String in einen Writer zu schreiben. In diesem Fall wird sie verwendet, um den bereitgestellten JSON-String in den http.ResponseWriter zu schreiben.
+                                    Das erste Argument w ist der http.ResponseWriter, in den der String geschrieben werden soll.
+                                    Das zweite Argument s ist der String-Inhalt, der in den http.ResponseWriter geschrieben werden soll.
+
+// utils //
+
+type result struct {Success string `json:"success"`}: Durch die Verwendung von Backticks (`) wird ein sogenanntes "Tag" für das Feld angegeben. In diesem Tag wird definiert, wie das Feld serialisiert werden soll, wenn es in JSON umgewandelt wird.
+                                                      Hier wird das JSON-Tag json:"success" verwendet. Dieses Tag gibt an, dass das Feld "Success" beim Umwandeln in JSON den Schlüsselnamen "success" haben soll. 
+                                                      Das heißt, wenn eine Instanz dieser Struktur in JSON umgewandelt wird, wird das Feld "Success" als "success" in JSON erscheinen.
+
+Funktion sendJson: Diese Funktion wird verwendet, um JSON-Daten an den Client zu senden.
+w http.ResponseWriter: Dies ist ein Interface, das von der net/http-Paket bereitgestellt wird und verwendet wird, um HTTP-Antworten zu schreiben. Durch die Verwendung dieses Interfaces können wir Daten an den Client senden, der die HTTP-Anfrage gestellt hat.
+value interface{}:     Dies ist ein leeres Interface, was bedeutet, dass die Funktion einen Wert jeglichen Typs akzeptieren kann. Das ermöglicht es, dass die Funktion eine Vielzahl von Datenstrukturen als Eingabe erhalten kann.
+json.NewEncoder(w).Encode(value): json.NewEncoder(w): Diese Funktion erstellt einen neuen JSON-Encoder, der mit dem übergebenen http.ResponseWriter verbunden ist. Der http.ResponseWriter ist die Schnittstelle, über die der Server HTTP-Antworten zurück an den Client sendet.
+                                                      .Encode(value): Auf den JSON-Encoder wird die Methode Encode aufgerufen, wobei der zu kodierende Wert (value) übergeben wird. Diese Methode kodiert den Wert value als JSON und schreibt ihn in den Ausgabedatenstrom, der mit dem http.ResponseWriter verbunden ist.
+http.Error(w, err.Error(), http.StatusInternalServerError): Diese Zeile sendet dem Client eine HTTP-Fehlerantwort mit dem HTTP-Statuscode "Internal Server Error" (Statuscode 500). Es wird die Error-Methode des Fehlers (err.Error()) verwendet, um die Fehlermeldung zu erhalten, die dem Client gesendet wird. 
+                                                            Der http.Error-Aufruf setzt dann den HTTP-Statuscode auf 500 und sendet die Fehlermeldung an den Client über den http.ResponseWriter.
+
+func getID: 
+vars := mux.Vars(r): Hier wird die Funktion mux.Vars aus dem Paket "gorilla/mux" verwendet, um die Variablen zu extrahieren, die im Anfrage-Context (Request-Context) gespeichert sind. 
+                     Das "gorilla/mux"-Paket wird häufig verwendet, um URL-Routen in Go-Anwendungen zu definieren und zu verwalten. Diese Funktion erwartet eine Anfrage (r) und gibt eine Karte (Map) von Variablen zurück, die im Anfrage-Context gespeichert sind.
+id, err := strconv.ParseUint(vars["id"], 10, 0): Hier wird die ID aus den Variablen extrahiert, die im vorherigen Schritt erhalten wurden. Die ID wird als Zeichenkette (String) extrahiert und dann in eine vorzeichenlose Ganzzahl (uint) umgewandelt, indem die Funktion strconv.ParseUint verwendet wird. 
+                                                 Die Funktion ParseUint erwartet die Zeichenkette, die konvertiert werden soll, die Basis (in diesem Fall 10 für dezimale Zahlen) und die Bitgröße der zurückgegebenen Ganzzahl (hier 0, was bedeutet, dass die Bitgröße abhängig von der Architektur ist). 
+                                                 Wenn ein Fehler auftritt, wird der Fehlerwert (err) nicht null sein, andernfalls wird die ID zurückgegeben.
+if err != nil { log.Errorf("Can't get ID from request: %v", err) return 0, err }: Hier wird überprüft, ob ein Fehler beim Parsen der ID aufgetreten ist. Wenn ein Fehler auftritt, wird der Fehler protokolliert und die Funktion gibt eine ID von 0 und den Fehlerwert zurück. 
+                                                                                  Dies bedeutet, dass die Extraktion der ID fehlgeschlagen ist und die Anfrage nicht erfolgreich verarbeitet werden konnte.
+return uint(id), nil: Wenn kein Fehler aufgetreten ist, wird die extrahierte ID zurückgegeben. Da die Funktion den Typ uint zurückgibt und der id-Wert ein uint ist, wird der id-Wert einfach in einen uint-Wert konvertiert. 
+                      Der zweite Rückgabewert ist nil, was bedeutet, dass keine Fehler aufgetreten sind.
+
+// campaign //
+
+CreateCampaign-Funktion:
+
+- Diese Funktion wird aufgerufen, um eine neue Kampagne zu erstellen. Sie erwartet eine HTTP-Anfrage (r) und einen Response-Writer (w), um die Antwort an den Client zu senden.
+- Zuerst wird versucht, die Kampagne aus der Anfrage zu extrahieren, indem die Funktion getCampaign aufgerufen wird. Die Funktion getCampaign decodiert den JSON-Body der Anfrage und konvertiert ihn in eine Kampagnenstruktur.
+- Wenn ein Fehler beim Decodieren der Anfrage auftritt, wird der Fehler protokolliert und ein HTTP-Fehler mit dem Statuscode "BadRequest" (400) an den Client gesendet.
+- Anschließend wird die Funktion service.CreateCampaign aufgerufen, um die erstellte Kampagne in der Datenbank zu speichern. Wenn dabei ein Fehler auftritt, wird dieser ebenfalls protokolliert und ein HTTP-Fehler mit dem Statuscode "InternalServerError" (500) an den Client gesendet.
+- Schließlich wird die erstellte Kampagne als JSON an den Client gesendet, indem die sendJson-Funktion aufgerufen wird.
+
+getCampaign-Funktion:
+
+- Diese Funktion wird verwendet, um eine Kampagne aus dem JSON-Body einer HTTP-Anfrage zu extrahieren.
+- Zuerst wird versucht, den JSON-Body der Anfrage zu decodieren und in eine Kampagnenstruktur zu konvertieren.
+- Wenn ein Fehler beim Decodieren auftritt, wird der Fehler protokolliert und zurückgegeben.
+
+Zusammengefasst liest diese Zeile JSON-Daten aus dem Body einer HTTP-Anfrage (r.Body) mithilfe eines JSON-Decoders und decodiert sie dann in die Kampagnenstruktur, die durch &campaign repräsentiert wird. 
+Nachdem dieser Vorgang abgeschlossen ist, enthält die Variable campaign die decodierten Daten aus dem JSON-Body der Anfrage.
+
+GetCampaigns-Funktion:
+
+- Zuerst werden alle Kampagnen aus der Datenbank abgerufen, indem die Funktion service.GetCampaigns aufgerufen wird.
+- Wenn ein Fehler beim Abrufen der Kampagnen auftritt, wird dieser protokolliert und ein HTTP-Fehler mit dem Statuscode "InternalServerError" (500) an den Client gesendet.
+- Schließlich werden die abgerufenen Kampagnen als JSON an den Client gesendet, indem die sendJson-Funktion aufgerufen wird.
+
+Beim Decodieren (Decode) wird ein JSON-Objekt in die Struktur (oder Typ) umgewandelt, die in Go repräsentiert wird. In diesem Fall wird der JSON-Body der Anfrage in eine Kampagnenstruktur umgewandelt. 
+Das bedeutet, dass die Daten, die im JSON-Format vorliegen, in eine Go-Datenstruktur überführt werden, damit sie in der Anwendung weiterverwendet werden können.
+
+Beim Codieren (Encode) wird die Struktur (oder Typ) wieder in ein JSON-Objekt umgewandelt. Hier wird die Kampagnenstruktur wieder in das JSON-Format umgewandelt, sodass sie als Antwort an den Client gesendet werden kann. 
+Auf diese Weise können Daten zwischen der Anwendung und dem Client ausgetauscht werden, wobei JSON als das gemeinsame Datenformat verwendet wird.
+
+-- main -- 
+
+Funktion init: 
+
+- log.SetFormatter(&log.TextFormatter{}): Hier wird der Logger-Formatter auf einen Text-Formatter gesetzt. Dies bedeutet, dass die Lognachrichten im Textformat ausgegeben werden.
+- log.SetReportCaller(true): Diese Zeile teilt dem Logger mit, dass der Dateiname und die Zeilennummer des Aufrufs in der Lognachricht angezeigt werden sollen. Das ist hilfreich für die Fehlerbehebung, um zu wissen, von welcher Stelle im Code die Lognachricht stammt.
+- level, err := log.ParseLevel(os.Getenv("LOG_LEVEL")): Hier wird versucht, das Log-Level aus einer Umgebungsvariablen namens "LOG_LEVEL" zu lesen. Das bedeutet, dass das Log-Level durch Setzen dieser Umgebungsvariablen gesteuert werden kann.
+- if err != nil { ... }: Dieser Abschnitt wird ausgeführt, wenn ein Fehler beim Parsen des Log-Levels aufgetreten ist. In diesem Fall wird das Log-Level auf "INFO" gesetzt und eine entsprechende Nachricht wird protokolliert.
+- log.SetLevel(level): Wenn das Log-Level erfolgreich aus der Umgebungsvariable gelesen wurde, wird es hier gesetzt. Dadurch wird das Log-Level auf den von der Umgebungsvariable bestimmten Wert festgelegt.
+
+- Der Wert von LOG_LEVEL also ein String wird geparst und in ein entsprechendes Log-Level Konstante umgewandelt z.B. "INFO" in 'INFO'
+
+Funktion main:
+
+- router := mux.NewRouter(): Hier wird ein neuer Router aus dem Paket "mux" initialisiert. Mux ist ein leistungsfähiges Paket für das Routing von HTTP-Anfragen in Go-Anwendungen. Der Router wird verwendet, um die Routen für die API-Endpunkte zu definieren.
+- router.HandleFunc("/health", handler.Health).Methods("GET"): Hier wird ein Endpunkt "/health" definiert, der HTTP-GET-Anfragen entgegennimmt und sie an die Funktion handler.Health weiterleitet. Diese Funktion wird verwendet, um die Gesundheit des Servers zu überprüfen.
+- router.HandleFunc("/campaigns", handler.CreateCampaign).Methods("POST"): Dies definiert einen Endpunkt "/campaigns", der HTTP-POST-Anfragen entgegennimmt und sie an die Funktion handler.CreateCampaign weiterleitet. Diese Funktion wird verwendet, um eine neue Kampagne zu erstellen.
+- router.HandleFunc("/campaigns", handler.GetCampaigns).Methods("GET"): Hier wird ein Endpunkt "/campaigns" definiert, der HTTP-GET-Anfragen entgegennimmt und sie an die Funktion handler.GetCampaigns weiterleitet. Diese Funktion wird verwendet, um eine Liste aller Kampagnen abzurufen.
+- http.ListenAndServe(":8000", router): Hier wird der HTTP-Server gestartet und auf Port 8000 gehorcht. Der Server verwendet den zuvor konfigurierten Router, um HTTP-Anfragen zu verarbeiten.
+- if err := ...: Dieser Abschnitt überprüft, ob beim Starten des Servers ein Fehler aufgetreten ist. Wenn ein Fehler auftritt, wird er mit log.Fatal(err) protokolliert und das Programm wird beendet.
+
+In Go ist die Funktion http.ListenAndServe eine blockierende Funktion. Das bedeutet, dass der Code nach dem Aufruf von http.ListenAndServe nicht weiter ausgeführt wird, bis der Server gestoppt wird (zum Beispiel durch einen Fehler oder das Beenden des Programms). 
+Daher ist es wichtig, dass alle notwendigen Konfigurationen (wie das Hinzufügen von Routen) vor dem Aufruf von http.ListenAndServe durchgeführt werden.
\ No newline at end of file
diff --git a/src/myaktion/db/db.go b/src/myaktion/db/db.go
new file mode 100644
index 0000000..0857b9d
--- /dev/null
+++ b/src/myaktion/db/db.go
@@ -0,0 +1,33 @@
+package db
+
+import (
+	"fmt"
+	//"os"
+
+	log "github.com/sirupsen/logrus"
+	"gitlab.reutlingen-university.de/frthih23/myaktion-go/src/myaktion/model"
+	"gorm.io/driver/mysql"
+	"gorm.io/gorm"
+)
+
+var DB *gorm.DB
+
+func init() {
+	var connect string = "127.0.0.1:3307"
+	//dsn := fmt.Sprintf("root:root@tcp(%s)/myaktion?charset=utf8&parseTime=True&loc=Local", os.Getenv("DB_CONNECT"))
+	dsn := fmt.Sprintf("root:root@tcp(%s)/myaktion?charset=utf8&parseTime=True&loc=Local", connect)
+	log.Info("Using DSN for DB:", dsn)
+	var err error
+	DB, err = gorm.Open(mysql.Open(dsn), &gorm.Config{})
+	if err != nil {
+		panic("failed to connect to database")
+	}
+	log.Info("Starting automatic migrations")
+	if err := DB.Debug().AutoMigrate(&model.Campaign{}); err != nil {
+		panic(err)
+	}
+	if err := DB.Debug().AutoMigrate(&model.Donation{}); err != nil {
+		panic(err)
+	}
+	log.Info("Automatic migrations finished")
+}
diff --git a/src/myaktion/go.mod b/src/myaktion/go.mod
index 9218561..d751446 100644
--- a/src/myaktion/go.mod
+++ b/src/myaktion/go.mod
@@ -1,3 +1,15 @@
 module gitlab.reutlingen-university.de/frthih23/myaktion-go/src/myaktion
 
 go 1.22.1
+
+require (
+	filippo.io/edwards25519 v1.1.0 // indirect
+	github.com/go-sql-driver/mysql v1.8.1 // indirect
+	github.com/gorilla/mux v1.8.1 // indirect
+	github.com/jinzhu/inflection v1.0.0 // indirect
+	github.com/jinzhu/now v1.1.5 // indirect
+	github.com/sirupsen/logrus v1.9.3 // indirect
+	golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8 // indirect
+	gorm.io/driver/mysql v1.5.6 // indirect
+	gorm.io/gorm v1.25.9 // indirect
+)
diff --git a/src/myaktion/go.sum b/src/myaktion/go.sum
new file mode 100644
index 0000000..d4c3b6a
--- /dev/null
+++ b/src/myaktion/go.sum
@@ -0,0 +1,27 @@
+filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
+filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
+github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/go-sql-driver/mysql v1.7.0/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI=
+github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y=
+github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg=
+github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY=
+github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ=
+github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
+github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
+github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
+github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
+github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
+github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
+github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
+golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8 h1:0A+M6Uqn+Eje4kHMK80dtF3JCXC4ykBgQG4Fe06QRhQ=
+golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+gorm.io/driver/mysql v1.5.6 h1:Ld4mkIickM+EliaQZQx3uOJDJHtrd70MxAUqWqlx3Y8=
+gorm.io/driver/mysql v1.5.6/go.mod h1:sEtPWMiqiN1N1cMXoXmBbd8C6/l+TESwriotuRRpkDM=
+gorm.io/gorm v1.25.7/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8=
+gorm.io/gorm v1.25.9 h1:wct0gxZIELDk8+ZqF/MVnHLkA1rvYlBWUMv2EdsK1g8=
+gorm.io/gorm v1.25.9/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8=
diff --git a/src/myaktion/handler/campaign.go b/src/myaktion/handler/campaign.go
new file mode 100644
index 0000000..b84b728
--- /dev/null
+++ b/src/myaktion/handler/campaign.go
@@ -0,0 +1,124 @@
+package handler
+
+import (
+	"encoding/json"
+	"net/http"
+
+	log "github.com/sirupsen/logrus"
+
+	"gitlab.reutlingen-university.de/frthih23/myaktion-go/src/myaktion/model"
+	"gitlab.reutlingen-university.de/frthih23/myaktion-go/src/myaktion/service"
+)
+
+func CreateCampaign(w http.ResponseWriter, r *http.Request) {
+	var campaign *model.Campaign
+	campaign, err := getCampaign(r)
+	if err != nil {
+		log.Printf("Can't serialize request body to campaign struct: %v", err)
+		http.Error(w, err.Error(), http.StatusBadRequest)
+		return
+	}
+	if err := service.CreateCampaign(campaign); err != nil {
+		log.Printf("Error calling service CreateCampaign: %v", err)
+		http.Error(w, err.Error(), http.StatusInternalServerError)
+		return
+	}
+	sendJson(w, campaign)
+}
+
+func GetCampaigns(w http.ResponseWriter, _ *http.Request) {
+	campaigns, err := service.GetCampaigns()
+	if err != nil {
+		log.Printf("Error calling service GetCampaigns: %v", err)
+		http.Error(w, err.Error(), http.StatusInternalServerError)
+		return
+	}
+	sendJson(w, campaigns)
+}
+
+func GetCampaign(w http.ResponseWriter, r *http.Request) {
+	var campaign *model.Campaign
+	id, err := getId(r)
+	if err != nil {
+		log.Printf("Can't get ID from request: %v", err)
+		http.Error(w, err.Error(), http.StatusBadRequest)
+		return
+	}
+	campaign, err = service.GetCampaign(id)
+	if err != nil {
+		log.Printf("Error calling service GetCampaign: %v", err)
+		http.Error(w, err.Error(), http.StatusInternalServerError)
+		return
+	}
+	sendJson(w, campaign)
+}
+
+func UpdateCampaign(w http.ResponseWriter, r *http.Request) {
+	id, err := getId(r)
+	if err != nil {
+		log.Printf("Can't get ID from request: %v", err)
+		http.Error(w, err.Error(), http.StatusBadRequest)
+		return
+	}
+	campaign, err := getCampaign(r)
+	if err != nil {
+		log.Printf("Can't serialize request body to campaign struct: %v", err)
+		http.Error(w, err.Error(), http.StatusBadRequest)
+		return
+	}
+	if err := service.UpdateCampaign(id, campaign); err != nil {
+		log.Printf("Error calling service UpdateCampaign: %v", err)
+		http.Error(w, err.Error(), http.StatusInternalServerError)
+		return
+	}
+	sendJson(w, campaign)
+}
+
+func DeleteCampaign(w http.ResponseWriter, r *http.Request) {
+	id, err := getId(r)
+	if err != nil {
+		log.Printf("Can't get ID from request: %v", err)
+		http.Error(w, err.Error(), http.StatusBadRequest)
+		return
+	}
+	if err := service.DeleteCampaign(id); err != nil {
+		log.Printf("Error deleting campaign: %v", err)
+		http.Error(w, "Failed to delete campaign", http.StatusInternalServerError)
+		return
+	}
+	response := Result{
+		Success: "Campaign deleted successfully",
+	}
+	sendJson(w, response)
+}
+
+func AddDonation(w http.ResponseWriter, r *http.Request) {
+	id, err := getId(r)
+	if err != nil {
+		log.Printf("Can't get ID from request: %v", err)
+		http.Error(w, err.Error(), http.StatusBadRequest)
+		return
+	}
+	var donation model.Donation
+	if err := json.NewDecoder(r.Body).Decode(&donation); err != nil {
+		http.Error(w, "Failed to decode donation data", http.StatusBadRequest)
+		return
+	}
+	campaign, err := service.AddDonation(id, &donation)
+	if err != nil {
+		log.Printf("Error adding donation: %v", err)
+		http.Error(w, "Failed to add donation", http.StatusInternalServerError)
+		return
+	}
+	sendJson(w, campaign)
+}
+
+func getCampaign(r *http.Request) (*model.Campaign, error) {
+	var campaign model.Campaign
+	err := json.NewDecoder(r.Body).Decode(&campaign)
+	if err != nil {
+		log.Errorf("Can't serialize request body to campaign struct: %v", err)
+		return nil, err
+	}
+	return &campaign, nil
+}
diff --git a/src/myaktion/handler/donation.go b/src/myaktion/handler/donation.go
new file mode 100644
index 0000000..abeebd1
--- /dev/null
+++ b/src/myaktion/handler/donation.go
@@ -0,0 +1 @@
+package handler
diff --git a/src/myaktion/handler/health.go b/src/myaktion/handler/health.go
new file mode 100644
index 0000000..bb62dc7
--- /dev/null
+++ b/src/myaktion/handler/health.go
@@ -0,0 +1,11 @@
+package handler
+
+import (
+	"io"
+	"net/http"
+)
+
+func Health(w http.ResponseWriter, r *http.Request) {
+	w.Header().Set("Content-Type", "application/json")
+	io.WriteString(w, `{"alive": true}`)
+}
diff --git a/src/myaktion/handler/utils.go b/src/myaktion/handler/utils.go
new file mode 100644
index 0000000..e9407b9
--- /dev/null
+++ b/src/myaktion/handler/utils.go
@@ -0,0 +1,32 @@
+package handler
+
+import (
+	"encoding/json"
+	"net/http"
+	"strconv"
+
+	"github.com/gorilla/mux"
+	log "github.com/sirupsen/logrus"
+)
+
+type Result struct {
+	Success string `json:"success"`
+}
+
+func sendJson(w http.ResponseWriter, value interface{}) {
+	w.Header().Set("Content-Type", "application/json")
+	if err := json.NewEncoder(w).Encode(value); err != nil {
+		log.Errorf("Failure encoding value to JSON: %v", err)
+		http.Error(w, err.Error(), http.StatusInternalServerError)
+	}
+}
+
+func getId(r *http.Request) (uint, error) {
+	vars := mux.Vars(r)
+	id, err := strconv.ParseUint(vars["id"], 10, 0)
+	if err != nil {
+		log.Errorf("Can't get ID from request: %v", err)
+		return 0, err
+	}
+	return uint(id), nil
+}
diff --git a/src/myaktion/main.go b/src/myaktion/main.go
index 2254b75..94bbe9c 100644
--- a/src/myaktion/main.go
+++ b/src/myaktion/main.go
@@ -2,21 +2,42 @@ package main
 
 import (
 	"fmt"
+	"os"
 
-	"gitlab.reutlingen-university.de/frthih23/myaktion-go/src/myaktion/model"
+	log "github.com/sirupsen/logrus"
+
+	"net/http"
+
+	"github.com/gorilla/mux"
+	"gitlab.reutlingen-university.de/frthih23/myaktion-go/src/myaktion/handler"
+	//"gitlab.reutlingen-university.de/frthih23/myaktion-go/src/myaktion/model"
 )
 
 func main() {
 
-	account := model.Account{Name: "Test", BankName: "Sparkasse", Number: "238244"}
-	fmt.Printf("Name: %s\n", account.Name)
-
-	donation := model.Donation{Amount: 500.45, DonorName: "Thimo", ReceiptRequested: true, Status: model.IN_PROCESS,
-		Account: model.Account{
-			Name:     "Thimo",
-			BankName: "Sparkasse",
-			Number:   "39494",
-		}}
+	fmt.Println("Starting MyAktion API Server")
+	router := mux.NewRouter()
+	router.HandleFunc("/health", handler.Health).Methods("GET")
+	router.HandleFunc("/campaigns", handler.CreateCampaign).Methods("POST")
+	router.HandleFunc("/campaigns", handler.GetCampaigns).Methods("GET")
+	router.HandleFunc("/campaigns/{id}", handler.GetCampaign).Methods("GET")
+	router.HandleFunc("/campaigns/{id}", handler.UpdateCampaign).Methods("PUT")
+	router.HandleFunc("/campaigns/{id}", handler.DeleteCampaign).Methods("DELETE")
+	router.HandleFunc("/campaigns/{id}/donation", handler.AddDonation).Methods("POST")
+	if err := http.ListenAndServe(":8000", router); err != nil {
+		log.Fatal(err)
+	}
+}
 
-	fmt.Println(donation)
+func init() {
+	// init logger
+	log.SetFormatter(&log.TextFormatter{})
+	log.SetReportCaller(true)
+	level, err := log.ParseLevel(os.Getenv("LOG_LEVEL"))
+	if err != nil {
+		log.Info("Log level not specified, set default to: INFO")
+		log.SetLevel(log.InfoLevel)
+		return
+	}
+	log.SetLevel(level)
 }
diff --git a/src/myaktion/model/account.go b/src/myaktion/model/account.go
index d2e74b4..e0e927a 100644
--- a/src/myaktion/model/account.go
+++ b/src/myaktion/model/account.go
@@ -1,7 +1,7 @@
 package model
 
 type Account struct {
-	Name     string
-	BankName string
-	Number   string
+	Name     string `gorm:"notNull;size:60"`
+	BankName string `gorm:"notNull;size:40"`
+	Number   string `gorm:"notNull;size:20"`
 }
diff --git a/src/myaktion/model/campaign.go b/src/myaktion/model/campaign.go
index 3074a37..117903c 100644
--- a/src/myaktion/model/campaign.go
+++ b/src/myaktion/model/campaign.go
@@ -1,11 +1,15 @@
 package model
 
+import "gorm.io/gorm"
+
 type Campaign struct {
-	Name               string
-	OrganizerName      string
-	TargetAmount       float64
-	DonationMinimum    float64
-	AmountDonatedSoFar float64
-	Donations          []Donation
-	Account            Account
+	gorm.Model
+	Name               string     `gorm:"notNull;size:30"`
+	OrganizerName      string     `gorm:"notNull"`
+	TargetAmount       float64    `gorm:"notNull;check:target_amount >= 10.0"`
+	DonationMinimum    float64    `gorm:"notNull;check:donation_minimum >= 1.0"`
+	AmountDonatedSoFar float64    `gorm:"-"`
+	Donations          []Donation `gorm:"foreignKey:CampaignID;constraint:OnUpdate:CASCADE,OnDelete:CASCADE"`
+	Account            Account    `gorm:"embedded;embeddedPrefix:account_"`
+	ID                 uint
 }
diff --git a/src/myaktion/model/donation.go b/src/myaktion/model/donation.go
index 4bf6af1..46470c0 100644
--- a/src/myaktion/model/donation.go
+++ b/src/myaktion/model/donation.go
@@ -1,5 +1,7 @@
 package model
 
+import "gorm.io/gorm"
+
 type Status string
 
 const (
@@ -8,9 +10,11 @@ const (
 )
 
 type Donation struct {
-	Amount           float64
-	DonorName        string
-	ReceiptRequested bool
-	Account          Account
-	Status           Status
+	gorm.Model
+	CampaignID       uint64
+	Amount           float64 `gorm:"notNull;check:amount >= 1.0"`
+	DonorName        string  `gorm:"notNull;size:40"`
+	ReceiptRequested bool    `gorm:"notNull"`
+	Account          Account `gorm:"embedded;embeddedPrefix:account_"`
+	Status           Status  `gorm:"notNull;type:ENUM('TRANSFERRED','IN_PROCESS')"`
 }
diff --git a/src/myaktion/service/campaign.go b/src/myaktion/service/campaign.go
new file mode 100644
index 0000000..65383e9
--- /dev/null
+++ b/src/myaktion/service/campaign.go
@@ -0,0 +1,81 @@
+package service
+
+import (
+	log "github.com/sirupsen/logrus"
+
+	"gitlab.reutlingen-university.de/frthih23/myaktion-go/src/myaktion/db"
+	"gitlab.reutlingen-university.de/frthih23/myaktion-go/src/myaktion/model"
+)
+
+func CreateCampaign(campaign *model.Campaign) error {
+	result := db.DB.Create(campaign)
+	if result.Error != nil {
+		return result.Error
+	}
+	log.Infof("Successfully stored new campaign with ID %v in database.", campaign.ID)
+	log.Tracef("Stored: %v", campaign)
+	return nil
+}
+
+func GetCampaigns() ([]model.Campaign, error) {
+	var campaigns []model.Campaign
+	result := db.DB.Preload("Donations").Find(&campaigns)
+	if result.Error != nil {
+		return nil, result.Error
+	}
+	log.Tracef("Retrieved: %v", campaigns)
+	return campaigns, nil
+}
+
+func GetCampaign(id uint) (*model.Campaign, error) {
+	var campaign model.Campaign
+	result := db.DB.Preload("Donations").First(&campaign, id)
+	if result.Error != nil {
+		return nil, result.Error
+	}
+	log.Tracef("Retrieved: %v", campaign)
+	return &campaign, nil
+}
+
+func UpdateCampaign(id uint, updatedCampaign *model.Campaign) error {
+	var campaign model.Campaign
+	result := db.DB.First(&campaign, id)
+	if result.Error != nil {
+		return result.Error
+	}
+
+	campaign.Name = updatedCampaign.Name
+	campaign.OrganizerName = updatedCampaign.OrganizerName
+	campaign.TargetAmount = updatedCampaign.TargetAmount
+	campaign.DonationMinimum = updatedCampaign.DonationMinimum
+
+	result = db.DB.Save(&campaign)
+	if result.Error != nil {
+		return result.Error
+	}
+	log.Infof("Successfully updated new campaign with ID %v in database.", campaign.ID)
+	log.Tracef("Updated: %v", campaign)
+	return nil
+}
+
+func DeleteCampaign(id uint) error {
+	result := db.DB.Delete(&model.Campaign{}, id)
+	if result.Error != nil {
+		return result.Error
+	}
+	return nil
+}
+
+func AddDonation(id uint, donation *model.Donation) (*model.Campaign, error) {
+	var campaign model.Campaign
+	result := db.DB.Preload("Donations").First(&campaign, id)
+	if result.Error != nil {
+		return nil, result.Error
+	}
+	campaign.Donations = append(campaign.Donations, *donation)
+	result = db.DB.Save(&campaign)
+	if result.Error != nil {
+		return nil, result.Error
+	}
+	return &campaign, nil
+}
diff --git a/src/myaktion/service/donation.go b/src/myaktion/service/donation.go
new file mode 100644
index 0000000..6d43c33
--- /dev/null
+++ b/src/myaktion/service/donation.go
@@ -0,0 +1 @@
+package service
-- 
GitLab