İçeriğe geç
academia.sh

Ders 14 / 20

Yapılandırma Yönetimi

Ayarların kaynak koddan ayrılması, ortam değişkenlerinin doğrulanması, öncelik sırası, gizli değerlerin sıradan ayarlardan ayrılması ve yapılandırmanın değiştirilemez tutulması.

İçindekiler

Önceki derste yazılan araçta ve HTTP Sunucusu dersindeki hizmette sabit yazılmış değerler kaldı: bağlantı noktası 8791, veri dosyasının adı, gövde boyutu sınırı. Bunlar geliştirme makinesinde doğru, başka bir ortamda yanlıştır.

Bu dersin sorusu şudur: bir ayarın değeri nereden gelmeli ve yanlış geldiğinde ne olmalı? Yanıtın iki yarısı vardır — kaynakların sırası ve doğrulamanın zamanı.

Ayar Kaynak Koddan Ayrılır

Ortama göre değişen her değer koddan çıkarılır. Ölçüt basittir: aynı yapı, değiştirilmeden başka bir ortamda çalışabilmelidir. Yapıyı yeniden üretmeden ortamı değiştirebilmek, aynı kodun sınandığı hâliyle çalıştırıldığını güvence altına alır.

Ayarların taşıyıcısı ortam değişkenleridir. Süreç Nesnesi dersinde görüldüğü gibi bunlar süreç başlatılırken devredilir ve process.env üzerinden okunur; değerleri her zaman dizgidir.

Okuma ve doğrulama tek bir modülde toplanır. Programın geri kalanı process.env nesnesine hiç dokunmaz:

// yapilandirma.mjs
function gerekli(ad) {
  const deger = process.env[ad];
  if (deger === undefined || deger === '') {
    throw new Error(`zorunlu ortam degiskeni eksik: ${ad}`);
  }
  return deger;
}

function tamsayi(ad, varsayilan) {
  const ham = process.env[ad];
  if (ham === undefined) return varsayilan;
  const sayi = Number(ham);
  if (!Number.isInteger(sayi) || sayi <= 0) {
    throw new Error(`${ad} pozitif tam sayi olmali, gelen: ${JSON.stringify(ham)}`);
  }
  return sayi;
}

export function yapilandirmayiOku() {
  return Object.freeze({
    dinlemeAdresi: process.env.OLCUM_ADRES ?? '127.0.0.1',
    dinlemeNoktasi: tamsayi('OLCUM_PORT', 8791),
    veriDizini: process.env.OLCUM_VERI ?? './veri',
    kutukDuzeyi: process.env.OLCUM_KUTUK_DUZEYI ?? 'bilgi',
    yonetimAnahtari: gerekli('OLCUM_YONETIM_ANAHTARI'),
  });
}

// Gizli degerler asla dogrudan yazilmaz
export function yazilabilirGorunum(y) {
  return { ...y, yonetimAnahtari: `***${y.yonetimAnahtari.slice(-2)}` };
}
// yap-dene.mjs
import { yapilandirmayiOku, yazilabilirGorunum } from './yapilandirma.mjs';
try {
  console.log(yazilabilirGorunum(yapilandirmayiOku()));
} catch (hata) {
  console.error('yapilandirma hatasi:', hata.message);
  process.exitCode = 78;
}

Değişken adlarının ortak bir önek taşıması bilinçlidir: süreç, kendisini başlatan kabuktan onlarca ilgisiz değişken devralır ve önek, hangilerinin bu programa ait olduğunu belirsizlikten çıkarır.

Doğrulama Başlangıçta Yapılır

Yanlış bir ayarın en kötü ortaya çıkma anı, hizmet saatlerce çalıştıktan sonra ilk kez o kod yoluna girildiği andır. Yapılandırma açılışta, herhangi bir istek kabul edilmeden önce okunup doğrulanır; geçersizse süreç açılmaz.

node yap-dene.mjs; echo "cikis kodu: $?"
yapilandirma hatasi: zorunlu ortam degiskeni eksik: OLCUM_YONETIM_ANAHTARI
cikis kodu: 78
OLCUM_YONETIM_ANAHTARI=abc123XYZ OLCUM_PORT=sekiz node yap-dene.mjs; echo "cikis kodu: $?"
yapilandirma hatasi: OLCUM_PORT pozitif tam sayi olmali, gelen: "sekiz"
cikis kodu: 78

İki hata iletisi de eksik olanı ve beklenen biçimi söylüyor. Yapılandırma hataları insanlar tarafından okunacak hatalardır; “geçersiz yapılandırma” gibi bir ileti, düzeltmeyi arayan kişiye hiçbir şey vermez.

Doğru değerlerle açılış:

OLCUM_YONETIM_ANAHTARI=abc123XYZ OLCUM_PORT=9100 node yap-dene.mjs
{
  dinlemeAdresi: '127.0.0.1',
  dinlemeNoktasi: 9100,
  veriDizini: './veri',
  kutukDuzeyi: 'bilgi',
  yonetimAnahtari: '***YZ'
}

Dizgi olarak gelen 9100 değeri sayıya çevrildi ve tam sayı olduğu doğrulandı. Dönüşümü tek yerde yapmak, programın geri kalanının tür sorusunu hiç sormamasını sağlar.

Öncelik Sırası

Ayar birden çok kaynaktan gelebilir. Sıra baştan belirlenir ve belgelenir; en yaygın düzen, dardan genişe doğru şudur:

  1. Komut satırı argümanı — tek bir çalıştırmayı etkiler.
  2. Ortam değişkeni — o süreci etkiler.
  3. Ortam dosyası — bir dizindeki bütün çalıştırmaları etkiler.
  4. Koddaki varsayılan — hiçbiri verilmediğinde geçerlidir.

Yukarıdaki sırada üstteki alttakini ezer. Çalışma zamanı, üçüncü basamağı doğrudan destekler: belirli bir sürümden itibaren bir bayrakla verilen dosyadaki AD=deger satırları process.env içine yüklenir.

printf 'OLCUM_YONETIM_ANAHTARI=dosyadan99\nOLCUM_PORT=9200\n' > .env
node --env-file=.env yap-dene.mjs
{
  dinlemeAdresi: '127.0.0.1',
  dinlemeNoktasi: 9200,
  veriDizini: './veri',
  kutukDuzeyi: 'bilgi',
  yonetimAnahtari: '***99'
}

Ortamda tanımlı bir değişken, dosyadan gelen değeri ezer:

OLCUM_PORT=9999 node --env-file=.env yap-dene.mjs
{
  dinlemeAdresi: '127.0.0.1',
  dinlemeNoktasi: 9999,
  veriDizini: './veri',
  kutukDuzeyi: 'bilgi',
  yonetimAnahtari: '***99'
}

Ortam dosyası bir geliştirme kolaylığıdır. Sürüm kontrolüne alınmaz; deponun kök dizininde yalnızca alan adlarını ve açıklamalarını içeren bir örnek dosya tutulur ve gerçek dosya yok sayılanlar listesine eklenir. Sürüm Kontrolüne Giriş kursunda tanıtılan yok sayma düzeneği tam olarak bu iş içindir.

En Üst Basamağı Gerçekleştirmek

Yukarıdaki listenin ilk maddesi henüz kodda yok: okuyucu yalnızca ortama bakıyor, komut satırı argümanına bakmıyor. Tek bir çalıştırmayı — bir tanı oturumunu, bir sınama açılışını — ortam değiştirmeden yönlendirmek için bu basamak gerekir.

Katmanları tek yerde birleştiren okuyucu, her alan için hangi kaynağın kazandığını da kaydeder:

// yapilandirma-katmanli.mjs
import { parseArgs } from 'node:util';

const TANIM = {
  dinlemeAdresi:  { secenek: 'adres',        degisken: 'OLCUM_ADRES',        tur: 'dizgi',   varsayilan: '127.0.0.1' },
  dinlemeNoktasi: { secenek: 'port',         degisken: 'OLCUM_PORT',         tur: 'tamsayi', varsayilan: 8791 },
  kutukDuzeyi:    { secenek: 'kutuk-duzeyi', degisken: 'OLCUM_KUTUK_DUZEYI', tur: 'dizgi',   varsayilan: 'bilgi' },
};

function donustur(kaynakAdi, ham, tur) {
  if (tur === 'dizgi') return ham;
  const sayi = Number(ham);
  if (!Number.isInteger(sayi) || sayi <= 0) {
    throw new Error(`${kaynakAdi} pozitif tam sayi olmali, gelen: ${JSON.stringify(ham)}`);
  }
  return sayi;
}

// Argumanlar ve ortam disaridan verilir: sinamada gercek surece dokunulmaz
export function yapilandirmayiOku(argumanlar = process.argv.slice(2), ortam = process.env) {
  const { values } = parseArgs({
    args: argumanlar,
    options: Object.fromEntries(Object.values(TANIM).map((t) => [t.secenek, { type: 'string' }])),
    strict: true,
  });

  const deger = {};
  const kaynak = {};
  for (const [alan, t] of Object.entries(TANIM)) {
    if (values[t.secenek] !== undefined) {
      deger[alan] = donustur(`--${t.secenek}`, values[t.secenek], t.tur);
      kaynak[alan] = 'arguman';
    } else if (ortam[t.degisken] !== undefined && ortam[t.degisken] !== '') {
      deger[alan] = donustur(t.degisken, ortam[t.degisken], t.tur);
      kaynak[alan] = 'ortam';
    } else {
      deger[alan] = t.varsayilan;
      kaynak[alan] = 'varsayilan';
    }
  }
  return Object.freeze({ deger: Object.freeze(deger), kaynak: Object.freeze(kaynak) });
}
// yap-katman-dene.mjs
import { yapilandirmayiOku } from './yapilandirma-katmanli.mjs';

try {
  const { deger, kaynak } = yapilandirmayiOku();
  for (const alan of Object.keys(deger)) {
    console.log(`${alan.padEnd(15)} ${String(deger[alan]).padEnd(12)} <- ${kaynak[alan]}`);
  }
} catch (hata) {
  console.error('yapilandirma hatasi:', hata.message);
  process.exitCode = 78;
}

Hiçbir kaynak verilmediğinde bütün alanlar koddaki varsayılana düşer:

node yap-katman-dene.mjs
dinlemeAdresi   127.0.0.1    <- varsayilan
dinlemeNoktasi  8791         <- varsayilan
kutukDuzeyi     bilgi        <- varsayilan

Ortam değişkeni varsayılanı ezer:

OLCUM_PORT=9100 OLCUM_KUTUK_DUZEYI=ayrinti node yap-katman-dene.mjs
dinlemeAdresi   127.0.0.1    <- varsayilan
dinlemeNoktasi  9100         <- ortam
kutukDuzeyi     ayrinti      <- ortam

Argüman da ortamı ezer:

OLCUM_PORT=9100 node yap-katman-dene.mjs --port 9500
dinlemeAdresi   127.0.0.1    <- varsayilan
dinlemeNoktasi  9500         <- arguman
kutukDuzeyi     bilgi        <- varsayilan

Doğrulama her katmanda aynı kalır; hata iletisi hangi kaynağın adını taşıyorsa düzeltmenin yapılacağı yer orasıdır:

node yap-katman-dene.mjs --port sekiz; echo "cikis kodu: $?"
yapilandirma hatasi: --port pozitif tam sayi olmali, gelen: "sekiz"
cikis kodu: 78

Kaynak kaydının tutulması ek bir alan gibi görünür ama tanıda belirleyicidir: bir ayarın beklenmedik değerde olması, ya yanlış değer verilmesinden ya da beklenen katmanın hiç okunmamasından gelir. Kaynak alanı ikisini ayırır ve bu bilgi açılış kütüğüne yazılabilir — gizli olmayan alanlar için.

Okuyucunun argüman dizisini ve ortamı parametre olarak alması, aynı zamanda sınanabilirlik kararıdır: test, gerçek süreç durumuna dokunmadan bütün katman birleşimlerini deneyebilir. Bu tasarım Test dersinde yeniden ele alınacak.

Gizli Değerler Ayrı Ele Alınır

Bir yönetim anahtarı ile bir bağlantı noktası numarası aynı sınıftan değildir. İkincisi tanı çıktısına, hata iletisine ve kütüğe yazılabilir; ilki yazılamaz.

Ayrımın uygulanması iki kurala dayanır. Birincisi, yapılandırma nesnesinin doğrudan yazdırılmamasıdır; yukarıdaki yazilabilirGorunum fonksiyonu gizli alanı maskeler ve yalnızca son iki karakteri bırakır. Bırakılan kısım, yanlış anahtarın yüklendiğini anlamaya yeter, anahtarı ele vermeye yetmez.

İkincisi, gizli değerin argüman olarak geçirilmemesidir. Komut satırı argümanları makinedeki süreç listesinde görünür; ortam değişkenleri görünmez. Bu yüzden gizli değerler yalnızca ortamdan veya bir dosyadan okunur.

Hata nesnelerine de dikkat edilir: bir bağlantı hatası nesnesi, bağlantı dizgisini alan içinde taşıyabilir. Bu nesnenin olduğu gibi kütüğe yazılması, maskelemeyi anlamsız kılar. Günlükleme dersinde kütüğe yazılan alanların bilinçli seçilmesinin nedeni budur.

Yapılandırma Değiştirilemezdir

Yapılandırma nesnesi bir kez okunur ve dondurulur. Çalışma sırasında değiştirilebilen bir ayar, aynı isteğin iki farklı davranış göstermesine yol açar ve tanıyı olanaksız kılar.

// yap-dondurma.mjs
import { yapilandirmayiOku } from './yapilandirma.mjs';

const y = yapilandirmayiOku();
console.log('dinleme noktasi :', y.dinlemeNoktasi);
console.log('dondurulmus mu  :', Object.isFrozen(y));

try {
  y.dinlemeNoktasi = 1234;      // ES modulleri kati kipte calisir
} catch (hata) {
  console.log('yazma denemesi  :', hata.constructor.name);
}
console.log('deger degismedi :', y.dinlemeNoktasi);
OLCUM_YONETIM_ANAHTARI=abc123XYZ node yap-dondurma.mjs
dinleme noktasi : 8791
dondurulmus mu  : true
yazma denemesi  : TypeError
deger degismedi : 8791

Dondurma, JavaScript’te Nesneler ve Fonksiyonlar kursunda tanıtılan değişmezlik tekniklerinden biridir. ES modülleri katı kipte çalıştığı için, dondurulmuş nesneye yazma girişimi sessizce yok sayılmaz; hata fırlatır ve yanlışı yazan kod hemen görünür.

Dondurma tek düzeylidir: iç içe nesneler ayrıca dondurulmalıdır. Yukarıdaki yapılandırma yalnızca ilkel değerler taşıdığı için tek çağrı yeterlidir.

Özet

  • Ortama göre değişen her değer koddan çıkarılır; aynı yapı değiştirilmeden başka bir ortamda çalışabilmelidir.
  • Yapılandırma tek bir modülde okunur ve doğrulanır; programın geri kalanı ortam nesnesine dokunmaz ve tür sorusunu sormaz.
  • Doğrulama açılışta yapılır ve başarısızsa süreç açılmaz; hata iletisi eksik alanı ve beklenen biçimi söyler.
  • Kaynak öncelik sırası baştan belirlenir: argüman, ortam değişkeni, ortam dosyası, koddaki varsayılan; her alan için kazanan kaynağın kaydedilmesi tanıyı kısaltır.
  • Gizli değerler yazdırılmadan önce maskelenir ve argüman olarak geçirilmez; komut satırı argümanları süreç listesinde görünür.

Sonraki Adım

Yapılandırma hatası, sürecin açılmasını engelleyen bir hataydı. Çalışma sırasında ortaya çıkan hatalar aynı davranışı gerektirmez: bir isteğin bozuk gövdesi hizmeti kapatmamalı, ama bellekte bozulmuş bir durum onu kapatmalıdır. Sonraki ders bu iki sınıfı ayırır ve hangi hatanın yakalanacağını, hangisinin sürecin sonlanmasına yol açacağını belirler.

İlerlemeni kaydetmek ve not almak için Giriş yap

Notlarım

Not almak için giriş yapmalısın.

Aramak için yazmaya başlayın.

↑↓ Esc gezin · aç · kapat