# Tek mimari kuralı, üç dil: archlint artık Go + TypeScript + Python

> Polyglot repoda 'domain altyapıyı import etmesin' kuralı her dilde aynı; tek architecture.json artık üç dilin sınırlarını CI'da zorluyor.

- Yayın: 2026-06-18
- Kategori: Günlük
- Etiketler: Go, TypeScript, Python
- Okuma süresi: 2 dk okuma
- Kaynak: https://www.muhammetsafak.com.tr/blog/archlint-tek-kural-uc-dil/
- Dil: tr-TR
- Yazar: Muhammet Şafak

---
[archlint'i ilk yazdığımda](/blog/archlint-mimari-sinirlari-ci-da-zorlamak/) sadece Go biliyordu. Ama tipik bir sistem tek dil değil: Go API, TypeScript frontend, Python worker'ları. Üçünde de aynı mimari niyet var — "domain katmanı altyapıyı import etmesin" — ama o kuralı üç dilde birden zorlayan tek bir araç yoktu. Şimdi archlint üçünü de yönetiyor, **tek bir `architecture.json` ile**.

## Sınır dile bağlı değil; sözdizimi bağlı

Katman düzeyinde kural her dilde aynıdır: `domain → db` yasak. Diller arasında değişen şey kuralın kendisi değil, **import'un nasıl yazıldığı ve hangi dosyaya çözüldüğü**:

- **Go:** mutlak paket yolu — `github.com/acme/app/internal/db`. Çözüm: modül-prefix'ini sıyır → `internal/db`.
- **TypeScript:** göreli (`../db/repo`) ya da alias (`@/db/repo`). Çözüm: göreliyi dosyanın konumuna, alias'ı `architecture.json`'daki `aliases` eşlemesine göre çöz.
- **Python:** noktalı + göreli — `from ..db.repo import find_order`. Çözüm: göreli noktaları dosyanın paketine göre yukarı çık, sonra modülü yola çevir.

archlint hepsini **tek bir şeye** indirger: repo-göreli bir yol → bir katman. O noktadan sonra motor aynı: "bu dosya hangi katmanda, bu import hangi katmana gidiyor, kural izin veriyor mu?"

## Tek config, karışık repo

İşin güzel yanı: aynı dosya üç dilli bir monorepo'yu birden yönetebiliyor. Katman adları ortak, yollar dile özel:

```json
{
  "module": "github.com/acme/app",
  "aliases": { "@/": "web/src/" },
  "layers": {
    "domain": ["internal/domain", "web/src/domain", "workers/app/domain"],
    "infra":  ["internal/infra", "web/src/infra", "workers/app/infra"]
  },
  "rules": { "domain": [], "infra": ["domain"] }
}
```

`domain` katmanı üç serviste de var (Go `internal/`, TS `web/src/`, Python `workers/app/`) ve hiçbirinde altyapıyı import edemez. Tek kural, üç dil, tek CI kapısı.

## Nasıl çalışıyor: dil-eklenebilir mimari

İçeride bir `Language` arayüzü var: her dil yalnızca iki şeyi bilir — bir dosyanın import'larını **çıkarmak** ve bir import'u **repo-göreli yola çözmek**. Linter'ın geri kalanı dil-agnostik. Yeni bir dil eklemek = yeni bir `Language` implementasyonu.

Disiplin aynen korundu: tree-sitter (CGo, ağır bağımlılık) yerine, Go'yu stdlib `go/parser` ile (kesin), TS ve Python'ı **regex tarayıcılarla** okuyorum. Dürüst sınır: bunlar tam parser değil — yorum ya da string içindeki import-benzeri bir satır false-positive olabilir. README'de açıkça yazıyor.

## Denemek için: üç örnek

Her biri kasıtlı bir ihlal içeriyor:

```sh
archlint check examples/sample      # Go     → internal/domain/bad.go
archlint check examples/ts-sample   # TS     → src/domain/bad.ts   ("@/" alias çözülür)
archlint check examples/py-sample   # Python → app/domain/bad.py   ("..db.repo" çözülür)
```

Üçü de aynı çıktıyı verir: `domain → db is not allowed`, exit 1, CI kırmızı.

---

Bunun neden bir wiki sayfasından daha iyi olduğunu [sade.dev'de ayrı yazdım](https://sade.dev/tr/journal/mimari-kararlar-curur): yazılı bir mimari karar, onu her commit'te kontrol eden bir şey olmadan çürür. Şimdi o "şey" üç dilde birden çalışıyor. Sıradaki sınır hangi dil olsun istersen — yaz, sıraya koyayım.

- [github.com/muhammetsafak/archlint](https://github.com/muhammetsafak/archlint)
