Eesti

Õppige, kuidas sündmuste hankimine (Event Sourcing) võib teie audititeede rakendamist revolutsiooniliselt muuta, pakkudes võrratut jälgitavust, andmete terviklikkust ja süsteemi vastupidavust. Avastage praktilisi näiteid ja rakendusstrateegiaid.

Sündmuste hankimine: Audititeede rakendamine robustsete ja jälgitavate süsteemide jaoks

Tänapäeva keerulises ja omavahel seotud digitaalses maastikus on robustse ja kõikehõlmava audititeekonna säilitamine esmatähtis. See ei ole mitte ainult sageli regulatiivne nõue, vaid see on ka kriitilise tähtsusega silumiseks, turvaanalüüsiks ja teie süsteemi arengu mõistmiseks. Sündmuste hankimine (Event Sourcing), arhitektuurimuster, mis salvestab kõik rakenduse oleku muudatused sündmuste jadana, pakub elegantset ja võimsat lahendust audititeede rakendamiseks, mis on usaldusväärsed, auditeeritavad ja laiendatavad.

Mis on sündmuste hankimine?

Traditsioonilised rakendused salvestavad andmebaasis tavaliselt ainult andmete hetkeseisu. See lähenemine muudab keeruliseks mineviku olekute rekonstrueerimise või hetkeseisuni viinud sündmuste jada mõistmise. Sündmuste hankimine seevastu keskendub iga olulise muudatuse salvestamisele rakenduse olekus muutumatu sündmusena. Need sündmused salvestatakse ainult lisamisrežiimis töötavasse sündmuste hoidlasse (event store), moodustades täieliku ja kronoloogilise kirjelduse kõigist süsteemis toimunud tegevustest.

Mõelge sellest kui pangakonto pearaamatust. Selle asemel, et lihtsalt salvestada praegust saldot, salvestatakse iga sissemakse, väljamakse ja ülekanne eraldi sündmusena. Neid sündmusi taasesitades saate rekonstrueerida konto seisu mis tahes ajahetkel.

Miks kasutada sündmuste hankimist audititeede jaoks?

Sündmuste hankimine pakub mitmeid kaalukaid eeliseid audititeede rakendamisel:

Sündmuste hankimise rakendamine audititeede jaoks: Samm-sammuline juhend

Siin on praktiline juhend sündmuste hankimise rakendamiseks audititeede jaoks:

1. Tuvastage võtmesündmused

Esimene samm on tuvastada võtmesündmused, mida soovite oma audititeekonnas salvestada. Need sündmused peaksid esindama olulisi muudatusi rakenduse olekus. Kaaluge selliseid tegevusi nagu:

Näide: E-kaubanduse platvormi puhul võivad võtmesündmused olla "TellimusLoodud," "MakseSaadud," "TellimusTeel," "ToodeLisatudOstukorvi" ja "KasutajaprofiilUuendatud."

2. Määratlege sündmuse struktuur

Igal sündmusel peaks olema hästi määratletud struktuur, mis sisaldab järgmist teavet:

Näide: Sündmusel "TellimusLoodud" võib olla järgmine struktuur:

{
  "eventType": "OrderCreated",
  "eventData": {
    "orderId": "12345",
    "customerId": "67890",
    "orderDate": "2023-10-27T10:00:00Z",
    "totalAmount": 100.00,
    "currency": "USD",
    "shippingAddress": {
      "street": "123 Main St",
      "city": "Anytown",
      "state": "CA",
      "zipCode": "91234",
      "country": "USA"
    }
  },
  "timestamp": "2023-10-27T10:00:00Z",
  "userId": "user123",
  "transactionId": "tx12345",
  "correlationId": "corr123",
  "metadata": {
    "ipAddress": "192.168.1.1",
    "browser": "Chrome",
    "location": {
       "latitude": 34.0522,
       "longitude": -118.2437
    }
  }
}

3. Valige sündmuste hoidla

Sündmuste hoidla (event store) on sündmuste salvestamise keskne repositoorium. See peaks olema ainult lisamisrežiimis töötav andmebaas, mis on optimeeritud sündmuste jadade kirjutamiseks ja lugemiseks. Saadaval on mitu valikut:

Sündmuste hoidla valimisel arvestage selliste teguritega nagu:

4. Rakendage sündmuste avaldamine

Kui sündmus toimub, peab teie rakendus selle sündmuste hoidlasse avaldama. See hõlmab tavaliselt järgmisi samme:

Näide (kasutades hüpoteetilist EventStoreService'i):

public class OrderService {

  private final EventStoreService eventStoreService;

  public OrderService(EventStoreService eventStoreService) {
    this.eventStoreService = eventStoreService;
  }

  public void createOrder(Order order, String userId) {
    // ... tellimuse loomise äriloogika ...

    OrderCreatedEvent event = new OrderCreatedEvent(
        order.getOrderId(),
        order.getCustomerId(),
        order.getOrderDate(),
        order.getTotalAmount(),
        order.getCurrency(),
        order.getShippingAddress()
    );

    eventStoreService.appendEvent("order", order.getOrderId(), event, userId);
  }
}

public class EventStoreService {

  public void appendEvent(String streamName, String entityId, Object event, String userId) {
    // Loo sündmuse objekt
    EventRecord eventRecord = new EventRecord(
        UUID.randomUUID(), // sündmuseId
        streamName,  // vooNimi
        entityId,   // olemId
        event.getClass().getName(), // sündmuseTüüp
        toJson(event),  // sündmuseAndmed
        Instant.now().toString(), // ajatempel
        userId  // kasutajaId
    );

    // Serialiseeri sündmus
    String serializedEvent = toJson(eventRecord);

    // Lisa sündmus sündmuste hoidlasse (rakendamine on spetsiifiline valitud sündmuste hoidlale)
    storeEventInDatabase(serializedEvent);

    // Avalda sündmus tellijatele (valikuline)
    publishEventToMessageQueue(serializedEvent);
  }

  // Kohatäite meetodid andmebaasi ja sõnumijärjekorraga suhtlemiseks
  private void storeEventInDatabase(String serializedEvent) {
    // Rakendus sündmuse andmebaasi salvestamiseks
    System.out.println("Salvestan sündmuse andmebaasi: " + serializedEvent);
  }

  private void publishEventToMessageQueue(String serializedEvent) {
    // Rakendus sündmuse sõnumijärjekorda avaldamiseks
    System.out.println("Avaldan sündmuse sõnumijärjekorda: " + serializedEvent);
  }

  private String toJson(Object obj) {
    // Rakendus sündmuse JSON-vormingusse serialiseerimiseks
    try {
      ObjectMapper mapper = new ObjectMapper();
      return mapper.writeValueAsString(obj);
    } catch (Exception e) {
      throw new RuntimeException("Viga sündmuse JSON-vormingusse serialiseerimisel", e);
    }
  }
}


class EventRecord {
  private final UUID eventId;
  private final String streamName;
  private final String entityId;
  private final String eventType;
  private final String eventData;
  private final String timestamp;
  private final String userId;

  public EventRecord(UUID eventId, String streamName, String entityId, String eventType, String eventData, String timestamp, String userId) {
    this.eventId = eventId;
    this.streamName = streamName;
    this.entityId = entityId;
    this.eventType = eventType;
    this.eventData = eventData;
    this.timestamp = timestamp;
    this.userId = userId;
  }

  // Getterid

  @Override
  public String toString() {
    return "EventRecord{" +
        "eventId=" + eventId +
        ", streamName='" + streamName + '\'' +
        ", entityId='" + entityId + '\'' +
        ", eventType='" + eventType + '\'' +
        ", eventData='" + eventData + '\'' +
        ", timestamp='" + timestamp + '\'' +
        ", userId='" + userId + '\'' +
        '}'_;
  }
}

class OrderCreatedEvent {
    private final String orderId;
    private final String customerId;
    private final String orderDate;
    private final double totalAmount;
    private final String currency;
    private final String shippingAddress;

    public OrderCreatedEvent(String orderId, String customerId, String orderDate, double totalAmount, String currency, String shippingAddress) {
        this.orderId = orderId;
        this.customerId = customerId;
        this.orderDate = orderDate;
        this.totalAmount = totalAmount;
        this.currency = currency;
        this.shippingAddress = shippingAddress;
    }

    // Kõikide väljade getterid

    public String getOrderId() { return orderId; }
    public String getCustomerId() { return customerId; }
    public String getOrderDate() { return orderDate; }
    public double getTotalAmount() { return totalAmount; }
    public String getCurrency() { return currency; }
    public String getShippingAddress() { return shippingAddress; }

    @Override
    public String toString() {
        return "OrderCreatedEvent{" +
                "orderId='" + orderId + '\'' +
                ", customerId='" + customerId + '\'' +
                ", orderDate='" + orderDate + '\'' +
                ", totalAmount=" + totalAmount +
                ", currency='" + currency + '\'' +
                ", shippingAddress='" + shippingAddress + '\'' +
                '}'_;
    }
}

class Order {
  private final String orderId;
  private final String customerId;
  private final String orderDate;
  private final double totalAmount;
  private final String currency;
  private final String shippingAddress;

  public Order(String orderId, String customerId, String orderDate, double totalAmount, String currency, String shippingAddress) {
        this.orderId = orderId;
        this.customerId = customerId;
        this.orderDate = orderDate;
        this.totalAmount = totalAmount;
        this.currency = currency;
        this.shippingAddress = shippingAddress;
    }

    // Kõikide väljade getterid

    public String getOrderId() { return orderId; }
    public String getCustomerId() { return customerId; }
    public String getOrderDate() { return orderDate; }
    public double getTotalAmount() { return totalAmount; }
    public String getCurrency() { return currency; }
    public String getShippingAddress() { return shippingAddress; }

    @Override
    public String toString() {
        return "Order{" +
                "orderId='" + orderId + '\'' +
                ", customerId='" + customerId + '\'' +
                ", orderDate='" + orderDate + '\'' +
                ", totalAmount=" + totalAmount +
                ", currency='" + currency + '\'' +
                ", shippingAddress='" + shippingAddress + '\'' +
                '}'_;
    }
}

5. Ehitage lugemismudelid (projektsioonid)

Kuigi sündmuste hoidla pakub täielikku ajalugu kõigist muudatustest, ei ole selle otse päringute tegemine lugemisoperatsioonideks sageli tõhus. Selle asemel saate ehitada lugemismudeleid, mida tuntakse ka projektsioonidena, mis on optimeeritud konkreetsete päringumustrite jaoks. Need lugemismudelid tuletatakse sündmuste voost ja neid uuendatakse asünkroonselt uute sündmuste avaldamisel.

Näide: Võite luua lugemismudeli, mis sisaldab nimekirja kõigist konkreetse kliendi tellimustest, või lugemismudeli, mis võtab kokku konkreetse toote müügiandmed.

Lugemismudeli ehitamiseks tellite sündmuste voo ja töötlete iga sündmust. Iga sündmuse puhul uuendate lugemismudelit vastavalt.

Näide:

public class OrderSummaryReadModelUpdater {

    private final OrderSummaryRepository orderSummaryRepository;

    public OrderSummaryReadModelUpdater(OrderSummaryRepository orderSummaryRepository) {
        this.orderSummaryRepository = orderSummaryRepository;
    }

    public void handle(OrderCreatedEvent event) {
        OrderSummary orderSummary = new OrderSummary(
                event.getOrderId(),
                event.getCustomerId(),
                event.getOrderDate(),
                event.getTotalAmount(),
                event.getCurrency()
        );

        orderSummaryRepository.save(orderSummary);
    }

    // Teised sündmuste käsitlejad sündmustele PaymentReceivedEvent, OrderShippedEvent jne.
}

interface OrderSummaryRepository {
    void save(OrderSummary orderSummary);
}

class OrderSummary {
    private final String orderId;
    private final String customerId;
    private final String orderDate;
    private final double totalAmount;
    private final String currency;

    public OrderSummary(String orderId, String customerId, String orderDate, double totalAmount, String currency) {
        this.orderId = orderId;
        this.customerId = customerId;
        this.orderDate = orderDate;
        this.totalAmount = totalAmount;
        this.currency = currency;
    }
    //Getterid
}

6. Turvake sündmuste hoidla

Sündmuste hoidla sisaldab tundlikke andmeid, seega on ülioluline seda korralikult turvata. Kaaluge järgmisi turvameetmeid:

7. Rakendage auditeerimine ja aruandlus

Kui olete sündmuste hankimise rakendanud, saate kasutada sündmuste voogu auditiraportite genereerimiseks ja turvaanalüüsi teostamiseks. Saate pärida sündmuste hoidlast, et leida kõik sündmused, mis on seotud konkreetse kasutaja, tehingu või olemiga. Samuti saate kasutada sündmuste voogu süsteemi oleku rekonstrueerimiseks mis tahes ajahetkel.

Näide: Võite genereerida aruande, mis näitab kõiki teatud ajavahemiku jooksul tehtud muudatusi konkreetses kasutajaprofiilis, või aruande, mis näitab kõiki teatud kasutaja algatatud tehinguid.

Kaaluge järgmisi aruandlusvõimalusi:

Sündmuste hankimise väljakutsed

Kuigi sündmuste hankimine pakub palju eeliseid, esitab see ka mõningaid väljakutseid:

Sündmuste hankimise parimad praktikad

Sündmuste hankimise väljakutsete leevendamiseks järgige neid parimaid praktikaid:

Reaalse maailma näited sündmuste hankimisest

Sündmuste hankimist kasutatakse mitmesugustes tööstusharudes ja rakendustes, sealhulgas:

Kokkuvõte

Sündmuste hankimine on võimas arhitektuurimuster, mis võib teie audititeekonna rakendamist revolutsiooniliselt muuta. See pakub võrratut jälgitavust, andmete terviklikkust ja süsteemi vastupidavust. Kuigi see esitab mõningaid väljakutseid, kaaluvad sündmuste hankimise eelised sageli üles kulud, eriti keeruliste ja kriitiliste süsteemide puhul. Järgides selles juhendis kirjeldatud parimaid praktikaid, saate edukalt rakendada sündmuste hankimist ja ehitada robustseid ja auditeeritavaid süsteeme.

Lisalugemist