BulutBusiness.Core
10.1.1
dotnet add package BulutBusiness.Core --version 10.1.1
NuGet\Install-Package BulutBusiness.Core -Version 10.1.1
<PackageReference Include="BulutBusiness.Core" Version="10.1.1" />
<PackageVersion Include="BulutBusiness.Core" Version="10.1.1" />
<PackageReference Include="BulutBusiness.Core" />
paket add BulutBusiness.Core --version 10.1.1
#r "nuget: BulutBusiness.Core, 10.1.1"
#:package BulutBusiness.Core@10.1.1
#addin nuget:?package=BulutBusiness.Core&version=10.1.1
#tool nuget:?package=BulutBusiness.Core&version=10.1.1
BulutBusiness.Core
.NET 10 tabanlı kurumsal iş uygulamaları için paylaşımlı altyapı kütüphanesi. Clean Architecture, CQRS ve Repository Pattern prensiplerine uygun olarak tasarlanmıştır.
Kurulum
dotnet add package BulutBusiness.Core
Modüller
Persistence — Repository & Entity
Generic EF Core repository implementasyonu. Tüm CRUD operasyonları, soft delete, sayfalama ve dinamik filtreleme desteklenir.
// Entity base sınıfı
public class Order : Entity<int> { ... }
// Repository
public class OrderRepository : EfRepositoryBase<Order, int, AppDbContext>, IOrderRepository { }
EfRepositoryBase<TEntity, TEntityId, TContext> metodları:
| Metod | Açıklama |
|---|---|
AddAsync |
Ekle (CreatedDate otomatik set edilir) |
UpdateAsync |
Güncelle (UpdatedDate otomatik set edilir) |
DeleteAsync |
Sil — permanent: true kalıcı, varsayılan soft delete |
GetAsync |
Tek kayıt getir |
GetListAsync |
Sayfalı liste getir |
GetListByDynamicAsync |
Dinamik filtreli liste |
AnyAsync |
Varlık kontrolü |
Soft Delete: DeletedDate set edilerek kayıt gizlenir. Cascade soft delete ilişkili kayıtlara otomatik uygulanır.
Timestamp davranışı: CreatedDate, UpdatedDate, DeletedDate alanları DateTimeOffset tipindedir ve DateTimeOffset.UtcNow ile set edilir. Sunucu/container saat dilimi ayarından bağımsızdır; yerel saat gösterimi client (web/mobil) tarafında kullanıcının bölgesine göre yapılmalıdır.
Sayfalama:
var result = await _orderRepository.GetListAsync(
predicate: o => o.Status == OrderStatus.Active,
orderBy: q => q.OrderByDescending(o => o.CreatedDate),
index: 0,
size: 20
);
// result.Items, result.Count, result.Pages, result.HasNext
Dinamik filtreleme:
var result = await _repository.GetListByDynamicAsync(
dynamic: new DynamicQuery
{
Filter = new Filter { Field = "Status", Operator = "eq", Value = "1" },
Sort = [new Sort { Field = "CreatedDate", Dir = "desc" }]
}
);
Security — JWT & Kimlik Doğrulama
JWT token üretimi, refresh token yönetimi, şifre hashleme ve iki faktörlü kimlik doğrulama (Email OTP / TOTP).
// appsettings.json
{
"TokenOptions": {
"Audience": "myapp.com",
"Issuer": "myapp.com",
"AccessTokenExpiration": 60,
"SecurityKey": "your-secret-key-min-32-chars"
}
}
// Program.cs
builder.Services.AddSecurityServices();
Bileşenler:
| Bileşen | Açıklama |
|---|---|
JwtHelper |
JWT access token üretir |
HashingHelper |
HMACSHA512 ile şifre hash/verify |
EmailAuthenticatorHelper |
E-posta OTP kodu üretir |
OtpNetOtpAuthenticatorHelper |
TOTP (Google Authenticator uyumlu) |
BearerSecurityRequirementOperationFilter |
Swagger'a otomatik Bearer ekler |
Hazır Security Entity'leri: User, OperationClaim, UserOperationClaim, RoleGroup, RoleGroupOperationClaim, UserRoleGroup, RefreshToken, EmailAuthenticator, OtpAuthenticator
Yetki Grubu (RoleGroup)
10.0.7 ile eklenen grup tabanlı yetkilendirme sistemi. Kullanıcılara tek tek claim atamak yerine, claim'leri gruplar halinde yönetip kullanıcılara gruplar atanabilir. Bir kullanıcı birden fazla gruba üye olabilir; token oluşturulurken tüm grupların claim'leri düzleştirilerek (flatten) birleştirilir.
RoleGroup ("Satın Alma Yöneticisi")
├─ PurchaseQuotes.Read
├─ PurchaseQuotes.Create
└─ PurchaseInvoices.Read
Kullanıcı: Ahmet
├─ Grup: "Satın Alma Yöneticisi"
└─ Grup: "Stok Görevlisi"
Token'daki roller (flatten + distinct):
["PurchaseQuotes.Read", "PurchaseQuotes.Create", "PurchaseInvoices.Read", "Stock.Read", ...]
// Domain'de extend et (navigation property'ler burada tanımlanır)
public class RoleGroup : RoleGroup<Guid> { }
public class RoleGroupOperationClaim : RoleGroupOperationClaim<Guid, Guid, int> { }
public class UserRoleGroup : UserRoleGroup<Guid, Guid, Guid> { }
// Token oluştururken — ITokenHelper overload
var directClaims = await _userOpClaimRepo.GetClaimsAsync(userId);
var userGroups = await _userRoleGroupRepo.GetGroupsAsync(userId);
var groupClaims = await _roleGroupClaimRepo.GetClaimsByGroupsAsync(groupIds);
var token = _tokenHelper.CreateToken(user, directClaims, userGroups, groupClaims);
Pasif grup davranışı: IsActive = false olan gruplar token'a claim eklemez; bu sayede bir grubun tüm kullanıcı yetkilerini tek seferde askıya alabilirsiniz.
Application — CQRS Pipeline Behavior'ları
MediatR pipeline üzerinde çalışan cross-cutting concern davranışları.
| Behavior | Interface | Açıklama |
|---|---|---|
AuthorizationBehavior |
ISecuredRequest |
Rol/claim tabanlı yetkilendirme |
CachingBehavior |
ICachableRequest |
In-memory önbellekleme |
CacheRemovingBehavior |
ICacheRemoverRequest |
Cache invalidation |
LoggingBehavior |
ILoggableRequest |
İstek/yanıt loglama |
PerformanceBehavior |
IIntervalRequest |
Yavaş sorgu uyarısı |
TransactionScopeBehavior |
ITransactionalRequest |
Transaction scope |
RequestValidationBehavior |
— | FluentValidation entegrasyonu |
// Yetkilendirme örneği
public class DeleteProductCommand : IRequest<Unit>, ISecuredRequest
{
public string[] Roles => ["Admin", "Manager"];
}
// Önbellekleme örneği
public class GetProductsQuery : IRequest<GetListResponse<ProductDto>>, ICachableRequest
{
public string CacheKey => "Products";
public TimeSpan? SlidingExpiration => TimeSpan.FromMinutes(30);
}
Mailing — E-posta Gönderimi
MailKit tabanlı HTML e-posta gönderimi.
// appsettings.json
{
"MailSettings": {
"Server": "smtp.example.com",
"Port": 587,
"SenderFullName": "example",
"SenderEmail": "noreply@example.com",
"UserName": "noreply@example.com",
"Password": "app-password",
"UseSsl": false
}
}
await _mailService.SendEmailAsync(new Mail
{
Subject = "Siparişiniz alındı",
HtmlBody = "<h1>Teşekkürler!</h1>",
ToList = [new ToEmail { Address = "customer@example.com", Name = "Ad Soyad" }]
});
ElasticSearch
NEST tabanlı ElasticSearch entegrasyonu. Index oluşturma, belge ekleme/güncelleme, arama işlemleri.
// appsettings.json
{
"ElasticSearchConfig": {
"ConnectionString": "http://localhost:9200",
"UserName": "",
"Password": ""
}
}
Localization — Çoklu Dil Desteği
İki farklı lokalizasyon stratejisi:
ResourceLocalizationManager—.resxdosyası tabanlı, yerel çeviriAmazonTranslateLocalizationManager— AWS Translate ile otomatik çeviriTranslateLocalizationManager— Karma strateji
// Program.cs
app.UseMiddleware<LocalizationMiddleware>(); // Accept-Language header'ına göre dil ayarlar
CrossCuttingConcerns — Hata Yönetimi & Loglama
Exception Middleware: BusinessException, AuthorizationException, NotFoundException, ValidationException tiplerini yakalar ve RFC 7807 uyumlu ProblemDetails döner.
Serilog Loglama:
// appsettings.json
{
"SeriLogConfigurations": {
"FileLogConfiguration": {
"FolderPath": "/logs"
}
}
}
Versiyon Geçmişi
10.1.1
- Hata düzeltmesi — yerelleştirme, bölgesel kültür:
Accept-Language: tr-TRartık*.tr.yamlkaynağına düşer (tr-TR→tr). Önceden bölgesel kültür hiçbir dosyayla eşleşmiyor, doğrudan varsayılan dile (en) geçiliyordu —tr-TRgönderen tarayıcılarda tüm iş kuralı mesajları İngilizce çıkıyordu. Kültür adları büyük/küçük harf duyarsız. - Hata düzeltmesi — dinamik filtre, harf duyarsız metin araması (10.1.0 regresyonu): aranan değer
ifade içinde
ToLower()ile bırakılıyordu; EF bu sabit alt-ifadeyi istemcide sunucu kültürüyle hesapladığındantr-TRsunucuda"I"→"ı"oluyor, SQLLOWERise"i"üretiyordu → büyükIiçeren aramalar ("MUHTELIF","KIRMIZI") sonuç vermiyordu. Değer artıkToLowerInvariantile küçültülüyor; sütun sağlayıcıdaLOWER. Türkçe'ye özgü İ/ı eşlemesi (ör. "İstanbul" ↔ "istanbul") uygulamanın sorumluluğundadır.
10.1.0
Hata düzeltme sürümü; davranış değiştiren (kırıcı) maddeler [KIRICI] ile işaretli.
- Dinamik filtre (
ToDynamic) yeniden yazıldı- Değerler alanın tipine çevrilir: sayılar,
bool(true/false/1/0),Guid,enum(ad ya da sayı),DateTime,DateTimeOffset(offset yoksa sunucu yerel saati),DateOnly,TimeOnly,TimeSpanve nullable karşılıkları. Önceden nullable alanlarda ve Guid/enum/tarih alanlarında ifade kurulamıyordu. - [KIRICI] Sayılar önce Invariant kültürle katı ayrıştırılır:
tr-TRsunucuda"12.5"artık125değil12.5. - [KIRICI] Geçersiz alan/operatör/değer
ArgumentException(500) yerineValidationException(400) fırlatır. - Alanı boş düğüm saf grup olarak çalışır (
{ logic: "or", filters: [...] }). Alanı dolu düğümde sözleşme aynı: kök koşul AND (çocuklar). contains/startswith/endswithyalnız metin alanlarında;CaseSensitive=false(varsayılan) artık gerçekten uygulanır (iki tarafLOWER) — PostgreSQL gibi harmanlaması duyarlı sağlayıcılarda da aynı sonuç. Null metin eşleşmez.isnull/isnotnullnull alamayan tipte sabitfalse/truedöner (önceden ifade hatası).
- Değerler alanın tipine çevrilir: sayılar,
RequestValidationBehavior: doğrulayıcılarValidateAsyncile (sırayla) çalışır. ÖncedenMustAsync/CustomAsynckurallarıAsyncValidatorInvokedSynchronouslyExceptionfırlatıyordu.EfRepositoryBasesoft delete 1:1 kontrolü: yalnız unique FK dolu ve benzersiz indeksDeletedDatefiltresi içermiyorsa engeller. Önceden FK boş olsa bile her 1:1 ilişkili kayıt silinemiyordu.Paginate:PagesartıkCountatandıktan sonra hesaplanıyor (önceden 0 →HasNexthep false);size<=0ve büyüksizedeğerlerinde taşma/sıfıra bölme yok.CacheRemovingBehavior:CacheGroupKeydizisinin tüm elemanları siliniyor (önceden hep ilk eleman).- [KIRICI]
ExceptionMiddleware/HttpExceptionHandlerAuthorizationException: kimliği doğrulanmış kullanıcıda 403, anonimde 401 (önceden hep 401 → 401'de oturumu kapatan istemciler yetkisiz işlemde kullanıcıyı sistemden atıyordu).- İşleyici istek başına oluşturulur (önceden paylaşılan örnek eşzamanlı hatalarda başka isteğin yanıtına yazabiliyordu). Yanıt başlamışsa istisna yeniden fırlatılır. İstemci hataları (4xx)
Information, diğerleriErrorseviyesinde loglanır. AuthorizationProblemDetails(string detail, int status = 401).
- Yerelleştirme:
AcceptLocalesboşsa (Accept-Language'siz istek, arka plan işi) varsayılan dile düşülür (öncedenNoNullAllowedException). YAML dosyaları süreç boyunca bir kez ayrıştırılır; kaynak klasörleri bir kez taranır (önceden her istekte/her çeviride). - Güvenlik: parola hash karşılaştırması sabit zamanlı (
CryptographicOperations.FixedTimeEquals); OTP doğrulaması RFC 6238 ağ gecikmesi penceresini (±1 adım) kabul eder. - [KIRICI]
EnsureDbApplied: veritabanı yoksa da migrate eder (öncedenCanConnectfalse ise sessizce atlıyordu). - [KIRICI]
PerformanceBehavior:Stopwatchartık DI'dan alınmaz (singleton/transient paylaşımı süreleri bozuyordu); yapıcı yalnızILoggeralır. - ElasticSearch:
GetSearchByFieldalan/değer filtresini uygular (önceden tüm dokümanlar dönüyordu);InsertManyAsynctoplu yazma hatalarını raporlar; hata mesajıServerErrorboşken NRE vermez;ElasticClienttek örnek. - Test projesi eklendi:
tests/BulutBusiness.Core.Tests.
10.0.9
- BREAKING CHANGE — Timestamp alanları
DateTime→DateTimeOffset:IEntityTimestamps/Entity<TId>:CreatedDate,UpdatedDate,DeletedDateAccessToken.ExpirationDate,RefreshToken.ExpirationDate,RefreshToken.RevokedDateEfRepositoryBaseveJwtHelperartıkDateTime.Now/DateTime.UtcNowyerineDateTimeOffset.UtcNowkullanıyor; sunucu saat dilimi (TZenv) davranışı etkilemiyor- Gerekli aksiyon: tüketici projelerde
Entity<TId>'den türeyen entity'lerin DB kolon tipleri (datetime2→datetimeoffset) için yeni EF Core migration oluşturulmalı; DTO/mapping katmanlarındaDateTimebekleyen kodDateTimeOffset'e güncellenmeli
- Paket güncellemeleri (breaking change yok, API uyumlu):
- AWSSDK.Translate 4.0.1.23 → 4.0.100.14
- MailKit 4.16.0 → 4.18.0, MimeKit 4.16.0 → 4.18.1
- MediatR 14.1.0 → 14.2.0
- Microsoft.EntityFrameworkCore(.InMemory/.Relational) 10.0.5 → 10.0.12
- Microsoft.IdentityModel.Tokens / System.IdentityModel.Tokens.Jwt 8.17.0 → 8.22.0 (8.23.0 bilinçli olarak atlandı — 2 gün önce yayınlandı, güvenlik-kritik bir bağımlılık için "olgunlaşmasını" beklemek adına bir önceki stabil sürüm tercih edildi)
- Serilog 4.3.1 → 4.4.0
- YamlDotNet 16.3.0 → 18.1.0
- Not: Proje zaten
MediatR14.x kullanıyor; MediatR v13'ten itibaren üretim kullanımı için ticari lisans gerektiriyor (bkz. luckypennysoftware.com). Bu güncellemenin getirdiği yeni bir durum değil, ancak lisans durumunuzun kontrol edilmesi önerilir.
10.0.8
- Güvenlik güncellemesi: MailKit 4.15.1 → 4.16.0, MimeKit 4.15.1 → 4.16.0
- GHSA-9j88-vvj5-vhgr (moderate severity) güvenlik açığı giderildi
- Breaking change yok — mevcut API kullanımı tam uyumlu
10.0.7
- Yetki Grubu (RoleGroup) altyapısı eklendi
RoleGroup<TId>—Name,Description,IsActivealanlarıyla yetki grubu entity'siRoleGroupOperationClaim<TId, TRoleGroupId, TOperationClaimId>— grup ↔ claim N:N köprüsüUserRoleGroup<TId, TUserId, TRoleGroupId>— kullanıcı ↔ grup N:N köprüsü
ITokenHelperyeni overload —CreateToken(user, directClaims, roleGroups, roleGroupClaims): aktif grupların claim'lerini otomatik flatten + distinct ederek token'a yazar; geriye dönük uyumlu (eski imza korundu)- Navigation property'ler Core entity'lerine eklenmedi — bağımlılıkları sıfır tutar, uygulama katmanı kendi Domain entity'lerinde tanımlar
10.0.6
EfRepositoryBase:CreatedDate,UpdatedDate,DeletedDatetimestamp'leriDateTime.UtcNow→DateTime.Nowolarak değiştirildi. ContainerTZortam değişkeni ile yerel saat desteği sağlandı.- Paket güncellemeleri: FluentValidation 12.1.1, MailKit 4.15.1, MediatR 14.1.0, EF Core 10.0.5, Microsoft.IdentityModel.Tokens 8.17.0, Swashbuckle 10.1.7, Otp.NET 1.4.1, Serilog 4.3.1
10.0.5
- .NET 10 desteği
- MimeKit 4.14.1, EF Core 10.0.2
10.0.x (önceki)
- .NET 9 → .NET 10 geçişi
- JWT, Repository, CQRS pipeline altyapısı
Gereksinimler
- .NET 10+
- Entity Framework Core 10 uyumlu veritabanı (SQL Server, PostgreSQL vb.)
- (Opsiyonel) ElasticSearch 7.x
- (Opsiyonel) AWS hesabı (Amazon Translate için)
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net10.0
- AWSSDK.Translate (>= 4.0.100.14)
- FluentValidation (>= 12.1.1)
- FluentValidation.DependencyInjectionExtensions (>= 12.1.1)
- MailKit (>= 4.18.0)
- MediatR (>= 14.2.0)
- Microsoft.EntityFrameworkCore (>= 10.0.12)
- Microsoft.EntityFrameworkCore.InMemory (>= 10.0.12)
- Microsoft.EntityFrameworkCore.Relational (>= 10.0.12)
- Microsoft.IdentityModel.Tokens (>= 8.22.0)
- MimeKit (>= 4.18.1)
- NEST (>= 7.17.5)
- NEST.JsonNetSerializer (>= 7.17.5)
- Otp.NET (>= 1.4.1)
- Serilog (>= 4.4.0)
- Serilog.Sinks.File (>= 7.0.0)
- Swashbuckle.AspNetCore.SwaggerGen (>= 10.2.3)
- System.IdentityModel.Tokens.Jwt (>= 8.22.0)
- YamlDotNet (>= 18.1.0)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 10.1.1 | 170 | 10/1/2026 |
| 10.1.0 | 109 | 9/30/2026 |
| 10.0.9 | 220 | 9/20/2026 |
| 10.0.8 | 851 | 4/19/2026 |
| 10.0.7 | 188 | 4/9/2026 |
| 10.0.6 | 254 | 4/5/2026 |
| 10.0.5 | 389 | 1/31/2026 |
| 10.0.4 | 149 | 1/31/2026 |
| 10.0.3 | 150 | 1/31/2026 |
| 10.0.2 | 147 | 1/31/2026 |
| 10.0.1 | 157 | 1/31/2026 |
| 10.0.0 | 189 | 11/29/2025 |
| 1.0.5 | 303 | 7/13/2025 |
| 1.0.4 | 503 | 6/9/2025 |
| 1.0.3 | 242 | 6/1/2025 |
| 1.0.2 | 301 | 4/22/2025 |
| 1.0.1 | 271 | 4/22/2025 |
| 1.0.0 | 246 | 4/21/2025 |