Türkçe

Olay Kaynaklı Mimari'nin denetim izi uygulamanızı nasıl devrimleştirebileceğini öğrenin. Eşsiz izlenebilirlik, veri bütünlüğü ve sistem dayanıklılığı sunar. Pratik örnekleri keşfedin.

Olay Kaynaklı Mimari: Sağlam ve İzlenebilir Sistemler için Denetim İzleri Uygulamak

Günümüzün karmaşık ve birbirine bağlı dijital ortamında, sağlam ve kapsamlı bir denetim izini sürdürmek esastır. Bu genellikle yasal bir gereklilik olmanın yanı sıra, hata ayıklama, güvenlik analizi ve sisteminizin evrimini anlamak için de kritik öneme sahiptir. Bir uygulamanın durumundaki tüm değişiklikleri bir olay dizisi olarak yakalayan bir mimari desen olan Olay Kaynaklı Mimari, güvenilir, denetlenebilir ve genişletilebilir denetim izlerinin uygulanması için zarif ve güçlü bir çözüm sunar.

Olay Kaynaklı Mimari Nedir?

Geleneksel uygulamalar genellikle verilerin yalnızca mevcut durumunu bir veritabanında saklar. Bu yaklaşım, geçmiş durumları yeniden oluşturmayı veya mevcut duruma yol açan olay dizisini anlamayı zorlaştırır. Olay Kaynaklı Mimari ise bunun aksine, uygulama durumundaki her önemli değişikliği değişmez bir olay olarak yakalamaya odaklanır. Bu olaylar, sistemdeki tüm eylemlerin eksiksiz ve kronolojik bir kaydını oluşturan, yalnızca eklenebilir bir olay deposunda saklanır.

Bunu bir banka hesabı defteri gibi düşünün. Sadece mevcut bakiyeyi kaydetmek yerine, her para yatırma, çekme ve transfer ayrı bir olay olarak kaydedilir. Bu olayları yeniden oynatarak, herhangi bir zamanda hesabın durumunu yeniden oluşturabilirsiniz.

Denetim İzleri İçin Neden Olay Kaynaklı Mimari Kullanılmalı?

Olay Kaynaklı Mimari, denetim izlerinin uygulanması için birkaç etkileyici avantaj sunar:

Denetim İzleri İçin Olay Kaynaklı Mimari Uygulamak: Adım Adım Kılavuz

Denetim izleri için Olay Kaynaklı Mimari uygulamak için pratik bir kılavuz:

1. Anahtar Olayları Tanımlayın

İlk adım, denetim izinizde yakalamak istediğiniz anahtar olayları belirlemektir. Bu olaylar, uygulama durumundaki önemli değişiklikleri temsil etmelidir. Aşağıdaki eylemleri göz önünde bulundurun:

Örnek: Bir e-ticaret platformu için anahtar olaylar "SiparişOluşturuldu", "ÖdemeAlındı", "SiparişGönderildi", "SepeteÜrünEklendi" ve "KullanıcıProfilGüncellendi" olabilir.

2. Olay Yapısını Tanımlayın

Her olay, aşağıdaki bilgileri içeren iyi tanımlanmış bir yapıya sahip olmalıdır:

Örnek: "SiparişOluşturuldu" olayı aşağıdaki yapıya sahip olabilir:

{
  "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. Bir Olay Deposu Seçin

Olay deposu, olayları depolamak için merkezi depodur. Yalnızca eklenebilir bir veritabanı olmalı ve olay dizilerini yazma ve okuma konusunda optimize edilmiş olmalıdır. Birkaç seçenek mevcuttur:

Bir olay deposu seçerken, aşağıdaki gibi faktörleri göz önünde bulundurun:

4. Olay Yayınlama Uygulayın

Bir olay meydana geldiğinde, uygulamanızın onu olay deposuna yayınlaması gerekir. Bu genellikle aşağıdaki adımları içerir:

Örnek (varsayımsal bir EventStoreService kullanarak):

public class OrderService {

  private final EventStoreService eventStoreService;

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

  public void createOrder(Order order, String userId) {
    // ... siparişi oluşturmak için iş mantığı ...

    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) {
    // Bir olay nesnesi oluşturun
    EventRecord eventRecord = new EventRecord(
        UUID.randomUUID(), // eventId
        streamName,  // streamName
        entityId,   // entityId
        event.getClass().getName(), // eventType
        toJson(event),  // eventData
        Instant.now().toString(), // timestamp
        userId  // userId
    );

    // Olayı serileştirin
    String serializedEvent = toJson(eventRecord);

    // Olayı olay deposuna ekleyin (seçilen olay deposuna özel uygulama)
    storeEventInDatabase(serializedEvent);

    // Olayı abonelere yayınlayın (isteğe bağlı)
    publishEventToMessageQueue(serializedEvent);
  }

  // Veritabanı ve mesaj kuyruğu etkileşimi için yer tutucu yöntemler
  private void storeEventInDatabase(String serializedEvent) {
    // Olayı veritabanına depolama uygulaması
    System.out.println("Storing event in database: " + serializedEvent);
  }

  private void publishEventToMessageQueue(String serializedEvent) {
    // Olayı bir mesaj kuyruğuna yayınlama uygulaması
    System.out.println("Publishing event to message queue: " + serializedEvent);
  }

  private String toJson(Object obj) {
    // Olayı JSON'a serileştirme uygulaması
    try {
      ObjectMapper mapper = new ObjectMapper();
      return mapper.writeValueAsString(obj);
    } catch (Exception e) {
      throw new RuntimeException("Error serializing event to JSON", 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;
  }

  // Getters

  @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;
    }

    // Getters for all fields

    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;
    }

    // Getters for all fields

    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. Okuma Modelleri (Projeksiyonlar) Oluşturun

Olay deposu tüm değişikliklerin eksiksiz bir geçmişini sağlarken, okuma işlemleri için doğrudan sorgulamak genellikle verimli değildir. Bunun yerine, belirli sorgu kalıpları için optimize edilmiş okuma modelleri, diğer adıyla projeksiyonlar oluşturabilirsiniz. Bu okuma modelleri olay akışından türetilir ve yeni olaylar yayınlandıkça zaman uyumsuz olarak güncellenir.

Örnek: Belirli bir müşteri için tüm siparişlerin listesini içeren bir okuma modeli veya belirli bir ürünün satış verilerini özetleyen bir okuma modeli oluşturabilirsiniz.

Bir okuma modeli oluşturmak için olay akışına abone olur ve her olayı işlersiniz. Her olay için okuma modelini buna göre güncellersiniz.

Örnek:

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);
    }

    // PaymentReceivedEvent, OrderShippedEvent vb. diğer olay işleyicileri
}

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;
    }
    //Getters
}

6. Olay Deposunu Güvenli Hale Getirin

Olay deposu hassas veriler içerir, bu nedenle onu düzgün bir şekilde güvence altına almak önemlidir. Aşağıdaki güvenlik önlemlerini göz önünde bulundurun:

7. Denetim ve Raporlama Uygulayın

Olay Kaynaklı Mimari'yi uyguladıktan sonra, denetim raporları oluşturmak ve güvenlik analizi yapmak için olay akışını kullanabilirsiniz. Belirli bir kullanıcı, işlem veya varlıkla ilgili tüm olayları bulmak için olay deposunu sorgulayabilirsiniz. Ayrıca, sistemin herhangi bir noktadaki durumunu yeniden oluşturmak için olay akışını kullanabilirsiniz.

Örnek: Belirli bir kullanıcı profilinde zaman içinde yapılan tüm değişiklikleri gösteren bir rapor veya belirli bir kullanıcı tarafından başlatılan tüm işlemleri gösteren bir rapor oluşturabilirsiniz.

Aşağıdaki raporlama yeteneklerini göz önünde bulundurun:

Olay Kaynaklı Mimari'nin Zorlukları

Olay Kaynaklı Mimari birçok fayda sağlarken, bazı zorlukları da beraberinde getirir:

Olay Kaynaklı Mimari İçin En İyi Uygulamalar

Olay Kaynaklı Mimari'nin zorluklarını azaltmak için bu en iyi uygulamaları izleyin:

Olay Kaynaklı Mimari'nin Gerçek Dünya Örnekleri

Olay Kaynaklı Mimari, çeşitli sektörlerde ve uygulamalarda kullanılır, bunlar arasında:

Sonuç

Olay Kaynaklı Mimari, denetim izi uygulamanızı devrimleştirebilecek güçlü bir mimari desendir. Eşsiz izlenebilirlik, veri bütünlüğü ve sistem dayanıklılığı sağlar. Bazı zorlukları beraberinde getirmesine rağmen, özellikle karmaşık ve kritik sistemler için Olay Kaynaklı Mimari'nin faydaları genellikle maliyetlerine değer. Bu kılavuzda belirtilen en iyi uygulamaları izleyerek, Olay Kaynaklı Mimari'yi başarıyla uygulayabilir ve sağlam ve denetlenebilir sistemler oluşturabilirsiniz.

Daha Fazla Okuma