# Uygulamalar Arası İletişim

> Uygulamalar arası olaylar (pub/sub), paylaşılan metafield’lar ve keşif kataloğu.
>
> Kaynak: https://tecof-app-dev.vercel.app/app-interop · Bölüm: Uygulama geliştirme · Son güncelleme: 2018-10-20

Aynı mağazada kurulu uygulamalar birbirleriyle gevşek bağlı biçimde çalışabilir: bir uygulama olay yayınlar, diğeri abone olur; bir uygulama kaynağa veri yazar, diğeri okur. Mağazanın onayı kurulumla örtüktür: iki uygulama da mağazada kuruluysa ve olaylar yapılandırmada bildirildiyse iletişim kurulur.

| Yöntem | Ne zaman | Yön |
| :--- | :--- | :--- |
| Uygulama olayları (pub/sub) | "Bir şey oldu" bildirimi (fatura kesildi, gönderi oluşturuldu) | Yayıncı → aboneler (webhook) |
| Paylaşılan metafield'lar | Kaynağa bağlı kalıcı veri (fatura numarası, takip numarası) | Yazan → okuyan (API) |
| Keşif kataloğu | Mağazada hangi olayların yayınlandığını öğrenmek | API |

---

## Uygulama Olayları

### Adlandırma

Yayıncı olay adını kısa haliyle bildirir (`invoice.created`); Tecof bunu yayıncının slug'ıyla önekler:

```text
app.<yayıncı slug>.<olay adı>      ör. app.isbasi.invoice.created
```

Öneki sunucu ekler; bu sayede bir uygulama başka bir uygulamanın adına olay yayınlayamaz.

### Yayınlama

1. Olayı `tecof.app.json` içinde bildirin:

```json
{
  "appEvents": {
    "publishes": [
      { "name": "invoice.created", "description": { "tr": "Fatura oluşturuldu", "en": "Invoice created" } }
    ]
  }
}
```

2. Olayı yayınlayın:

```ts
await tecof.client(installationId).events.publish("invoice.created", {
  orderId: "6720bb00cc11dd22ee33ff44",
  orderNumber: "TCF-1001",
  invoiceNumber: "ABC2026000000123",
});
```

Ham HTTP: `POST /api/v1/app/events { name, data }`.

| Kural | Değer |
| :--- | :--- |
| `data` boyutu | En fazla 32 KB |
| Bildirilmemiş ad | `400 app-event-undeclared` |
| Kapsam | Gerekmez (uygulamanın kendi verisi) |

### Abone olma

Abone uygulama, dinlediği olayları `appEvents.subscribes` içinde tam adla ya da yayıncı jokeriyle bildirir:

```json
{
  "appEvents": {
    "subscribes": ["app.isbasi.invoice.created", "app.kargo.*"]
  }
}
```

Olay, **aynı mağazada kurulu** ve bu adı (ya da `app.<slug>.*` jokerini) listeleyen uygulamalara webhook olarak `appUrl + webhooks.path` adresine teslim edilir. Abone uygulamada `webhooks.path` tanımlı değilse uygulama olayları teslim edilemez. Teslimat biçimi, imza, yeniden deneme ve idempotency kuralları normal webhook'larla aynıdır ([Webhook'lar](/app-webhooks)); `data` alanı şu biçimdedir:

```json
{
  "id": "evt_91c0d2aa47",
  "event": "app.isbasi.invoice.created",
  "test": false,
  "timestamp": "2026-10-10T09:20:00.000Z",
  "merchantId": "66f0c0ffee0000000000abcd",
  "installationId": "6720aa00bb11cc22dd33ee77",
  "data": {
    "source": { "slug": "isbasi", "clientId": "tcfa_yyyyyyyyyyyyyyyy" },
    "payload": {
      "orderId": "6720bb00cc11dd22ee33ff44",
      "orderNumber": "TCF-1001",
      "invoiceNumber": "ABC2026000000123"
    }
  }
}
```

`installationId` burada **abone** uygulamanın kurulumudur. Yayıncıyı `data.source` ile tanırsınız.

### Keşif

Mağazada kurulu uygulamaların yayınladığı olayları listelemek için:

```ts
const catalog = await tecof.client(installationId).events.catalog();
```

```json
[
  {
    "slug": "isbasi",
    "appName": "Logo İşbaşı",
    "event": "app.isbasi.invoice.created",
    "description": { "tr": "Fatura oluşturuldu", "en": "Invoice created" }
  }
]
```

Ham HTTP: `GET /api/v1/app/events/catalog`. Liste, mağazada **aktif** kurulu platform uygulamalarının `appEvents.publishes` bildirimlerinden üretilir; `event` tam (önekli) addır. Panelinizde "şu uygulama kuruluysa şu entegrasyonu aç" gibi akışlar için kullanabilirsiniz.

---

## Paylaşılan Metafield'lar

`visibility: "shared"` ile yazılan metafield'ları aynı mağazadaki diğer uygulamalar okuyabilir. Okuyan uygulama, kaynağın okuma kapsamına sahip olmalıdır (sipariş için `orders:read`).

```ts
// Yazan: İşbaşı uygulaması
await api.metafields.set({
  ownerType: "order",
  ownerId: orderId,
  key: "invoiceNumber",
  type: "string",
  value: "ABC2026000000123",
  visibility: "shared",
});

// Okuyan: kargo uygulaması
const shared = await kargoApi.metafields.list({ ownerType: "order", ownerId: orderId, namespace: "isbasi" });
const invoiceNumber = shared.find((m: { key: string }) => m.key === "invoiceNumber")?.value;
```

Namespace yazan uygulamanın slug'ıdır; okurken `namespace` süzgeciyle hangi uygulamanın verisini istediğinizi belirtin. `private` metafield'lar başka uygulamalara dönmez.

---

## Örnek: Fatura → Kargo

Senaryo: İşbaşı uygulaması ödenen sipariş için fatura keser ve `invoice.created` yayınlar. Mağazada kurulu bir kargo uygulaması (`kargo`) bu olayı dinler, gönderiyi faturalı olarak oluşturur ve kendi olayı `shipment.created`'i yayınlar.

```mermaid
sequenceDiagram
    participant T as Tecof
    participant I as İşbaşı uygulaması (isbasi)
    participant K as Kargo uygulaması (kargo)

    T->>I: webhook order.paid
    I->>I: Faturayı kes
    I->>T: PUT /api/v1/metafields (invoiceNumber, shared)
    I->>T: POST /api/v1/app/events { name: "invoice.created" }
    T->>K: webhook app.isbasi.invoice.created
    K->>T: GET /api/v1/orders/:id
    K->>K: Gönderiyi oluştur
    K->>T: POST /api/v1/app/events { name: "shipment.created" }
    T->>I: webhook app.kargo.shipment.created
```

**İşbaşı yapılandırması**

```json
{
  "slug": "isbasi",
  "scopes": ["orders:read", "customers:read", "products:read"],
  "webhooks": { "path": "/api/webhooks", "events": ["order.paid"] },
  "appEvents": {
    "publishes": [{ "name": "invoice.created", "description": { "tr": "Fatura oluşturuldu", "en": "Invoice created" } }],
    "subscribes": ["app.kargo.shipment.created"]
  }
}
```

**Kargo uygulaması yapılandırması**

```json
{
  "slug": "kargo",
  "scopes": ["orders:read"],
  "webhooks": { "path": "/api/webhooks", "events": ["order.fulfilled"] },
  "appEvents": {
    "publishes": [{ "name": "shipment.created", "description": { "tr": "Gönderi oluşturuldu", "en": "Shipment created" } }],
    "subscribes": ["app.isbasi.invoice.created"]
  }
}
```

**Kargo uygulamasının işleyicisi**

```ts
// kargo: app/api/webhooks/route.ts
import { createWebhookHandler } from "@tecof/app-sdk";
import { tecof } from "@/lib/tecof";

type InvoiceCreated = {
  source: { slug: string; clientId: string };
  payload: { orderId: string; invoiceNumber: string };
};

export const POST = createWebhookHandler({
  app: tecof,
  handlers: {
    "app.isbasi.invoice.created": async (event) => {
      const { payload } = event.data as InvoiceCreated;
      const api = tecof.client(event.installationId);
      const order = await api.orders.get(payload.orderId);
      // … gönderiyi payload.invoiceNumber ile oluştur
      await api.events.publish("shipment.created", { orderId: order.id, trackingNumber: "TRK000000001" });
    },
  },
});
```

Notlar:

- Yayıncı kurulu değilse olay gelmez; abone uygulama bu entegrasyonu isteğe bağlı bir özellik olarak tasarlamalıdır. Keşif kataloğuyla yayıncının varlığını kontrol edebilirsiniz.
- Olay yükü yalnız bildirimdir; kaydın güncel hâlini API'den okuyun.
- Aynı olay birden fazla gelebilir; `X-Tecof-Event-Id` ile idempotent olun.

---

## Yol Haritası

Aşağıdakiler **gelecek** genişletme noktalarıdır ve henüz mevcut değildir:

- Uygulamalar arası doğrudan istek (capability): bir uygulamanın başka bir uygulamanın sunduğu işlevi doğrudan çağırması.
- Vitrin (tema) uzantıları: uygulamaların mağaza vitrinine bileşen eklemesi.
