Bu proje, .NET uygulamalarında kullanılan statik konfigürasyon dosyaları (web.config, app.config, appsettings.json vb.) yerine merkezi ve dinamik bir konfigürasyon yönetim sistemi oluşturmak amacıyla geliştirilmiştir.
Konfigürasyon kayıtları MongoDB üzerinde tutulur ve uygulama çalışırken değiştirilebilir. Böylece deployment, restart veya recycle işlemi gerekmeksizin uygulamalar güncel konfigürasyon değerlerini kullanabilir.
Tüm ekosistem docker-compose ile çalıştırıldı.
- .NET 8
- ASP.NET Core Web API
- React
- MongoDB
- RabbitMQ
- Docker Compose
- xUnit
- FluentAssertions
- Moq
- async/await
src/
ConfigReader
Dinamik konfigürasyon kütüphanesi (DLL)
ConfigReader.Api
Konfigürasyon yönetim API'si
ConfigReader.Web
React tabanlı yönetim ekranı
samples/
ServiceA.Console
DLL kullanım örneği
tests/
ConfigReader.Tests
Unit Test Projesi
Her kayıt aşağıdaki alanlardan oluşmaktadır
| Alan | Açıklama |
|---|---|
| Id | Kayıt Id'si |
| Name | Konfigürasyon anahtarı |
| Type | Veri tipi |
| Value | Değer |
| IsActive | Aktif/Pasif |
| ApplicationName | Uygulama adı |
Desteklenen veri tipleri:
- string
- int
- double
- bool
using ConfigReader;
var configurationReader = new ConfigurationReader(
"SERVICE-A",
"mongodb://localhost:27017/ConfigReaderDb",
5000);
string siteName =
configurationReader.GetValue<string>("SiteName");ConfigurationReader belirlenen süre aralıklarında MongoDB'yi kontrol eder.
refreshTimerIntervalInMs = 5000;Yeni kayıt eklenmesi veya mevcut kayıtların değiştirilmesi durumunda uygulama yeniden başlatılmadan yeni değerler okunabilir
Her servis yalnızca kendi konfigürasyon kayıtlarına erişebilir
Örnek olarak:
SERVICE-A
->
ApplicationName = SERVICE-A
SERVICE-B kayıtlarını göremez.
DLL yalnızca
IsActive = true
olan kayıtları döndürür
Pasif kayıtlar yönetim ekranında görüntülenebilir ancak GetValue() tarafından okunamaz
Case gereksinimi doğrultusunda storage erişilemediği durumlarda kütüphane son başarılı konfigürasyon kayıtları ile çalışmaya devam eder
-
MongoDB çalışırken konfigürasyon okundu
-
MongoDB durduruldu
-
Uygulama son başarılı cache ile çalışmaya devam eder
Sonuç: Storage erişilemediğinde uygulama çalışmasını sürdürmektedir
Bu amaçla iki seviyeli cache yapısı uygulanmıştır:
Uygulama çalışırken MongoDB erişilemez hale gelirse RAM üzerinde bulunan son başarılı konfigürasyon kayıtları kullanılmaya devam edilir
MongoDB
->
Memory Cache
->
GetValue<T>()
Uygulama yeniden başlatıldığında MongoDB erişilemiyorsa, önceki başarılı kayıtlar
cache/{ApplicationName}.cache.json
dosyasından okunur
MongoDB
->
cache.json
->
Memory Cache
->
GetValue<T>()
Bu sayede sistem storage erişilemese dahi çalışmaya devam eder
Sunulan servisler
| Method | Endpoint |
|---|---|
| GET | /api/configurations |
| POST | /api/configurations |
| PUT | /api/configurations/{id} |
| DELETE | /api/configurations/{id} |
API üzerinden;
- kayıt listeleme
- kayıt ekleme
- kayıt güncelleme
- kayıt silme
işlemleri gerçekleştirilebilir
Web arayüzü aşağıdaki işlemleri desteklemektedir:
- Konfigürasyon kayıtlarını listeleme
- Yeni kayıt ekleme
- Mevcut kayıt güncelleme
- Client-side isim filtreleme
Configuration kayıtlarında yapılan değişiklikler event olarak RabbitMQ'ya yayınlanmaktadır.
Aşağıdaki işlemler event üretir:
- Configuration oluşturma
- Configuration güncelleme
- Configuration silme
Routing Key formatı
configuration.changed.{ApplicationName}
Örnek
configuration.changed.SERVICE-A
Event Payload
- Id
- Name
- Type
- Value
- IsActive
- ApplicationName
- Version
- Operation
- OccurredAtUtc
Tüm sistem aşağıdaki komut ile ayağa kaldırılabilir:
docker compose up --buildServisler
| Servis | Adres |
|---|---|
| API | http://localhost:7193 |
| React | http://localhost:5173 |
| RabbitMQ - guest & guest | http://localhost:15672 |
| MongoDB | localhost:27017 |
Not: Lütfen http yazalım.
{
"name": "SiteName",
"type": "string",
"value": "soty.io",
"isActive": true,
"applicationName": "SERVICE-A"
}
{
"name": "IsBasketEnabled",
"type": "bool",
"value": "1",
"isActive": true,
"applicationName": "SERVICE-B"
}
{
"name": "MaxItemCount",
"type": "int",
"value": "50",
"isActive": false,
"applicationName": "SERVICE-A"
}
Unit testleri çalıştırmak için
dotnet testMevcut Unit Testler
- ConfigurationValueConverter
- FileConfigurationCache
- ConfigurationAdminService Delete
Yeni özellik geliştirilirken Test Driven Development (TDD) yaklaşımı uygulanmıştır
Sürecimiz:
-
Önce başarısız test yazıldı
-
İlgili iş mantığı geliştirildi
-
Testler başarılı hale getirildi
-
Refactor yapıldı
Mevcut testler aşağıdaki veri tiplerini kapsamaktadır
- string
- int
- double
- bool
Dinamik Refresh
- React üzerinden SiteName değeri güncellendi
- Console uygulaması restart olmadan yeni değeri okumaya başladı
Sonuç: Başarılı
RabbitMQ
- Güncelleme sonrasında RabbitMQ Queue içerisine Updated event düştü
Sonuç: Başarılı
Offline Cache
- MongoDB durduruldu
- Console uygulaması son başarılı cache ile çalışmaya devam etti
Sonuç: Başarılı
| Gereksinim | Durum |
|---|---|
| .NET 8 DLL | Yapıldı |
| GetValue() | Yapıldı |
| Dinamik Refresh | Yapıldı |
| Restart Gerektirmemesi | Yapıldı |
| MongoDB Storage | Yapıldı |
| IsActive Filtreleme | Yapıldı |
| Application Bazlı İzolasyon | Yapıldı |
| Storage Erişilemezse Çalışmaya Devam Etme | Yapıldı |
| Web Yönetim Paneli | Yapıldı |
| Yeni Kayıt Ekleme | Yapıldı |
| Güncelleme | Yapıldı |
| Client-side Filtreleme | Yapıldı |
| Docker Compose | Yapıldı |
| Unit Test | Yapıldı |
| Dokümantasyon | Yapıldı |
| RabbitMQ Event Publishing | Yapıldı |
| TDD Yaklaşımı | Yapıldı |
| Delete API | Yapıldı |
| Offline Cache | Yapıldı |
| Runtime Cache | Yapıldı |
| File Cache | Yapıldı |
| Dependency Inversion | Yapıldı |
| TPL / async-await Kullanımı | Yapıldı |
| Concurrency Kontrolü | Yapıldı |
| Repository Pattern | Yapıldı |
| Dependency Injection | Yapıldı |
| Proje Dokümantasyonu | Yapıldı |
| Source Control / GitHub | Yapıldı |
| SOLID Prensipleri | Yapıldı |
Bu projede aşağıdaki prensipler uygulanmıştır:
- SOLID prensipleri uygulandı
- Dependency Inversion yapıldı
- Repository benzeri storage soyutlaması yapıldı
- Thread-safe in-memory cache (ConcurrentDictionary) yapıldı
- Memory + File Cache
- Event Driven Communication (RabbitMQ)
- Async/Await tabanlı I/O asenkron programlama işlemleri gerçekleştirildi
- Generic tip dönüşümü (GetValue) yapıldı
- İki seviyeli cache mekanizması (Memory + File) işlemleri sağlandı