Warum DDD nicht nur "ein weiteres Pattern" ist
\n\nHaben Sie jemals ein Projekt von Grund auf neu geschrieben, nur weil "die Geschäftslogik über alle Controller verteilt war"? Oder ist Ihr UserManager vielleicht auf 2000 Zeilen angewachsen und niemand versteht, was er eigentlich tut? Kommt Ihnen bekannt vor?
In der Welt der Swift-Entwicklung sind wir an MVC, VIPER oder MVVM gewöhnt. Aber all diese Patterns lösen ein und dasselbe Problem: Wie trennt man die UI von der Logik. Sie sagen jedoch fast nichts darüber aus, wie man die Geschäftslogik selbst organisiert. Und hier kommt Domain-Driven Design (DDD) ins Spiel – ein Ansatz, der nicht das Framework, sondern die Fachdomäne Ihrer Anwendung in den Mittelpunkt stellt.
\n\nIn diesem Artikel werden wir uns ansehen, wie man Domain-Driven Design in Swift anhand realer Beispiele anwendet. Sie werden sehen, dass DDD keine abstrakte Theorie ist, sondern ein mächtiges Werkzeug, das Code lesbar, testbar und widerstandsfähig gegenüber Änderungen macht.
\n\nWas ist DDD in einfachen Worten
\n\nStellen Sie sich vor, Sie schreiben eine Anwendung für einen Online-Shop. MVC wird Ihnen sagen: "Erstelle ein Modell Product, einen Controller ProductViewController und Views". DDD hingegen wird fragen: "Was ist ein 'Produkt' in Ihrem Geschäft? Kann es ohne Preis existieren? Was passiert, wenn es in den Warenkorb gelegt wird?"
Domain-Driven Design ist eine Methode zur Modellierung von Software, bei der der Code die Sprache und die Regeln des Geschäfts so genau wie möglich widerspiegelt. Anstatt mit "technischen" Entitäten (wie DataManager) arbeiten Sie mit Domänenobjekten: Order, Customer, Invoice.
Die grundlegenden Bausteine von DDD
\n\nUm sich in den Begriffen nicht zu verlieren, merken Sie sich drei Hauptkonzepte:
\n\n- \n
- Entity (Entität) – ein Objekt, das eine Identität besitzt. Zwei Objekte mit denselben Feldern sind unterschiedliche Entitäten (wie zwei verschiedene Personen mit demselben Namen). \n
- Value Object (Wertobjekt) – ein Objekt, das nur durch seine Attribute definiert wird. Wenn bei zwei Objekten alle Felder übereinstimmen, handelt es sich um dasselbe Objekt (wie ein Datum oder ein Geldbetrag). \n
- Aggregate (Aggregat) – eine Gruppe zusammengehöriger Objekte, die als eine Einheit betrachtet wird. Das Aggregat hat eine Wurzel (Root Entity), über die der Zugriff auf alle internen Objekte erfolgt. \n
Klingt trocken? Dann lassen Sie uns zu Swift übergehen.
\n\nEin Domänenmodell in Swift schreiben
\n\nAngenommen, wir erstellen eine Anwendung zur Aufgabenverwaltung (TODO-Liste). Beginnen wir mit dem Einfachsten – einem Value Object für den Aufgabenstatus.
\n\n// Value Object: Aufgabenstatus\nstruct TaskStatus: Equatable {\n let rawValue: String\n \n static let todo = TaskStatus(rawValue: "todo")\n static let inProgress = TaskStatus(rawValue: "inProgress")\n static let done = TaskStatus(rawValue: "done")\n \n // Geschäftsregel: Es kann nur in den nächsten Status gewechselt werden\n func canTransition(to newStatus: TaskStatus) -> Bool {\n switch (self, newStatus) {\n case (.todo, .inProgress), (.inProgress, .done):\n return true\n default:\n return false\n }\n }\n}\n\nBeachten Sie: Wir verwenden kein enum, weil in der realen Geschäftswelt Status vom Server kommen oder in der Datenbank gespeichert werden können. TaskStatus ist ein unveränderliches Objekt, das seine eigenen Regeln kennt.
Jetzt erstellen wir eine Entity – die Aufgabe selbst:
\n\n// Entity: Aufgabe\nfinal class Task {\n let id: UUID\n var title: String\n var description: String\n private(set) var status: TaskStatus\n let createdAt: Date\n \n init(id: UUID = UUID(), title: String, description: String, status: TaskStatus = .todo) {\n self.id = id\n self.title = title\n self.description = description\n self.status = status\n \n