C# ile uçtan uca bir Web projesi geliştirilmesi. Konu, dünya üzerindeki deniz fenerlerine ait fotoğrafların paylaşıldığı, yorumlandığı ve puanlandığı bir sosyal platform. (Proje ilerleyişi Youtube kanalından da takip edebilirsiniz) Projede mümkün mertebe yazılım dünyasının efsane konularına olan ihtiyaçları ortaya koymaya çalışmak ilk amaçlarımdan birisidir. Örneğin, hiçbir mimari kalıba uymadan sadece belli prensipleri (soyutlamalar, bağımlılıkları tersine çevirme, sorumlulukları dağıtma vs gibi) baz alarak bir proje iskeleti oluşturup, sonrasında sorularla yaklaşımın doğruluğunu değerlendirmek, açık noktaları tespit etmek ve standartlaşmış bir mimari kalıba çevirmek gibi.
- EntityBase - Audit Alanları:
EntityBasesınıfınaCreatedAt,ModifiedAt,DeletedAtgibi audit alanları eklenebilir. (EntityBase.cs:6)
-
AddCommentHandler - Bileşen Yapılandırması: İhtiyaç duyulan bileşenlerin daha temiz bir şekilde yapılandırılması gerekiyor. (AddCommentHandler.cs:22)
-
CreateLighthouseHandler - User ID Yönetimi: Gerçek bir uygulamada kullanıcı ID'si authentication context'ten alınmalı. Şu an
Guid.Emptykullanılıyor. (CreateLighthouseHandler.cs:29, 47, 65, 87) -
CreateUserHandler - User ID Yönetimi: Gerçek bir uygulamada kullanıcı ID'si authentication context'ten alınmalı. Şu an
Guid.Emptykullanılıyor. (CreateUserHandler.cs:25, 41, 58, 76) -
LighthouseDtoValidator - Primary Key Tasarımı: Tablodaki Primary Key tasarımı gözden geçirilmeli. (LighthouseDtoValidator.cs:17)
-
PhotoRepository - Reflection ile ID Atama:
PhotoRepository.MapPhotometodunda reflection kullanarak ID ataması yapılıyor. Bu yaklaşım terk edilmeli, daha temiz bir çözüm bulunmalı. (PhotoRepository.cs:151) -
CachedCountryDataReader - Mapper Kullanımı: Country ve CountryDto arasında mapper kullanımı değerlendirilmeli. (CachedCountryDataReader.cs:74)
-
ExternalCommentAuditor - Runtime Service Discovery: Servis adresi runtime'da bulunmalı, hardcoded olmamalı. (ExternalCommentAuditor.cs:15)
-
ExternalCommentAuditor - HashiCorp Consul Entegrasyonu: HashiCorp Consul ile Service Discovery mekanizması kurulabilir. (ExternalCommentAuditor.cs:16)
-
CachedConfigurationService - Cache Invalidation: Cache Invalidation fonksiyonları düzenlenebilir ve iyileştirilebilir. (CachedConfigurationService.cs:242)
-
Lighthouse Create - Unknown Alanlar: Deniz feneri oluştururken fotoğraf yükleme sırasında "Unknown" olarak işaretlenen alanlar (CameraType, Resolution, Lens, UserId) gerçek verilerle doldurulmalı. (Create.cshtml.cs:114)
-
Lighthouse Delete - Photo İlişkisi: Deniz feneri silinirken o deniz fenerine ait ana (primary) fotoğrafın ID'si bulunmalı ve silinmeli. Şu an lighthouse ID ile photo ID aynı varsayılıyor. (List.cshtml.cs:70)
-
Photo Deletion Error Handling: Deniz feneri silme işlemi başarılı olup fotoğraf silme başarısız olduğunda kullanıcıya bilgi verilmesi ve ne yapılacağına karar verilmesi gerekiyor. (List.cshtml.cs:75)
-
IsPrimary İş Kuralı: Bir deniz fenerinin birden fazla fotoğrafı olabilir ancak sadece bir tanesi primary olabilir. Bu iş kuralının implementasyonu için geliştirme gerekiyor.
-
Photo Validation: Bir lighthouse için birden fazla primary fotoğraf olmaması için validation katmanında kontrol mekanizması eklenebilir.
-
Backoffice Photo Gallery: Deniz fenerlerinin tüm fotoğraflarını görüntüleme ve primary fotoğrafı değiştirme özelliği eklenebilir.
-
Event Handler - Switch Case Yapısı:
RabbitMqEventConsumerService.ProcessEventAsyncmetodunda farklı event tiplerini switch case ile yönetme yerine daha esnek bir yapı kurulmalı. (RabbitMqEventConsumerService.cs:122) -
PhotoUploadedEventHandler - İş Mantığı Eksikliği:
PhotoUploadedEventHandler.HandleAsyncmetodunda gerçek iş mantığı implementasyonu yapılmalı. Şu anda sadece loglama yapılıyor. (PhotoUploadedEventHandler.cs:23)
Deniz Feneri meraklıları için bir sosyal paylaşım platformu geliştirmek.
- Platform kullanıcıları çektikleri deniz feneri fotoğraflarını paylaşabilirler.
- Kullanıcılar dünyanın dört bir yanında yer alan deniz fenerleri hakkında kapsamlı ve detaylı bilgileri öğrenebilirler.
- Kullanıcılar deniz feneri fotoğraflarına yorum bırakabilir ve puanlayabilirler.
Bu projeyi geliştirmenin temel amaçları aşağıda listelenmiştir.
- C# ve .Net platformunu örnek bir proje geliştirerek tanımak.
- Düzenli olarak refactoring uygulayıp kodu iyileştirmeye çalışmak.
- AI Asistanlarından yararlanmak (minimum ölçüde)
- Temel yazılım prensiplerini keşfetmek, uygulamak ve sorgulamak.
- Yazılım mimarilerinin ihtiyaçlarını fark etmeye çalışıp, tartışmak, uygulamak.
Minimum Profil;
- Temel seviyede C# ile programlama bilgisine sahiptir.
- Temel seviyede Nesne Yönelimli Programlama (Object Oriented Programming) bilgisi vardır.
- Temiz kod kavramı ve standartları hakkında farkındalık sahibidir.
- Doğrudan uygulamak yerine, sorgular, araştırır, ikna olur ve sonra uygular.
İdeal Profil;
- SOLID prensiplerini sorgular.
- Teknik Borç ile mücadele yöntemleri hakkında fikir sahibidir.
- Yazılım mimarilerine meraklıdır.
- Kendi sistemlerinde docker kullanır.
- Web Api dışında farklı servis geliştirme standartları olduğunu bilir.
- Dağıtık sistemlerin zorluklarına aşinadır.
- Doğrudan uygulamak yerine, sorgular, araştırır, ikna olur ve sonra uygular.
- Kullanıcıların paylaştığı fotoğrafları nasıl ve nerede tutacağız? (Boyut, depolama yeri, yazma/okuma hızları, dağıtık topoloji kullanımları)
- Kullanıcı yorumlarının denetlenmesi ve istenmeyen ifadelerin engellenmesi nasıl sağlanır?
- Bir fotoğrafın doğru deniz fenerine ait olduğu nasıl tespit edilir?
- Fotoğraflardaki özgünlüğü anlamak için kategorilendirme veya tag'leme aşamasında AI araçlarından nasıl yararlanılır?
- Çok sayıda kullanıcının farklı lokasyonlardan fotoğraf yüklemesi halinde fotoğrafın analizi, doğrulanması, sınıflandırılması gibi hizmetlerin sistemin genelini etkilemeden en hızlı şekilde yapılması nasıl sağlanır?
- Çözüme dahil olan harici servislerin oluşturacağı dağıtık sistemde kaotik durumların önüne nasıl geçilir, sistemin dayanıklılığı nasıl sağlanır?
- Bölüm 00 - Project Lighthouse Social Açılış
- Bölüm 01 - Domain Projesinin Oluşturulması
- Bölüm 02 - Entity Nesnelerinin Eklenmesi
- Bölüm 03 - Enumeration Bazlı Türlerin Eklenmesi
- Bölüm 04 - Örnek Kontratların(Interface) Eklenmesi
- Bölüm 05 - Application Katmanı
- Bölüm 06 - Handler Nesnelerinin Yazılması
- Bölüm 07 - Handler Sınıfları için Birim Testler
- Bölüm 08 - DTO Nesneleri için Validator Kullanımı
- Bölüm 09 - Teknik Borç Önlemleri için Sonarqube Kullanımı
- Bölüm 10 - Yorumlar için Denetim Bileşeninin Eklenmesi
- Bölüm 11 - OpenAI Moderation API ile Yorum Denetim Servisinin Geliştirilmesi
- Bölüm 12 - Uçtan Uca Entegrasyon Testlerinin Yazılması
- Bölüm 13 - PostgreSQL Veritabanı Hazırlıkları
- Bölüm 14 - Data Katmanı ve Repository Sınıflarının Geliştirilmesi
- Bölüm 15 - Application Katmanı ve LighthouseService Bileşeninin Geliştirilmesi
- Bölüm 16 - Terminal Bazlı İstemci Uygulamanın Geliştirilmesi
- Bölüm 17 - MinIO Destekli PhotoStorage Bileşeninin Geliştirilmesi
- Bölüm 18 - İstemci Tarafında PhotoStorageService' in Kullanılması
- Bölüm 19 - Vault Entegrasyonu I
- Bölüm 20 - Vault Entegrasyonu II
- Bölüm 21 - Client Uygulamasını Çalışır Halde Tutmak
- Bölüm 22 - Cache Entegrasyonu I
- Bölüm 23 - Cache Entegrasyonu II
- Bölüm 24 - Redis Entegrasyonu
- Bölüm 25 - Redis Servis Bileşeninin Geliştirilmesi
- Bölüm 26 - Pipeline Nesnelerinin İnşası
- Bölüm 27 - Pipeline Behavior Bileşenlerinin Yazılması ve Entegrasyonu
- Bölüm 28 - Debug İşlemleri ile Pipeline Akışının İzlenmesi
- Bölüm 29 - Lighthouse Web API Projesinin Oluşturulması
- Bölüm 30 - Web API Projesi Debug ve Runtime Hatalarının Giderilmesi
- Bölüm 31 - Projeye Yeni Bir Özellik(Feature) Eklemek
- Bölüm 32 - Tüm Veriyi Döndürme! Sayfalama(Paging) Tekniğini Kullan
- Bölüm 33 - Teknik Borçtan Kaçıl(a)maz! Sonarqube Yakalar
- Bölüm 34 - OData Servisi ile Veriyi HTTP Üzerinden Sorgulamak
- Bölüm 35 - Peki ya DI Servislerine Bildirilen Her Bileşene İhtiyacımız Yoksa?
- Bölüm 36 - Keycloak ile Tanışalım
- Bölüm 37 - Web API Projesinde Keycloak Bazlı Yetkilendirme(Authorization) Kullanımı
- Bölüm 38 - Logları Elasticsearch'e Gönderelim
- Bölüm 39 - Vault Bilgilerini Cache Üzerinde Tutmak (Açtık Yine Dertsiz Başımıza Dert)
- Bölüm 40 - Daha İyi Bir Hata Yönetimi (Result Pattern Refactoring)
- Bölüm 41 - Gelen Bir Yorum Üzerine Sistemimize Graylog'u Adapte Ediyoruz
- Bölüm 42 - Projedeki Son Durum ve PhotoController ile Dükkana Dönüş
- Bölüm 43 - Photo Upload Sürecinde Dağıtık Transaction Yönetimi için SAGA Pattern
- Bölüm 44 - SAGA Deseninin Photo Upload Sürecine Uyarlanması
- Bölüm 45 - Photo Upload Sürecinde SAGA Pattern Testleri
- Bölüm 46 - Event Based Sistem için Hazırlıklar
- Bölüm 47 - RabbitMQ için Event Publisher Bileşeninin Geliştirilmesi
- Bölüm 48 - Event'ler RabbitMQ'ya Gidibilecek mi?
- Bölüm 49 - Nerede Bu RabbitMQ Mesajları? (Queue Oluşturma ve Kod Üzerinden Dinleme)
- Bölüm 50 - Razor Tabanlı Backoffice Uygulaması için İlk Adımlar
- Bölüm 51 - Yeni Deniz Feneri Eklemek için Create Sayfasının Oluşturulması
- Bölüm 52 - Deniz Fenerlerini Listeleme Sayfasının Oluşturulması
- Bölüm 53 - Ülke Bilgilerini Çekmek için Application Katmanında Geliştirmeler
- Bölüm 54 - Razor Sayfasındaki Select Kontrolünü Servis Katmanına Bağladık
- Bölüm 55 - Ülke Listesi için Her Seferinde Servis Çağrısı mı yapacağız?
- Bölüm 56 - Razor Sayfasında Yeni Bir Deniz Feneri Oluşturmayı Başardık. Peki Fotoğraf?
- Bölüm 57 - Fotoğraf Kaydetme ve Doğrulama İşlemleri İçin Razor Kod Tarafında Geliştirmeler
- Bölüm 58 - En Sonunda Backoffice Uygulamasından Fotoğraf Yüklemeyi Başardık!
- Bölüm 59 - Sistemde Oluşan Event'leri Tüketmek İçin Bir Worker Service Projesi Oluşturalım
- Bölüm 60 - Worker Service Projemize RabbitMQ Entegrerasyonunu Ekleyelim
- Bölüm 61 - Kod Hatalarını Düzeltip Worker Service Projemizi İşler Hale Getirelim
Teknik borçlanmanın önüne geçmek için statik kod analiz aracı olarak Sonarqube kullanılmaktadır. Local ortamda docker-compose ile ayağa kaldırılan üründe tarama başlatmak için aşağıdaki hazırlıkları yapmak yeterlidir.
# Dotnet için gerekli tarama aracının yüklenmesi
dotnet tool install --global dotnet-sonarscanner
# Solution klasöründe ise aşağıdaki komutların çalıştırılması yeterlidir.
# Elbette token bilgisi sizin kurulumunuza göre değişiklik gösterecektir.
dotnet sonarscanner begin /k:"Project-Ligthouse-Social" /d:sonar.host.url="http://localhost:9000" /d:sonar.token="sizin_için_üretilen_token"
dotnet build
dotnet sonarscanner end /d:sonar.token="sizin_için_üretilen_token"Belirli periyotlarda Sonarqube taraması yaparak projenin kod kalite uyumluluğu ölçümlenebilir. Ölçmekte fayda vardır.
# docker-compose dosyasında JudgeDredd klasörü için servis bildirimi yapılır.
# Sonrasında docker-compose dosyasının olduğu klasörde build işlemi başlatılır.
docker-compose build
# Ardından hizmetler ayağa kaldırılır.
docker compose up -d
# Testler için 5005 portundan hizmet veren JudjeDredd servisine talepler gönderilebilirPath : ProjectLighthouseSocial-Dev
{
"DbConnStr": "Host=localhost;Port=5432;Database=lighthousedb;Username=johndoe;Password=somew0rds",
"KeycloakAudience": "account",
"KeycloakAuthority": "http://localhost:8400/",
"KeycloakClientId": "lighthouse-service-client",
"KeycloakClientSecret": "Lupgay1UcpJA7vRDOr7MsrBJN5B7bJoN",
"KeycloakClockSkew": "5",
"KeycloakRealm": "ProjectLighthouseSocialRealm",
"KeycloakRequireHttpsMetadata": "false",
"KeycloakValidateAudience": "true",
"KeycloakValidateIssuer": "true",
"KeycloakValidateIssuerSigningKey": "true",
"KeycloakValidateLifetime": "true",
"MinIOAccessKey": "admin",
"MinIOSecretKey": "password",
"RabbitMQUser": "admin",
"RabbitMQPassword": "admin1234"
}Örneğin Vault tarafında tanımlı bir secret için yeni sürüm çıktık. Eğer Cache aktifse vault değerlerinin tekrardan yüklenmesi gerekir. Bunu normalde Cache Invalidation metodunu çağırarak yapabiliriz ama her ihtimale karşı docker üzerinden de ilgili key değerini silerek ilerlemek mümkün. Örneğin vault ayarlarını silmek istersek aşağıdaki komutu kullanabiliriz.
docker exec -it plh-redis redis-cli del vault:keycloak_settingsRedis cache testleri sırasında docker container içerisine girip keyleri kontrol etmek veya manuel silmek isteyebiliriz. Aşağıdaki bunun için kullanabileceğimiz pratik komutlar yer alıyor.
# Redis container'ına erişim
docker exec -it plh-redis sh
# Redis CLI'yi çalıştırma
redis-cli
# Tüm anahtarları listeleme
keys *
# Key, Value ekleme, listeleme, silme
set Environment "Development"
get Environment
set user:Service "{\"user\":\"apiAccount\"}" EX 3600 # 1 saat Geçerlilik süresi
get user:Service
del user:Service
# Key var mı kontrolü
exists user:Service
# Bir key'in kalan yaşam süresini öğrenme
TTL user:Service- Yorum Denetiminde Caching: JudjeDredd servisi bir metin içeriğini denetlemek için OpenAI Moderation API'sine gidiyor. Saldırı türünden aynı yorumun defalarca gönderildiği bir durumda, OpenAI servisine sayısız kez gidilebilir. Gelen isteklerden aynı olanlar için daha önceden alınmış cevaplar (Örneğin zaten flagged=true olanları) belli süreliğini cache'te tutulup anında cevap dönülebilir.
- Aynı isteğin birden fazla defa gönderilmesi bir saldırı işareti de olabilir. Bunu tespit edip tedbir alan ve uyarı veren bir düzenek de geliştirilebilir.
- Repository Sözleşmeleri: Repository sözleşmelerinin Domain katmanında durması doğru mu? Ya da domain katmanında duracaklarsa hangi davranışları içeren sözleşmeler konulmalı? Sadece Create, Retrieve, Update, Delete fonksiyonelliklerini taşıyan ve Domain'i doğrudan ilgilendiren sözleşmeler buraya konup Business Rules içeren sözleşmeler farklı bir yere mi alınmalıdır?
- Handler Bileşenlerine Erişim: Projenin çekirdek katmanı Appliacation kütüphanesi. Bu katman içinden UI, Api, Terminal gibi istemcilere açtığımız sözleşmeler (Contracts klasörü) düşünüldüğünde Handler sınıfları dışarıya kapatılmalı mıdır?
- Unit of Work: Handler bileşenlerindeki HandleAsync metotları belli bir akışa sahip kodları işletmekte. DTO doğrulamaları, farklı iş kuralları, transaction'a dahil işlemler, loglama vs Bu akış deseni bir üst yapıda mı toplanmalı? (Bir unit of work içerisinde mesela)
- Photo Storage: Projede fiziksel alan olarak en çok yer kaplayacak ve en çabuk büyüyecek kısım deniz feneri fotoğrafları. Fotoğrafların depolanacağı yer olarak local bir container kullanılabilir mi? Örneğin AWS S3 Api uyumlu MinIO veya muadili başka bir depo.
- Persistence: Proje domaininde yer alan Entity'ler sayıca çok veya içerik olarak karmaşık değiller. Veriler şema bazlı bir veritabanı sisteminde tutulabilir (Örn: Postgresql). İlk etapta Entity Framework Core ve Code-First yerine Database First modelde ilerlenebilir ve erişimler için Dapper'dan yararlanılabilir.
- Service Health Check/Discovery: JudgeDredd gibi harici servislerin sayısı artacak. Ayrıca sistem büyüdükçe Postgresql Server, Storage Service, RabbitMQ, Keycloak vb birçok enstrüman da çözüme dahil edilecek. Bu servislerin ayakta olup olmadıklarının kontrolü ve hatta ortamlara göre değişecek port veya ip bilgilerinin kolayca yönetimi gerekecek. Bu amaçla HashiCorp'un Consul aracından yararlanılabilir.
- Membership Management: Sisteme dahil olacak abonelerin doğrulama (Authentication) işlemleri için hibrit bir model tercih edilebilir. Identity Provider olarak Keycloak kullanılabilir, kullanıcı profil bilgileri veritabanında saklanabilir. Projemiz authentication detayları ile uğraşmak zorunda kalmaz.
- Secret Keys: Projede veritabanı adresi, api key, api secret gibi şifrelenmesi ve ele geçirilmemesi gereken bilgiler yer alacak. Bunların daha güvenli bir ortamda tutulup çalışma zamanında çözümlenerek kullanılması yerinde olacaktır. Gizli değerlerin yönetimi içim bir Vault sisteminden yararlanılabilir. Hashicorp Vault veya Localstack olabilir.
- CLI Aracı: CLI (Command Line Interface) kullanma bilgisi olanlar son kullanıcılar için bir terminal aracı geliştirilebilir mi?
- Public API: Projenin genel kullanıma açık bir API hizmeti olabilir mi? Örneğin, deniz feneri bilgilerini dış dünyaya açabiliriz. Bu, standart web arayüzü dışında bir hizmettir, farklı uygulamaların işine de yarar.
- Raporlama: Projemiz ne tür raporlar sunabilir? Dünyanın en popüler fotoğraflarına sahip deniz fenerleri, en iyi fotoğraflara sahip kullanıcılar, en çok uğranılan deniz fenerlerinin olduğu ülkeler, faal olan deniz fenerleri listesi vb Bu raporlar nasıl bir uygulama baz alınabilir.
- Entegrasyon Testleri: Projede ilerledikçe servis ve bileşenlerin sayısı artıyor. Bu durumda entegrasyon testleri nasıl bir strateji ile iyileştirilebilir? Örneğin, JudgeDredd ya da PhotoStorage bileşenlerinin dahil olduğu vakalar için entegrasyon testleri yazılabilir ve çalışma zamanı olarak da bir TestContainer kullanılabilir.
- Öneri Sistemi: Platform kullanıcılarına belli kriterlere göre gidilmesi gereken ya da gidebilecekleri deniz fenerleri listeleyen bir öneri sistemi eklenebilir.
Proje ve video anlatım serisi sona erdiğinde aşağıdaki sorulara cevap verebiliyor olmalıyız.
- C# dilinin temel özelliklerine yer verildi mi?
- OOP (Object Oriented Programming) prensipleri uygulandı mı?
- SOLID prensiplerine yer verildi mi?
- Kod bazlı teknik borçlardan arındırıldı mı?
- Belli bir yazılım mimari stiline evrildi mi?
- Projede en az bir Rest tabanlı Web Api kullanıldı mı?
- Projede gRPC tabanlı bir servis kullanıldı mı?
- Razor tabanlı Web uygulaması geliştirildi mi?
- Farklı dillerde yazılmış servisler kullanıldı mı?
- Bir dağıtık sistem kurgusu tesis edildi mi?
- Dağıtık sistem kurgusu tesis edildiyse resilience için gerekli tedbirler alındı mı?
- Dağıtık sistem kurugusu için izleme, loglama, alarm mekanizmaları vs kullanıldı mı?