> ## Documentation Index
> Fetch the complete documentation index at: https://docs.diyanet.pro/llms.txt
> Use this file to discover all available pages before exploring further.

# Integration Events

> IntegrationEvent base, IBusEvent, IIntegrationEventHandler, AddMassTransitSubscription ve idempotent consumer

## `IntegrationEvent` base

Cross-service event'lerin temel sınıfı (`src/BuildingBlocks/.../Contracts.Events/IntegrationEvent.cs`). Tüm event POCO'ları bundan türer; `INotification`'dır:

```csharp theme={null}
public class IntegrationEvent : INotification
{
    public IntegrationEvent()
    {
        Id = Guid.CreateVersion7(DateTime.UtcNow);   // time-ordered v7
        CreatedAt = DateTime.UtcNow;
    }

    [JsonConstructor]
    public IntegrationEvent(Guid id, DateTime createdAt)
    {
        Id = id;
        CreatedAt = createdAt;
    }

    [JsonInclude] public Guid Id { get; private init; }
    [JsonInclude] public DateTime CreatedAt { get; private init; }
}
```

<Note>
  `[JsonConstructor]` + `[JsonInclude]` private init'e izin verir: receiver tarafında deserialize edilirken `Id` ve `CreatedAt` **orijinal** değerlerini korur (yeniden üretilmez). Bu, idempotency ve duplicate detection için önemlidir.
</Note>

## `IBusEvent` ve `IBusMessage`

`MediatorExtension` bir domain event'i in-process mı yoksa bus'a mı göndereceğine `IBusEvent` marker'ı ile karar verir:

```csharp theme={null}
// Bus'a yayınlanan tüm event'lerin marker'ı
public interface IBusEvent : INotification, IBusMessage
{
    string GetEventKey();   // topic routing key üretimi için
}

public interface IBusMessage { }   // bus üzerinden taşınan tüm tipler için marker
```

`MassTransitEventBus.PublishAsync` routing key'i şöyle belirler: event `IBusEvent` ise `GetEventKey()`, değilse tip adı. `RoutingKeyPrefix` (default `integration.event`) ile birleştirilir:

```csharp theme={null}
string routingKey = @event is IBusEvent busEvent ? busEvent.GetEventKey() : @event.GetType().Name;
context.SetRoutingKey($"{_options.RoutingKeyPrefix}.{routingKey}");
```

## Örnek: `UserCreatedIntegrationEvent`

```csharp theme={null}
[MessageUrn("urn:event:user-created:v1")]
public class UserCreatedIntegrationEvent : IntegrationEvent
{
    public Guid UserId { get; init; }
    public string? FullName { get; init; }
    public string Phone { get; init; } = string.Empty;
    public string Email { get; init; } = string.Empty;

    public UserCreatedIntegrationEvent() { }
    public UserCreatedIntegrationEvent(Guid userId, string? fullName, string phone, string email)
    {
        UserId = userId; FullName = fullName; Phone = phone; Email = email;
    }
}
```

<Tip>
  `[MessageUrn("urn:event:...:v1")]` cross-service deserialization için stabil bir identity sağlar — tip adı/namespace değişse bile mesajlar eşleşir. Versiyon son ek (`:v1`) şema evrimi içindir.
</Tip>

`CacheInvalidationIntegrationEvent` de aynı kalıbı kullanır (`urn:event:cache-invalidation:v1`); ayrıntı: [Multi-Instance Senkron](/caching/multi-instance).

## Handler kontratı: `IIntegrationEventHandler<T>`

Üretici servis event'i `Contracts.Events`'ten import eder; tüketici servis handler'ı implement eder:

```csharp theme={null}
public interface IIntegrationEventHandler<in TIntegrationEvent> : IIntegrationEventHandler
    where TIntegrationEvent : IntegrationEvent
{
    Task Handle(TIntegrationEvent @event);

    Task IIntegrationEventHandler.Handle(IntegrationEvent @event) => Handle((TIntegrationEvent)@event);
}

// Tipsiz handler — keyed DI registration için.
public interface IIntegrationEventHandler { Task Handle(IntegrationEvent @event); }
```

## Kayıt: `AddMassTransitSubscription<T, TH>`

Event/handler çiftini iki işle birden register eder (`BuildingBlocks.EventBus.MassTransit.RabbitMq/EventBusBuilderExtensions.cs`):

```csharp theme={null}
public static IEventBusBuilder AddMassTransitSubscription<T, TH>(this IEventBusBuilder builder)
    where T : IntegrationEvent
    where TH : class, IIntegrationEventHandler<T>
{
    // 1) Keyed handler kaydı + EventTypes mapping (RabbitMQ binding'i bunu kullanır)
    EventBusBuilderExtensions.AddSubscription<T, TH>(builder);
    // 2) MassTransit consumer'ı DI'a ekle
    builder.Services.AddScoped<IntegrationEventConsumer<T>>();
    return builder;
}
```

Application katmanındaki kullanım (`Application/DependencyInjection.cs`):

```csharp theme={null}
builder.Services.AddMassTransitRabbitMqEventBus(builder.Configuration, x =>
    {
        x.AddEntityFrameworkOutbox<DiyanetCleanArchitectureDbContext>(o => { /* ... */ });
    })
    .AddMassTransitSubscription<CacheInvalidationIntegrationEvent, CacheInvalidationIntegrationEventHandler>();
```

<Note>
  `UserCreatedIntegrationEvent` ve `CitizenCreatedIntegrationEvent` şu an **yayın-only**: yayınlanır ve outbox/RabbitMQ'ya gider, ama bu projede dinleyen consumer henüz yoktur. Cross-service tüketici çıktığında `AddMassTransitSubscription` ile kayıt eklenir.
</Note>

## `IntegrationEventConsumer<T>` — keyed handler çözümü

Tek bir generic consumer tüm tipler için çalışır; gelen mesaj tipine kayıtlı tüm handler'ları keyed DI'dan resolve eder ve sırayla çağırır:

```csharp theme={null}
public class IntegrationEventConsumer<T>(IServiceProvider serviceProvider) : IConsumer<T>
    where T : IntegrationEvent
{
    public async Task Consume(ConsumeContext<T> context)
    {
        var handlers = serviceProvider.GetKeyedServices<IIntegrationEventHandler>(typeof(T));
        foreach (var handler in handlers)
            if (handler is IIntegrationEventHandler<T> integrationHandler)
                await integrationHandler.Handle(context.Message);
    }
}
```

Aynı event'e birden çok handler kaydedilebilir (keyed services), ama RabbitMQ tarafında consumer tipi tektir — binding'ler distinct event tipleri üzerinden kurulur.

## İdempotent handler

Mesaj en az bir kez (at-least-once) teslim edilebilir: retry, redelivery veya broker yeniden teslimi sonucu **aynı event iki kez** gelebilir. Bu yüzden handler'lar idempotent yazılmalı:

* Outbox `DuplicateDetectionWindow` (30 dk) MassTransit Inbox tarafında tekrarları yakalar (bkz. [Outbox Pattern](/events/outbox-pattern)).
* Yine de handler kendi tarafında güvende olmalı: `event.Id` ile işlenmişlik kontrolü, upsert semantiği, veya doğal idempotent işlem (örn. `RemoveByTagLocallyAsync` tekrar çağrılması zararsızdır).

## İlgili

<CardGroup cols={2}>
  <Card title="Outbox Pattern" icon="inbox" href="/events/outbox-pattern">
    Transactional outbox + Inbox duplicate detection.
  </Card>

  <Card title="RabbitMQ Topology" icon="rabbit" href="/events/rabbitmq-topology">
    Exchange, routing key, queue, retry.
  </Card>

  <Card title="Domain Events" icon="bolt" href="/events/domain-events">
    Domain event'ten integration event yayma.
  </Card>

  <Card title="Multi-Instance Senkron" icon="network-wired" href="/caching/multi-instance">
    `CacheInvalidationIntegrationEvent` örneği.
  </Card>
</CardGroup>
