Ren arkitektur og designmønstre i praksis · Lektion

Hændelsesdrevet Clean Architecture

Integrér hændelsesdrevne mønstre i Clean Architecture, og brug domænehændelser til at øge afkobling og skalerbarhed.

Lektion 2 af 412 trin

Hændelsesdrevet Clean Architecture er en gratis Ren arkitektur og designmønstre i praksis-lektion på CoddyKit. Dette er lektion 2 af 4. Du kan læse hele lektionen gratis nedenfor — og derefter øve dig praktisk i browseren med en indbygget kodeeditor og en AI-vejleder, der er tilgængelig døgnet rundt. Den er en del af læringsforløbet i Ren arkitektur og designmønstre i praksis, og dine fremskridt synkroniseres på tværs af nettet og CoddyKit-appen. Ren arkitektur og designmønstre i praksis-kurset indeholder 4 lektioner i alt.

Introduktion til hændelsesdrevet Clean Architecture

Velkommen til hændelsesdrevet Clean Architecture! I komplekse systemer har dele ofte brug for at reagere på hændelser andre steder i systemet uden at være tæt koblet til dem.

Denne lektion undersøger, hvordan du integrerer hændelsesdrevne mønstre i din Clean Architecture ved hjælp af domænehændelser for at styrke afkobling og skalerbarhed, samtidig med at afhængighedsreglen overholdes strengt.

Hvad er domænehændelser?

En domænehændelse er noget, der er sket i domænet, og som domæneeksperter finder vigtigt. Det er et uforanderligt faktum, en registrering af en hændelse, f.eks. »Ordre afgivet« eller »Bruger registreret«.

  • De repræsenterer en ændring i domænets tilstand.
  • De navngives typisk i datid, f.eks. OrderPlaced.
  • De bør være simple datastrukturer, der kun indeholder relevante oplysninger om hændelsen.

Hændelser og afhængighedsreglen

I Clean Architecture siger afhængighedsreglen, at afhængigheder skal bevæge sig indad. Domænehændelser passer perfekt ind:

  • Entiteter kan udløse hændelser, men ved ikke, hvem der håndterer dem.
  • Anvendelsestilfælde orkestrerer entiteter og kan publicere hændelser efter en handling.
  • Grænsefladeadaptere, f.eks. præsentere og gateways til eksterne tjenester, kan abonnere på og håndtere hændelser fra indre lag.

Det opretholder en streng adskillelse, fordi de indre lag ikke kender detaljer om de ydre lag.

Implementering af en domænehændelse

En domænehændelse er grundlæggende et dataoverførselsobjekt (DTO), der indeholder oplysninger om, hvad der skete. Det er god praksis at have en fælles grænseflade for alle domænehændelser.

Her er en enkel Java-grænseflade:

public interface DomainEvent {
  long occurredOn();
}

Kode: Konkret domænehændelse

Lad os oprette en specifik domænehændelse: OrderPlacedEvent. Den indeholder orderId og tidsstemplet for, hvornår hændelsen fandt sted.

Prøv at køre denne simple definition:

public class OrderPlacedEvent implements DomainEvent {
  private final String orderId;
  private final long occurredOn;

  public OrderPlacedEvent(String orderId) {
    this.orderId = orderId;
    this.occurredOn = System.currentTimeMillis();
  }

  public String getOrderId() {
    return orderId;
  }

  @Override
  public long occurredOn() {
    return occurredOn;
  }

  public static void main(String[] args) {
    OrderPlacedEvent event = new OrderPlacedEvent("ORD-123");
    System.out.println("Order event for: " + event.getOrderId());
  }
}

Hændelsesudgiveren

For at publicere hændelser har vi brug for en hændelsesudgiver, også kendt som en hændelsesdistributør eller hændelsesbus. Denne komponent modtager en domænehændelse og videresender den til alle registrerede håndterere.

Den fungerer som en mellemmand, der afkobler hændelseskilden fra dens forbrugere. Anvendelsestilfældet afhænger af denne udgivers grænseflade, ikke af specifikke håndterere.

Kode: Grænseflade for hændelsesudgiver

Her er en grænseflade til vores EventPublisher. Vi skal også bruge en måde, hvorpå håndterere kan registrere sig selv.

public interface EventPublisher {
  void publish(DomainEvent event);
  <T extends DomainEvent> void subscribe(Class<T> eventType, EventHandler<T> handler);
}

Kode: Hændelsesudgiver i hukommelsen

En enkel implementering i hukommelsen til demonstrationsformål. I en virkelig applikation kunne den bruge en meddelelseskø, f.eks. Kafka eller RabbitMQ, til lagring og distribution.

Kør dette for at se den grundlæggende udgiver i funktion:

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public interface DomainEvent { long occurredOn(); }
public interface EventHandler<T extends DomainEvent> { void handle(T event); }

public class OrderPlacedEvent implements DomainEvent {
  private final String orderId; private final long occurredOn;
  public OrderPlacedEvent(String orderId) {
    this.orderId = orderId; this.occurredOn = System.currentTimeMillis();
  }
  public String getOrderId() { return orderId; }
  @Override public long occurredOn() { return occurredOn; }
}

public class SimpleEventPublisher implements EventPublisher {
  private final Map<Class<? extends DomainEvent>, List<EventHandler<?>>> subscribers = new HashMap<>();

  @Override
  public void publish(DomainEvent event) {
    List<EventHandler<?>> handlers = subscribers.get(event.getClass());
    if (handlers != null) {
      for (EventHandler handler : handlers) {
        // Unchecked cast is safe due to type checking during subscription
        ((EventHandler<DomainEvent>) handler).handle(event);
      }
    }
  }

  @Override
  public <T extends DomainEvent> void subscribe(Class<T> eventType, EventHandler<T> handler) {
    subscribers.computeIfAbsent(eventType, k -> new ArrayList<>()).add(handler);
  }

  public static void main(String[] args) {
    SimpleEventPublisher publisher = new SimpleEventPublisher();
    publisher.subscribe(OrderPlacedEvent.class, event -> {
      System.out.println("Handler 1: Order " + event.getOrderId() + " placed!");
    });
    publisher.subscribe(OrderPlacedEvent.class, event -> {
      System.out.println("Handler 2: Notifying ops for order " + event.getOrderId());
    });

    OrderPlacedEvent event = new OrderPlacedEvent("DEMO-456");
    publisher.publish(event);
  }
}

Integration af anvendelsestilfælde og håndterere

Lad os nu se, hvordan et anvendelsestilfælde publicerer en hændelse, og hvordan en hændelseshåndterer, der f.eks. befinder sig i laget med grænsefladeadaptere, reagerer på den. Anvendelsestilfældet forbliver afkoblet fra håndtereren.

Dette design gør det muligt at tilføje nye håndterere uden at ændre anvendelsestilfældet.

Kode: Fuldt hændelsesdrevet forløb

Dette eksempel viser et PlaceOrderUseCase, der publicerer en OrderPlacedEvent, som derefter håndteres af en OrderEmailNotifier.

Bemærk, hvordan PlaceOrderUseCase kun afhænger af EventPublisher og ikke af den specifikke underretter.

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

// Domain Layer Interfaces & Classes
interface DomainEvent { long occurredOn(); }
class OrderPlacedEvent implements DomainEvent {
  private final String orderId; private final long occurredOn;
  public OrderPlacedEvent(String orderId) {
    this.orderId = orderId; this.occurredOn = System.currentTimeMillis();
  }
  public String getOrderId() { return orderId; }
  @Override public long occurredOn() { return occurredOn; }
}

// Application Layer Interfaces & Classes
interface EventPublisher {
  void publish(DomainEvent event);
  <T extends DomainEvent> void subscribe(Class<T> eventType, EventHandler<T> handler);
}
interface EventHandler<T extends DomainEvent> { void handle(T event); }

// Use Case (Application Layer)
class PlaceOrderUseCase {
  private final EventPublisher eventPublisher;

  public PlaceOrderUseCase(EventPublisher eventPublisher) {
    this.eventPublisher = eventPublisher;
  }

  public void execute(String orderDetails) {
    // Simulate order creation logic
    String newOrderId = "ORD-" + System.nanoTime();
    System.out.println("Order " + newOrderId + " created with details: " + orderDetails);

    // Publish domain event
    eventPublisher.publish(new OrderPlacedEvent(newOrderId));
  }
}

// Infrastructure/Interface Adapters Layer Implementation
class SimpleEventPublisher implements EventPublisher {
  private final Map<Class<? extends DomainEvent>, List<EventHandler<?>>> subscribers = new HashMap<>();

  @Override
  public void publish(DomainEvent event) {
    List<EventHandler<?>> handlers = subscribers.get(event.getClass());
    if (handlers != null) {
      for (EventHandler handler : handlers) {
        ((EventHandler<DomainEvent>) handler).handle(event);
      }
    }
  }

  @Override
  public <T extends DomainEvent> void subscribe(Class<T> eventType, EventHandler<T> handler) {
    subscribers.computeIfAbsent(eventType, k -> new ArrayList<>()).add(handler);
  }
}

class OrderEmailNotifier implements EventHandler<OrderPlacedEvent> {
  @Override
  public void handle(OrderPlacedEvent event) {
    System.out.println("Email Notifier: Sending email for order " + event.getOrderId());
  }
}

class InventoryUpdater implements EventHandler<OrderPlacedEvent> {
  @Override
  public void handle(OrderPlacedEvent event) {
    System.out.println("Inventory Updater: Updating inventory for order " + event.getOrderId());
  }
}

public class Main {
  public static void main(String[] args) {
    SimpleEventPublisher publisher = new SimpleEventPublisher();

    // Register handlers
    publisher.subscribe(OrderPlacedEvent.class, new OrderEmailNotifier());
    publisher.subscribe(OrderPlacedEvent.class, new InventoryUpdater());

    // Create use case with publisher dependency
    PlaceOrderUseCase placeOrderUseCase = new PlaceOrderUseCase(publisher);

    // Execute use case, which publishes the event
    placeOrderUseCase.execute("Laptop, Quantity: 1");
    placeOrderUseCase.execute("Keyboard, Quantity: 2");
  }
}

Hurtigt tjek: Domænehændelser

Hvad er den primære fordel ved at lade anvendelsestilfælde publicere hændelser i stedet for at kalde andre tjenester direkte, når du integrerer domænehændelser i Clean Architecture?

Opsummering: Hændelsesdrevet Clean Architecture

I denne lektion undersøgte vi hændelsesdrevet Clean Architecture. Du lærte:

  • Domænehændelser er uforanderlige fakta, der repræsenterer betydningsfulde hændelser.
  • De muliggør afkobling, så anvendelsestilfælde kan publicere hændelser uden at kende deres abonnenter.
  • Hændelsesudgivere fungerer som mellemled ved videresendelse af hændelser til hændelseshåndterere.
  • Dette mønster understøtter skalerbarhed og udvidelsesmuligheder, så systemer bliver lettere at videreudvikle, samtidig med at afhængighedsreglen overholdes.

Ved at udnytte domænehændelser kan din Clean Architecture blive endnu mere robust og tilpasningsdygtig.

Gratis at komme i gang

Lær Ren arkitektur og designmønstre i praksis med en AI-underviser — gratis

Skriv og kør rigtig kode i din browser, få øjeblikkelig hjælp fra en AI-underviser døgnet rundt, og fortsæt, hvor du slap, på web eller i appen.

Kurser
12
Lektioner
48

Ofte stillede spørgsmål

Er lektionen “Hændelsesdrevet Clean Architecture” gratis?

Ja — hele teksten til “Hændelsesdrevet Clean Architecture” kan læses gratis her på nettet. Hvis du vil øve dig interaktivt med en indbygget kodeeditor og en AI-vejleder døgnet rundt og få adgang til resten af Ren arkitektur og designmønstre i praksis-kurset, skal du opgradere til CoddyKit PRO. Ren arkitektur og designmønstre i praksis-kurset indeholder 4 lektioner i alt.

Hvad lærer jeg i “Hændelsesdrevet Clean Architecture”?

Integrér hændelsesdrevne mønstre i Clean Architecture, og brug domænehændelser til at øge afkobling og skalerbarhed. Du øver dig i Ren arkitektur og designmønstre i praksis med praktisk kode, som du kører direkte i browseren, og en AI-vejleder døgnet rundt besvarer dine spørgsmål, mens du arbejder dig gennem lektionen.

Skal jeg have erfaring for at begynde på Ren arkitektur og designmønstre i praksis?

Der kræves ingen tidligere erfaring. Ren arkitektur og designmønstre i praksis på CoddyKit er tilrettelagt for både begyndere og øvede, så du kan starte her eller fra begyndelsen og lære i dit eget tempo. Dette er lektion 2 af 4.

Hvor lang tid tager lektionen “Hændelsesdrevet Clean Architecture”?

De fleste CoddyKit-lektioner tager cirka 5–10 minutter. Hver lektion er kort og interaktiv, så du gør løbende fremskridt og kan fortsætte, hvor du slap – på både web og app.

Kan jeg skrive og køre kode i denne Ren arkitektur og designmønstre i praksis-lektion?

Ja. Alle Ren arkitektur og designmønstre i praksis-lektioner har en indbygget kodeeditor, så du kan skrive og køre rigtig kode direkte i din browser og få øjeblikkelig feedback fra AI – uden lokal opsætning.

Alle lektioner i dette kursus

  1. Håndtering af tværgående forhold
  2. Hændelsesdrevet Clean Architecture
  3. Clean Architecture i mikrotjenester
  4. CQRS i Clean Architecture
← Tilbage til Ren arkitektur og designmønstre i praksis