Domain-Driven Design: Was mir niemand vorher gesagt hat

Domain-Driven Design: Was mir niemand vorher gesagt hat

Meine ehrliche Meinung zu DDD nach Jahren im Produktiveinsatz. Wann es den Schmerz wert ist, wann nicht, und was wirklich zählt.

Raul Lugo

Domain-Driven Design: Was mir niemand vorher gesagt hat

Ich sag's direkt: Als ich das erste Mal Eric Evans' DDD-Buch in die Hand nahm, dachte ich, ich hätte die Lösung für alle meine Architektur-Probleme gefunden. Spoiler: Hab ich nicht. Aber nach drei Jahren DDD-Implementierung in Produktivsystemen (und nachdem ich jeden erdenklichen Fehler gemacht hab), hab ich ein paar Gedanken dazu.

Was DDD wirklich ist (und was nicht)

Die Sache ist die: Domain-Driven Design ist kein Framework, keine Library die man mal eben per npm installiert, und definitiv nichts, das man sich an einem Wochenende draufschaffen kann. DDD ist eher eine Philosophie, um Software zu bauen, die tatsächlich zu deinem Business passt, anstatt dein Business in irgendein Datenbankschema zu zwängen, das um 2 Uhr nachts gut klang.

Eric Evans hat den Begriff 2003 geprägt, und die Grundidee ist simpel aber schwierig: Modelliere deine Software basierend auf der tatsächlichen Business-Domäne, nicht auf deinen technischen Vorlieben. Ich weiß, ich weiß, du denkst jetzt "na klar, macht das nicht jeder so?" Glaub mir, die meisten Codebases, die ich gesehen hab, sind nach Datenbanktabellen strukturiert oder nach irgendwelchen REST-Konventionen, die jemand mal gelesen hat, nicht nach der eigentlichen Business-Logik.

Die Hauptkonzepte, die du verstehen musst:

Ubiquitous Language ist wahrscheinlich der am meisten unterschätzte Teil von DDD. Es bedeutet, dass Entwickler und Business-Leute die GLEICHEN Wörter für die GLEICHEN Dinge benutzen. Klingt offensichtlich, oder? Aber ich war in Meetings, wo Entwickler von einem "User Record" sprachen, während das Produktteam von einem "Customer Profile" redete und die Buchhaltung von einer "Billing Entity" sprach. Das sind drei verschiedene mentale Modelle für ein Konzept, und das ist der absolute Albtraum.

// Schlecht: Technischer Jargon, den Business-Leute nicht nutzen
class UserRepository {
  async persistEntity(data: UserDTO): Promise<void> { }
}

// Gut: Sprache, die zu Business-Gesprächen passt
class CustomerRepository {
  async registerNewCustomer(customer: Customer): Promise<void> { }
}

Entities und Value Objects klingen akademisch, sind aber eigentlich ziemlich praktisch. Entities haben eine Identität (ein Customer mit ID 12345 ist immer dieser Kunde, auch wenn er seine E-Mail ändert). Value Objects haben keine Identität, sie sind einfach... Werte (eine EmailAddress von "test@example.com" ist identisch mit jeder anderen Instanz mit diesem String).

// Entity: hat Identität, kann sich über Zeit ändern
class Order {
  constructor(
    private readonly id: OrderId,
    private status: OrderStatus,
    private items: OrderItem[]
  ) {}
  
  ship(): void {
    this.status = OrderStatus.Shipped;
  }
}

// Value Object: unveränderlich, wird über Wert verglichen
class Money {
  constructor(
    readonly amount: number,
    readonly currency: string
  ) {}
  
  add(other: Money): Money {
    if (this.currency !== other.currency) {
      throw new Error("Kann verschiedene Währungen nicht addieren");
    }
    return new Money(this.amount + other.amount, this.currency);
  }
}

Aggregates sind da, wo's interessant (und kompliziert) wird. Ein Aggregate ist im Grunde ein Cluster von Objekten, die du als eine Einheit behandelst. Das klassische Beispiel ist eine Order mit ihren OrderItems. Du änderst OrderItems nicht direkt, sondern gehst über die Order. Das hält deine Daten konsistent und deine Invarianten durchgesetzt.

Repositories sind einfach das Pattern, um Aggregates in den und aus dem Storage zu kriegen. Nichts Ausgefallenes, aber sie abstrahieren weg, ob du Postgres, MongoDB oder Brieftauben benutzt.

Domain Events sind die Art, wie du broadcastest, dass was Wichtiges passiert ist. "OrderShipped", "PaymentReceived", solche Sachen. Ich liebe die, weil verschiedene Teile deines Systems reagieren können, ohne tight gekoppelt zu sein.

Wann DDD tatsächlich hilft (meiner Erfahrung nach)

Ich sag's mal direkt: DDD ist für die meisten Projekte overkill. Wenn du 'ne CRUD-App oder eine simple REST-API baust, nimm einfach Rails oder Django oder was auch immer und mach weiter mit deinem Leben. Ernsthaft.

Aber DDD glänzt, wenn du wirklich komplexe Business-Logik hast. Letztes Jahr hab ich an einem Supply-Chain-System gearbeitet, wo "eine Order ausliefern" bedeutete, Inventory über drei Warehouses zu koordinieren, Shipping basierend auf Verträgen zu berechnen, die sich quartalsweise änderten, und Teillieferungen mit verschiedenen SLAs zu handlen. Da hat uns DDD gerettet, weil wir die tatsächlichen Business-Regeln modellieren konnten, anstatt in if-else-Statements zu ertrinken.

Die Kommunikations-Vorteile sind real. Wenn dein Code die gleiche Sprache benutzt wie deine Business-Stakeholder, kannst du ihnen tatsächlich das Domain-Model zeigen und sie verstehen's. Ich hab buchstäblich Produktmanagern Aggregate-Diagramme gezeigt und die haben auf Business-Logik hingewiesen, die wir verpasst hatten. Versuch das mal mit einem typischen Rails-Controller.

Die Flexibilität ist auch nice. Wenn sich Business-Regeln ändern (und das tun sie immer), modifizierst du Domain-Objekte, die tatsächliche Business-Konzepte repräsentieren, nicht irgendwelchen Spaghetti-Code, wo Business-Logik mit Datenbankabfragen und HTTP-Handling vermischt ist.

Die Teile, vor denen dich keiner warnt

Lass mich dir sagen, was die Bücher nicht genug betonen: DDD ist HART zu lernen. Nicht "lies ein Tutorial" hart, eher "denk fundamental um, wie du Code strukturierst" hart. Ich hab sechs Monate lang das Gefühl gehabt, dass ich's falsch mache, und ehrlich gesagt hab ich's wahrscheinlich mindestens vier dieser Monate auch tatsächlich falsch gemacht.

Es ist am Anfang langsam. Richtig langsam. Du verbringst Stunden in Meetings mit Domain-Experten und versuchst rauszufinden, was ein "Shipment" in eurem Business-Kontext eigentlich bedeutet. Deine Velocity geht in den Keller. Management wird nervös. Ich war da.

Und der Overhead... schau, wenn deine App straightforward ist, werden sich all die Aggregates, Bounded Contexts und Repository-Interfaces wie Zeremonie anfühlen. Weil sie es sind. DDD fügt Abstraktionsschichten hinzu, und Abstraktion hat immer einen Preis.

// Simpler Ansatz
async function createOrder(userId: string, items: Item[]) {
  const order = await db.orders.create({ userId, items });
  return order;
}

// DDD-Ansatz (mehr Schichten, mehr Konzepte)
class OrderService {
  constructor(
    private orderRepository: OrderRepository,
    private customerRepository: CustomerRepository,
    private eventBus: EventBus
  ) {}
  
  async placeOrder(customerId: CustomerId, items: OrderItem[]): Promise<Order> {
    const customer = await this.customerRepository.find(customerId);
    if (!customer) throw new CustomerNotFound();
    
    const order = Order.create(customer, items);
    await this.orderRepository.save(order);
    
    await this.eventBus.publish(new OrderPlaced(order.id));
    return order;
  }
}

Ist die zweite Version besser? Kommt drauf an. Für einen simplen E-Commerce-Shop wahrscheinlich nicht. Für eine komplexe B2B-Plattform mit komischen Business-Regeln auf jeden Fall.

Mein tatsächlicher Rat für die DDD-Implementierung

Erstens brauchst du echte Domain-Experten. Nicht einfach "jemand vom Business-Team", sondern Leute, die die Domäne wirklich tiefgehend kennen. Wenn du nicht regelmäßig Zugang zu diesen Leuten bekommst, wird DDD schmerzhaft.

Investiere ernsthaft in die Ubiquitous Language. Ich mein's ernst. Hab diese awkward Gespräche, wo ihr debattiert, ob was ein "Shipment" oder eine "Delivery" oder ein "Dispatch" ist. Schreib's auf. Pack's in ein Glossar. Referenzier's in Code-Reviews. Das allein wird dir Monate an Verwirrung sparen.

Bounded Contexts sind dein Freund, aber sie sind auch verwirrend am Anfang. Denk an sie wie verschiedene Subsysteme mit eigenen Modellen. In einem E-Commerce-System könnte "Customer" im Shopping-Kontext anders sein als "Customer" im Billing-Kontext. Das ist okay! Die können verschiedene Properties und Verhaltensweisen haben. Versuch nicht, ein riesiges unified Model zu erstellen.

Fang klein an. Pick dir eine Subdomain, implementier DDD dort, schau wie sich's anfühlt. Versuch nicht, deine gesamte Codebase auf einmal zu DDD-ifizieren. Hab ich mal versucht. Lief nicht gut.

Iterier weiter. Dein Domain-Model wird am Anfang falsch sein. Das ist in Ordnung. Der ganze Punkt von DDD ist, dass sich das Model weiterentwickelt, während du die Domäne besser verstehst. Refactor es. Viel.

Zeug, das sich lohnt zu lesen

Wenn du das ernsthaft machen willst, lies Eric Evans' Originalbuch. Ja, es ist dicht. Ja, es nutzt Java-Beispiele von 2003. Lies es trotzdem. Es ist die Grundlage.

Vaughn Vernons "Implementing Domain-Driven Design" ist praktischer und hat bessere Beispiele. Das hat mir ehrlich gesagt besser gefallen für Implementierungsdetails.

Martin Fowlers Website hat ein paar gute Artikel zu DDD-Patterns ohne die ganze Zeremonie. Gut für schnelle Referenz.

Mein Take nach all den Jahren

Domain-Driven Design ist keine Wunderwaffe. Es macht deine Codebase nicht magisch besser. Es lässt Komplexität nicht verschwinden.

Was es MACHEN wird, wenn du an wirklich komplexer Business-Logik arbeitest, ist dir einen strukturierten Weg zu geben, diese Komplexität zu modellieren. Es verbessert, wie du mit Nicht-Entwicklern über das System sprichst. Es macht deinen Code an Business-Konzepten ausgerichtet, anstatt an technischen Details.

Aber ehrlich? Für die meisten Projekte ist es overkill. Und das ist okay. Nutz das richtige Tool für den Job.

Wenn du was baust, wo die Business-Logik der schwierige Teil ist (nicht der Tech-Stack, nicht Skalierung, sondern die eigentlichen Regeln und Workflows), dann ja, gib DDD einen ernsthaften Blick. Wenn du eine typische CRUD-App oder eine dünne API über einer Datenbank baust, spar dir den Kopfschmerz und nutz was Simpleres.

Ich hab gesehen, wie DDD chaotische Codebases in was Wartbares verwandelt hat. Ich hab aber auch gesehen, wie Teams Monate verschwendet haben, um DDD auf Probleme zu zwingen, die es nicht brauchten. Wisse, in welcher Situation du dich befindest.