Optima.Net.Decisioning
1.0.0
Prefix Reserved
See the version list below for details.
dotnet add package Optima.Net.Decisioning --version 1.0.0
NuGet\Install-Package Optima.Net.Decisioning -Version 1.0.0
<PackageReference Include="Optima.Net.Decisioning" Version="1.0.0" />
<PackageVersion Include="Optima.Net.Decisioning" Version="1.0.0" />
<PackageReference Include="Optima.Net.Decisioning" />
paket add Optima.Net.Decisioning --version 1.0.0
#r "nuget: Optima.Net.Decisioning, 1.0.0"
#:package Optima.Net.Decisioning@1.0.0
#addin nuget:?package=Optima.Net.Decisioning&version=1.0.0
#tool nuget:?package=Optima.Net.Decisioning&version=1.0.0
Optima.Net.Decisioning
Optima.Net.Decisioning is a minimal, immutable, framework‑agnostic representation of what a system decided and why — independent of how the decision was reached. It serves as the authoritative record of a decision outcome, not as an execution engine, workflow system, or evaluation framework.
Principles
Decisioning is built on these core tenets:
- Outcome‑centric, not process‑centric.
- Immutable facts — once created, decisions cannot be changed.
- Framework‑agnostic — no dependency on domain, infrastructure, or orchestration systems.
- Semantic clarity — decisions record meaning, not control flow.
- Audit‑friendly — explicit metadata and evidence for traceability.
Key Concepts
Decision<TIntent, TResult>
Represents a completed decision, capturing:
- Intent — what was attempted.
- Outcome — what was decided.
- Result — produced value, if any.
- Evidence — optional diagnostics or context.
- Metadata — audit information (timestamp, actor, correlation).
- Negotiation — optional negotiated outcome.
DecisionOutcome
Semantic classification of the decision result:
public enum DecisionOutcome
{
Approved,
Rejected,
CounterProposed,
Deferred,
Escalated,
Indeterminate
}
DecisionMetadata
Contextual information that accompanies every decision:
public sealed record DecisionMetadata
{
public DateTimeOffset Timestamp { get; init; } = DateTimeOffset.UtcNow;
public Optional<string> CorrelationId { get; init; }
public Optional<string> Actor { get; init; }
public Optional<string> Source { get; init; }
}
INegotiationOutcome
Used to record the result of a negotiation without performing it:
public interface INegotiationOutcome
{
NegotiationDisposition Disposition { get; }
Optional<object> Proposal { get; }
IReadOnlyCollection<object> Evidence { get; }
}
Example Domain Types
These classes are domain examples used to demonstrate how Decisioning can model real problems. These belong to your application layer, not the Decisioning library itself.
Intent
public sealed record InsuranceClaimIntent(
string ClaimId,
string PolicyNumber,
decimal ClaimedAmount,
string Reason);
Result / Proposal
public sealed record ClaimSettlementProposal(
string ClaimId,
decimal ApprovedAmount,
string SettlementNotes);
Negotiation Outcome Implementation
public sealed class NegotiationOutcome : INegotiationOutcome
{
public NegotiationDisposition Disposition { get; }
public Optional<object> Proposal { get; }
public IReadOnlyCollection<object> Evidence { get; }
public NegotiationOutcome(
NegotiationDisposition disposition,
Optional<object> proposal,
IReadOnlyCollection<object> evidence)
{
Disposition = disposition;
Proposal = proposal;
Evidence = evidence;
}
}
Handling Decisions — Example Handler
This handler shows how you might produce a Decision<TIntent, TResult> in an application flow. It evaluates incoming intents, applies domain logic, and returns immutable decisions.
public static class InsuranceClaimDecisionHandler
{
public static Decision<InsuranceClaimIntent, ClaimSettlementProposal> Handle(
InsuranceClaimIntent intent)
{
var metadata = new DecisionMetadata
{
Actor = Optional<string>.Some("Claims.Adjudicator.System"),
Source = Optional<string>.Some("Optima.Net.Decisioning.Handler"),
CorrelationId = Optional<string>.Some(Guid.NewGuid().ToString())
};
// Negotiation example
if (intent.ClaimedAmount == 12000m &&
intent.Reason.Contains("hail", StringComparison.OrdinalIgnoreCase))
{
var counterProposal = new ClaimSettlementProposal(
intent.ClaimId,
9000m,
"Partial approval under hail coverage clause #HAIL-2025.");
var negotiation = Optional<INegotiationOutcome>.Some(
new NegotiationOutcome(
NegotiationDisposition.Modified,
Optional<object>.Some(counterProposal),
new List<object>
{
"Customer initially requested $12,000 for full roof replacement.",
"Coverage capped at $9,000 under policy tier Silver.",
"Customer accepted counterproposal."
}));
var evidence = Optional<IReadOnlyCollection<object>>.Some(
new List<object> { "Negotiation concluded with partial payout agreement." });
return new Decision<InsuranceClaimIntent, ClaimSettlementProposal>(
intent,
DecisionOutcome.CounterProposed,
Optional<ClaimSettlementProposal>.Some(counterProposal),
evidence,
metadata,
negotiation);
}
// Deferred: high-value claim
if (intent.ClaimedAmount > 10000m)
{
var evidence = Optional<IReadOnlyCollection<object>>.Some(new List<object>
{
"Claim amount exceeds auto-approval threshold ($10,000).",
"Manual review required under risk policy #RC-2025.",
"No prior authorization found for high-value claim."
});
return new Decision<InsuranceClaimIntent, ClaimSettlementProposal>(
intent,
DecisionOutcome.Deferred,
Optional<ClaimSettlementProposal>.None(),
evidence,
metadata,
Optional<INegotiationOutcome>.None());
}
// Rejected: flood exclusion
if (intent.Reason.Contains("flood", StringComparison.OrdinalIgnoreCase))
{
var evidence = Optional<IReadOnlyCollection<object>>.Some(new List<object>
{
"Flood damage not covered under current policy tier.",
"Refer to exclusion clause #17B."
});
return new Decision<InsuranceClaimIntent, ClaimSettlementProposal>(
intent,
DecisionOutcome.Rejected,
Optional<ClaimSettlementProposal>.None(),
evidence,
metadata,
Optional<INegotiationOutcome>.None());
}
// Approved
var settlement = new ClaimSettlementProposal(
intent.ClaimId,
intent.ClaimedAmount * 0.9m,
"Approved under standard coverage terms.");
return new Decision<InsuranceClaimIntent, ClaimSettlementProposal>(
intent,
DecisionOutcome.Approved,
Optional<ClaimSettlementProposal>.Some(settlement),
Optional<IReadOnlyCollection<object>>.None(),
metadata,
Optional<INegotiationOutcome>.None());
}
}
Usage Summary
- Construct an intent object from your application logic.
- Invoke your handler to produce a
Decision. - The
Decisionobject is immutable, semantically meaningful, and suitable for:- Auditing
- Logging
- Persistence
- Event publication
- Analytical inspection
Decisioning does not execute business logic — that belongs in your domain or application layer.
This project embodies a clear separation of concerns: your system decides — Decisioning records the outcome.
Optional fields explicitly represent presence or absence using Optional<T> from Optima.Net.
| 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 was computed. 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
- Optima.Net (>= 1.0.8)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
RELEASENOTES.md