Appouse.ApprovalOrchestrator
1.0.0
dotnet add package Appouse.ApprovalOrchestrator --version 1.0.0
NuGet\Install-Package Appouse.ApprovalOrchestrator -Version 1.0.0
<PackageReference Include="Appouse.ApprovalOrchestrator" Version="1.0.0" />
<PackageVersion Include="Appouse.ApprovalOrchestrator" Version="1.0.0" />
<PackageReference Include="Appouse.ApprovalOrchestrator" />
paket add Appouse.ApprovalOrchestrator --version 1.0.0
#r "nuget: Appouse.ApprovalOrchestrator, 1.0.0"
#:package Appouse.ApprovalOrchestrator@1.0.0
#addin nuget:?package=Appouse.ApprovalOrchestrator&version=1.0.0
#tool nuget:?package=Appouse.ApprovalOrchestrator&version=1.0.0
🔐 Appouse.ApprovalOrchestrator
Generic, dynamic approval workflow orchestration library for .NET applications.
Herhangi bir .NET uygulamasına entegre edilebilen, entity-agnostic, generic, çok adımlı, strateji destekli bir onay/approval mekanizması kütüphanesi.
✨ Özellikler
| Özellik | Açıklama |
|---|---|
| 🎯 Generic Yapı | IApprovalEngine<TEntity> — her entity tipi için bağımsız onay akışı |
| 🔄 Çoklu Strateji | Sequential, Parallel, Majority, Any, All |
| 📊 9 Durum | Draft → Pending → InReview → Approved / Rejected / Cancelled / Recalled / Escalated / Expired |
| 🔍 Query/Check API | IsApprovedAsync(), IsPendingAsync(), CanUserApproveAsync(), filtreli sorgu, pagination |
| 👥 Delegasyon | Onay adımını başka kullanıcıya devretme |
| ⬆️ Eskalasyon | Onay talebini üst makama taşıma |
| ⏰ Süre Aşımı | Otomatik expiry ile background service |
| 📸 Entity Snapshot | Submit anında entity'nin JSON kopyası |
| 📋 Dashboard | Özet istatistikler, kategori/entity/workflow bazlı dağılım |
| 🔌 Event System | IApprovalEventHandler<TEntity> — bildirim, email, message bus entegrasyonu |
| 💾 Pluggable Storage | InMemory (varsayılan) + EF Core (relational DB desteği) |
| ⚙️ Fluent Builder | AddApprovalOrchestrator<T>().WithStepResolver<R>().WithPolicy<P>() |
📦 Paketler
| Paket | Açıklama |
|---|---|
Appouse.ApprovalOrchestrator |
Core kütüphane — abstractions, engine, in-memory store |
Appouse.ApprovalOrchestrator.EntityFrameworkCore |
EF Core persistence provider |
🚀 Hızlı Başlangıç
1. NuGet Kurulumu
dotnet add package Appouse.ApprovalOrchestrator
dotnet add package Appouse.ApprovalOrchestrator.EntityFrameworkCore # Opsiyonel — DB persistence
2. DI Kaydı (Program.cs)
// En basit kullanım — InMemory store, default policy
builder.Services.AddApprovalOrchestrator<PurchaseOrder>();
// Tam konfigürasyon — EF Core, custom step resolver, policy, event handler
builder.Services
.AddApprovalOrchestrator<PurchaseOrder>(options =>
{
options.DefaultExpiration = TimeSpan.FromDays(7);
options.AllowSelfApproval = false;
options.RequireCommentOnReject = true;
options.SnapshotEntityOnSubmit = true;
})
.WithStepResolver<PurchaseOrderStepResolver>()
.WithPolicy<PurchaseOrderApprovalPolicy>()
.WithEventHandler<PurchaseOrderApprovalEvents>()
.UseEntityFrameworkCore(options =>
{
options.UseSqlServer(connectionString);
});
3. Onay Talebi Oluştur ve Gönder
public class PurchaseOrderService
{
private readonly IApprovalEngine<PurchaseOrder> _engine;
private readonly IApprovalQueryService<PurchaseOrder> _queries;
public PurchaseOrderService(
IApprovalEngine<PurchaseOrder> engine,
IApprovalQueryService<PurchaseOrder> queries)
{
_engine = engine;
_queries = queries;
}
public async Task<ApprovalRequest<PurchaseOrder>> SubmitForApprovalAsync(PurchaseOrder order)
{
return await _engine.CreateAndSubmitAsync(
entity: order,
entityId: order.Id.ToString(),
requestedBy: order.RequestedBy,
workflowName: "PurchaseOrderApproval",
category: order.Department,
priority: order.Amount > 50_000 ? 1 : 0);
}
public async Task<bool> IsOrderApprovedAsync(string orderId)
{
return await _queries.IsApprovedAsync(orderId);
}
}
📖 Detaylı Kullanım
Onay Yaşam Döngüsü (State Machine)
┌─────────┐ Submit() ┌───────────┐
│ Draft │───────────────>│ InReview │
└─────────┘ └─────┬─────┘
^ │
│ Recall() ┌────────┼────────┐
│ │ │ │
┌─────────┐ ┌─────▼──┐ ┌──▼────┐ ┌─▼────────┐
│Recalled │ │Approved│ │Rejected│ │Escalated │
└─────────┘ └────────┘ └───────┘ └──────────┘
│
┌────────┐ ┌───────┐ │
│Cancelled│ │Expired│ ◄──┘
└────────┘ └───────┘
Onay Stratejileri
public enum ApprovalStrategy
{
Sequential, // Adım adım — her adım bitmeden sonraki başlamaz
Parallel, // Tümü aynı anda — hepsi onaylamalı
Majority, // %50+1 onay yeterli
Any, // Tek bir onay yeterli
All // Tümü onaylamalı (paralel aktivasyon)
}
Strateji seçimi dinamik yapılabilir:
public class MyPolicy : IApprovalPolicy<PurchaseOrder>
{
public Task<ApprovalStrategy> DetermineStrategyAsync(PurchaseOrder entity, CancellationToken ct)
{
// Komite kararı gereken konular için Majority
if (entity.Amount > 100_000)
return Task.FromResult(ApprovalStrategy.Majority);
// Normal akış — sıralı onay
return Task.FromResult(ApprovalStrategy.Sequential);
}
// ... diğer metotlar
}
Dinamik Adım Çözümleme (Step Resolver)
Step resolver, entity'nin durumuna göre dinamik olarak onay adımlarını belirler:
public class PurchaseOrderStepResolver : IApprovalStepResolver<PurchaseOrder>
{
public Task<List<ApprovalStep>> ResolveStepsAsync(
PurchaseOrder entity, string entityId, ApprovalContext context, CancellationToken ct)
{
var steps = new List<ApprovalStep>();
// Tutar bazlı dinamik adımlar
steps.Add(new ApprovalStep
{
StepName = "Yönetici Onayı",
AssignedTo = $"manager-{entity.Department}",
AssignedRole = "Manager"
});
if (entity.Amount > 10_000)
{
steps.Add(new ApprovalStep
{
StepName = "Direktör Onayı",
AssignedTo = $"director-{entity.Department}",
AssignedRole = "Director"
});
}
if (entity.Amount > 100_000)
{
steps.Add(new ApprovalStep
{
StepName = "CFO Onayı",
AssignedTo = "cfo",
AssignedRole = "CFO"
});
}
return Task.FromResult(steps);
}
}
Onay İşlemleri (Engine)
// Oluştur + Gönder (tek adımda)
var request = await _engine.CreateAndSubmitAsync(entity, entityId, requestedBy);
// Adım onayla
await _engine.ApproveStepAsync(new ApprovalDecisionRequest
{
ApprovalRequestId = requestId,
StepId = stepId,
DecidedBy = "manager1",
Comment = "Onaylandı"
});
// Adım reddet
await _engine.RejectStepAsync(new ApprovalDecisionRequest
{
ApprovalRequestId = requestId,
StepId = stepId,
DecidedBy = "manager1",
Comment = "Bütçe aşımı"
});
// Opsiyonel adım atla
await _engine.SkipStepAsync(requestId, stepId, "admin");
// Delegasyon
await _engine.DelegateStepAsync(requestId, stepId, "manager1", "manager2");
// Eskalasyon
await _engine.EscalateAsync(requestId, "manager1", "director1", "Acil onay gerekli");
// İptal
await _engine.CancelAsync(requestId, "admin", "Talep geçersiz");
// Geri çekme (sadece talep eden yapabilir)
await _engine.RecallAsync(requestId, requestedBy);
Query/Check API — Onay Durumu Sorgulama
// ── Hızlı Durum Kontrolleri ──
bool isApproved = await _queries.IsApprovedAsync(entityId);
bool isPending = await _queries.IsPendingAsync(entityId);
bool isRejected = await _queries.IsRejectedAsync(entityId);
bool hasActive = await _queries.HasActiveRequestAsync(entityId);
var status = await _queries.GetStatusAsync(entityId); // ApprovalStatus?
bool canApprove = await _queries.CanUserApproveAsync(requestId, userId);
// ── Listeler ──
var myPending = await _queries.GetPendingForUserAsync(userId); // Benim onayımı bekleyenler
var myRequests = await _queries.GetMyRequestsAsync(userId); // Benim taleplerim
var overdue = await _queries.GetOverdueAsync(); // Süresi geçenler
var expiringSoon = await _queries.GetExpiringAsync(TimeSpan.FromHours(24)); // 24 saat içinde dolacaklar
// ── Filtreli Sorgu (Pagination + Sort) ──
var result = await _queries.QueryAsync(new ApprovalFilter
{
Status = ApprovalStatus.InReview,
Category = "IT",
RequestedBy = "user1",
MinPriority = 1,
CreatedAfter = DateTime.UtcNow.AddDays(-30),
PageNumber = 1,
PageSize = 20,
SortBy = "CreatedAt",
SortDescending = true
});
// result.Items — sayfa öğeleri
// result.TotalCount — toplam kayıt
// result.TotalPages — toplam sayfa
// result.HasNextPage / HasPreviousPage
// ── Dashboard Özeti ──
var summary = await _queries.GetSummaryAsync(userId);
// summary.TotalPending, TotalApproved, TotalRejected, AssignedToMe, OverdueCount
// summary.ByCategory, ByEntityType, ByWorkflow — dağılım sözlükleri
// ── Adım Geçmişi (Audit Trail) ──
var steps = await _queries.GetStepHistoryAsync(requestId);
Event Handler — Bildirim Entegrasyonu
public class EmailApprovalHandler : IApprovalEventHandler<PurchaseOrder>
{
private readonly IEmailService _emailService;
public EmailApprovalHandler(IEmailService emailService) => _emailService = emailService;
public async Task OnSubmittedAsync(ApprovalRequest<PurchaseOrder> request, CancellationToken ct)
{
var step = request.CurrentStep;
if (step != null)
{
await _emailService.SendAsync(
to: step.EffectiveAssignee,
subject: $"Yeni Onay Talebi: {request.EntityId}",
body: $"Merhaba, {request.RequestedBy} tarafından gönderilen talep onayınızı bekliyor.");
}
}
public async Task OnApprovedAsync(ApprovalRequest<PurchaseOrder> request, CancellationToken ct)
{
await _emailService.SendAsync(
to: request.RequestedBy,
subject: $"Talebiniz Onaylandı: {request.EntityId}",
body: "Satın alma talebiniz tüm adımlardan geçerek onaylanmıştır.");
}
// ... diğer event'ler benzer şekilde
public Task OnStepCompletedAsync(ApprovalRequest<PurchaseOrder> r, ApprovalStep s, CancellationToken ct) => Task.CompletedTask;
public Task OnRejectedAsync(ApprovalRequest<PurchaseOrder> r, CancellationToken ct) => Task.CompletedTask;
public Task OnCancelledAsync(ApprovalRequest<PurchaseOrder> r, CancellationToken ct) => Task.CompletedTask;
public Task OnEscalatedAsync(ApprovalRequest<PurchaseOrder> r, CancellationToken ct) => Task.CompletedTask;
public Task OnExpiredAsync(ApprovalRequest<PurchaseOrder> r, CancellationToken ct) => Task.CompletedTask;
public Task OnDelegatedAsync(ApprovalRequest<PurchaseOrder> r, ApprovalStep s, CancellationToken ct) => Task.CompletedTask;
public Task OnRecalledAsync(ApprovalRequest<PurchaseOrder> r, CancellationToken ct) => Task.CompletedTask;
}
Approval Policy — İş Kuralları
public class PurchaseOrderPolicy : IApprovalPolicy<PurchaseOrder>
{
// 100₺ altı onay gerektirmez
public Task<bool> RequiresApprovalAsync(PurchaseOrder entity, CancellationToken ct)
=> Task.FromResult(entity.Amount >= 100);
// Self-approval kontrolü
public Task<bool> CanSubmitAsync(PurchaseOrder entity, string requestedBy, CancellationToken ct)
=> Task.FromResult(true);
// Rol bazlı onay yetkisi
public Task<bool> CanApproveAsync(
ApprovalRequest<PurchaseOrder> request, ApprovalStep step, string userId, CancellationToken ct)
=> Task.FromResult(step.AssignedRole != null); // Rol varsa herkes onaylayabilir
// Tutar bazlı strateji
public Task<ApprovalStrategy> DetermineStrategyAsync(PurchaseOrder entity, CancellationToken ct)
=> Task.FromResult(entity.Amount > 100_000 ? ApprovalStrategy.Majority : ApprovalStrategy.Sequential);
// Tutar bazlı expire süresi
public Task<TimeSpan?> GetExpirationAsync(PurchaseOrder entity, CancellationToken ct)
=> Task.FromResult<TimeSpan?>(entity.Amount switch
{
< 1_000 => TimeSpan.FromDays(3),
< 10_000 => TimeSpan.FromDays(7),
_ => TimeSpan.FromDays(14)
});
}
🏗️ Çoklu Entity Desteği
Tek uygulamada birden fazla entity tipi için onay sistemi kaydedilebilir:
// Satın Alma Onayı
builder.Services
.AddApprovalOrchestrator<PurchaseOrder>(opt => opt.RequireCommentOnReject = true)
.WithStepResolver<PurchaseOrderStepResolver>()
.WithPolicy<PurchaseOrderPolicy>();
// İzin Talebi Onayı
builder.Services
.AddApprovalOrchestrator<LeaveRequest>(opt => opt.AllowSelfApproval = false)
.WithStepResolver<LeaveRequestStepResolver>()
.WithPolicy<LeaveRequestPolicy>();
// Belge Onayı
builder.Services
.AddApprovalOrchestrator<Document>()
.WithStepResolver<DocumentStepResolver>()
.WithEventHandler<DocumentApprovalNotifier>();
Her entity tipi kendi bağımsız engine, store, policy ve event handler'ına sahiptir.
🗄️ Persistence
InMemory Store (Varsayılan)
// Ekstra bir şey yapmaya gerek yok — varsayılan olarak InMemory kullanılır
builder.Services.AddApprovalOrchestrator<PurchaseOrder>();
⚠️ InMemory store uygulama yeniden başlatıldığında verileri kaybeder. Test ve prototipleme için uygundur.
EF Core (SQL Server, PostgreSQL, MySQL, Oracle, SQLite)
// SQL Server
.UseEntityFrameworkCore(options => options.UseSqlServer(connectionString));
// PostgreSQL
.UseEntityFrameworkCore(options => options.UseNpgsql(connectionString));
// MySQL
.UseEntityFrameworkCore(options => options.UseMySql(connectionString, ServerVersion.AutoDetect(connectionString)));
// SQLite (test)
.UseEntityFrameworkCore(options => options.UseSqlite("Data Source=approvals.db"));
// InMemory EF Core (test)
.UseEntityFrameworkCore(options => options.UseInMemoryDatabase("TestDb"));
EF Core Migration
dotnet ef migrations add InitialApprovals --project src/Appouse.ApprovalOrchestrator.EntityFrameworkCore
dotnet ef database update --project src/Appouse.ApprovalOrchestrator.EntityFrameworkCore
Custom Store
Kendi persistence mekanizmanızı yazabilirsiniz:
public class RedisApprovalStore<TEntity> : IApprovalStore<TEntity> where TEntity : class
{
// Redis implementasyonu
}
builder.Services
.AddApprovalOrchestrator<PurchaseOrder>()
.WithStore<RedisApprovalStore<PurchaseOrder>>();
⚙️ Konfigürasyon Referansı
builder.Services.AddApprovalOrchestrator<TEntity>(options =>
{
// Varsayılan expire süresi (null = expire yok)
options.DefaultExpiration = TimeSpan.FromDays(7);
// Otomatik expire background service
options.AutoExpireEnabled = true;
options.AutoExpireCheckInterval = TimeSpan.FromMinutes(30);
// Self-approval izni
options.AllowSelfApproval = false;
// Ret'te yorum zorunluluğu
options.RequireCommentOnReject = true;
// Submit anında entity snapshot'ı
options.SnapshotEntityOnSubmit = true;
// Maksimum delegasyon derinliği
options.MaxDelegationDepth = 3;
// Event handler hatalarında exception fırlatılsın mı
options.ThrowOnEventHandlerFailure = false;
});
🎯 Kullanım Senaryoları
| Senaryo | Entity Tipi | Step Resolver Mantığı |
|---|---|---|
| Satın Alma Onayı | PurchaseOrder |
Tutara göre Manager → Director → CFO |
| İzin Talebi | LeaveRequest |
Süreye göre Manager → HR |
| Belge Onayı | Document |
Gizlilik seviyesine göre adımlar |
| ITSM Değişiklik Yönetimi | ChangeRequest |
Risk seviyesine göre CAB → Director |
| Masraf Beyanı | ExpenseReport |
Tutara göre Manager → Finance |
| Proje Teklifi | ProjectProposal |
Bütçeye göre PMO → VP → C-Level |
| Kredi Başvurusu | LoanApplication |
Skora göre Analyst → Supervisor → Committee |
| İnsan Kaynakları | PromotionRequest |
Pozisyona göre Manager → HR → VP |
| Sözleşme Yönetimi | Contract |
Tutara göre Legal → Finance → CEO |
| Yayınlama | ContentPublish |
İçerik tipine göre Editor → Chief Editor |
🧪 Test
# Unit testleri çalıştır
dotnet test Appouse.ApprovalOrchestrator.sln
# Detaylı çıktı
dotnet test --verbosity normal --logger "trx"
📂 Proje Yapısı
ApprovalOrchestrator/
├── src/
│ ├── Appouse.ApprovalOrchestrator/ # Core library
│ │ ├── Abstractions/ # Interface tanımları
│ │ │ ├── IApprovalEngine.cs # Workflow engine
│ │ │ ├── IApprovalQueryService.cs # Query/Check API
│ │ │ ├── IApprovalStore.cs # Persistence soyutlaması
│ │ │ ├── IApprovalStepResolver.cs # Dinamik adım çözücü
│ │ │ ├── IApprovalPolicy.cs # İş kuralları
│ │ │ └── IApprovalEventHandler.cs # Event callback
│ │ ├── Configuration/
│ │ │ └── ApprovalOrchestratorOptions.cs # Konfigürasyon
│ │ ├── Defaults/
│ │ │ ├── DefaultApprovalPolicy.cs # Varsayılan politika
│ │ │ ├── DefaultApprovalStepResolver.cs # Varsayılan resolver
│ │ │ ├── InMemoryApprovalStore.cs # InMemory store
│ │ │ └── NullApprovalEventHandler.cs # No-op event handler
│ │ ├── Engine/
│ │ │ ├── ApprovalEngine.cs # Ana engine implementasyonu
│ │ │ └── ApprovalQueryService.cs # Query service impl.
│ │ ├── Enums/
│ │ │ ├── ApprovalStatus.cs # 9 durum
│ │ │ ├── ApprovalStrategy.cs # 5 strateji
│ │ │ └── StepDecision.cs # Adım kararları
│ │ ├── Exceptions/
│ │ │ ├── ApprovalException.cs
│ │ │ ├── ApprovalNotFoundException.cs
│ │ │ ├── InvalidApprovalTransitionException.cs
│ │ │ └── UnauthorizedApprovalException.cs
│ │ ├── Extensions/
│ │ │ └── ServiceCollectionExtensions.cs # DI builder
│ │ ├── Background/
│ │ │ └── ApprovalExpirationHostedService.cs # Auto-expire service
│ │ └── Models/
│ │ ├── ApprovalRequest.cs # Ana model
│ │ ├── ApprovalStep.cs # Adım modeli
│ │ ├── ApprovalContext.cs # Kontekst
│ │ ├── ApprovalDecisionRequest.cs # Karar DTO
│ │ ├── ApprovalFilter.cs # Filtre modeli
│ │ ├── ApprovalSummary.cs # Dashboard özeti
│ │ └── PagedResult.cs # Sayfalı sonuç
│ │
│ └── Appouse.ApprovalOrchestrator.EntityFrameworkCore/ # EF Core persistence
│ ├── ApprovalDbContext.cs
│ ├── Entities/
│ ├── Extensions/
│ └── Stores/EfCoreApprovalStore.cs
│
├── samples/
│ └── Appouse.ApprovalOrchestrator.Sample.WebApi/ # Örnek Web API
│ ├── Controllers/PurchaseOrdersController.cs
│ ├── Models/PurchaseOrder.cs
│ ├── Workflows/
│ │ ├── PurchaseOrderStepResolver.cs
│ │ ├── PurchaseOrderPolicy.cs
│ │ └── PurchaseOrderApprovalEvents.cs
│ └── Program.cs
│
├── tests/
│ └── Appouse.ApprovalOrchestrator.UnitTests/
│ ├── ApprovalEngineTests.cs
│ ├── ApprovalQueryServiceTests.cs
│ └── ApprovalStrategyTests.cs
│
├── Appouse.ApprovalOrchestrator.sln
├── Directory.Build.props
├── Directory.Packages.props
├── pack.bat
├── LICENSE
└── README.md
🔧 Sample Web API Çalıştırma
cd samples/Appouse.ApprovalOrchestrator.Sample.WebApi
dotnet run
Swagger UI: http://localhost:5000 (veya otomatik atanan port)
Örnek API Akışı
1. Satın Alma Talebi Oluştur:
curl -X POST http://localhost:5000/api/purchase-orders \
-H "Content-Type: application/json" \
-d '{
"title": "Yeni Sunucu",
"description": "Geliştirme ortamı için sunucu",
"amount": 15000,
"currency": "TRY",
"department": "IT",
"requestedBy": "user1",
"vendor": "Dell"
}'
2. Onay Durumunu Kontrol Et:
curl http://localhost:5000/api/purchase-orders/{entityId}/status
curl http://localhost:5000/api/purchase-orders/{entityId}/is-approved
3. İlk Adımı Onayla (Manager):
curl -X POST http://localhost:5000/api/purchase-orders/{requestId}/approve \
-H "Content-Type: application/json" \
-d '{
"stepId": "{stepId}",
"decidedBy": "manager-it",
"comment": "Uygun bulundu"
}'
4. İkinci Adımı Onayla (Director):
curl -X POST http://localhost:5000/api/purchase-orders/{requestId}/approve \
-H "Content-Type: application/json" \
-d '{
"stepId": "{stepId}",
"decidedBy": "director-it",
"comment": "Onaylandı"
}'
5. Dashboard Özeti:
curl http://localhost:5000/api/approvals/summary?userId=user1
6. Bekleyen Onaylar:
curl http://localhost:5000/api/approvals/pending/manager-it
📋 Sık Sorulan Sorular (FAQ)
<details> <summary><strong>Tek adımlı basit onay için ne yapmalıyım?</strong></summary>
Default step resolver otomatik olarak tek adımlı bir onay oluşturur. Hiçbir özelleştirme yapmanıza gerek yok:
builder.Services.AddApprovalOrchestrator<MyEntity>();
</details>
<details> <summary><strong>Approval gerektirmeyen entity'leri nasıl handle ederim?</strong></summary>
IApprovalPolicy<TEntity>.RequiresApprovalAsync() metodunda false döndürün — engine otomatik olarak Approved durumuna geçer:
public Task<bool> RequiresApprovalAsync(PurchaseOrder entity, CancellationToken ct)
=> Task.FromResult(entity.Amount >= 100); // 100₺ altı otomatik onay
</details>
<details> <summary><strong>Aynı entity için birden fazla aktif talep olabilir mi?</strong></summary>
Evet, varsayılan olarak olabilir. Bunu engellemek için IApprovalPolicy.CanSubmitAsync() içinde kontrol ekleyebilirsiniz:
public async Task<bool> CanSubmitAsync(PurchaseOrder entity, string requestedBy, CancellationToken ct)
{
var hasActive = await _queries.HasActiveRequestAsync(entity.Id.ToString(), ct);
return !hasActive; // Aktif talep varsa yeni talep açılamaz
}
</details>
<details> <summary><strong>Metadata alanını ne için kullanabilirim?</strong></summary>
Hem ApprovalRequest hem ApprovalStep üzerinde Dictionary<string, string> Metadata alanı mevcuttur. IP adresi, session ID, departman kodu, external reference gibi özel verileri saklamak için kullanabilirsiniz.
</details>
<details> <summary><strong>Custom store nasıl yazarım?</strong></summary>
IApprovalStore<TEntity> interface'ini implement edin (Redis, MongoDB, Cassandra vb.):
public class MongoApprovalStore<TEntity> : IApprovalStore<TEntity> where TEntity : class
{
// MongoDB implementasyonu
}
builder.Services.AddApprovalOrchestrator<T>().WithStore<MongoApprovalStore<T>>();
</details>
📄 Lisans
MIT License — Detaylar için LICENSE dosyasına bakın.
🤝 Katkıda Bulunma
- Fork edin
- Feature branch oluşturun (
git checkout -b feature/amazing-feature) - Commit atın (
git commit -m 'feat: add amazing feature') - Branch'e push edin (
git push origin feature/amazing-feature) - Pull Request açın
Appouse — Enterprise-grade .NET libraries 🚀
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 is compatible. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 was computed. 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. |
-
net8.0
- Microsoft.Extensions.DependencyInjection.Abstractions (>= 9.0.4)
- Microsoft.Extensions.Hosting.Abstractions (>= 9.0.4)
- Microsoft.Extensions.Logging.Abstractions (>= 9.0.4)
- Microsoft.Extensions.Options (>= 9.0.4)
- System.Text.Json (>= 9.0.4)
-
net9.0
- Microsoft.Extensions.DependencyInjection.Abstractions (>= 9.0.4)
- Microsoft.Extensions.Hosting.Abstractions (>= 9.0.4)
- Microsoft.Extensions.Logging.Abstractions (>= 9.0.4)
- Microsoft.Extensions.Options (>= 9.0.4)
- System.Text.Json (>= 9.0.4)
NuGet packages (1)
Showing the top 1 NuGet packages that depend on Appouse.ApprovalOrchestrator:
| Package | Downloads |
|---|---|
|
Appouse.ApprovalOrchestrator.EntityFrameworkCore
Entity Framework Core persistence provider for Appouse.ApprovalOrchestrator. Provides EF Core-based storage with full query support. |
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 1.0.0 | 124 | 4/10/2026 |