Optima.Net.Domain 3.1.3

Prefix Reserved
dotnet add package Optima.Net.Domain --version 3.1.3
                    
NuGet\Install-Package Optima.Net.Domain -Version 3.1.3
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Optima.Net.Domain" Version="3.1.3" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Optima.Net.Domain" Version="3.1.3" />
                    
Directory.Packages.props
<PackageReference Include="Optima.Net.Domain" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add Optima.Net.Domain --version 3.1.3
                    
#r "nuget: Optima.Net.Domain, 3.1.3"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package Optima.Net.Domain@3.1.3
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=Optima.Net.Domain&version=3.1.3
                    
Install as a Cake Addin
#tool nuget:?package=Optima.Net.Domain&version=3.1.3
                    
Install as a Cake Tool

Optima.Net.Domain

Optima.Net.Domain is a lightweight, framework-agnostic toolkit for expressing business rules, decisions, and outcomes in a clear, auditable, and scalable Domain-Driven Design (DDD) style.

As we all know Events are a big part of DDD and you can find support for events in Optima.Net.Events (separate NuGet package) If you do not like dealing with nulls then check out Optima.Net (separate NuGet package) to Implement a light version of Optional<T>, Result<T> and Result<T, TError>

It does not try to be a framework. Instead, it provides a small, explicit set of primitives that scale from simple business rules to finance-grade decision systems.

This document is both:

  • a comprehensive reference, and
  • a guided tutorial for engineers new to decision-centric domain modelling.

If you read it top-to-bottom, you should be able to confidently design Specifications, Policies, Diagnostics, and Application flows without burying business logic in if statements.


Table of Contents


Motivation and Mental Model

In many systems, business rules end up:

  • buried in nested if statements,
  • duplicated across services,
  • tightly coupled to infrastructure,
  • or impossible to explain after the fact.

Optima.Net.Domain exists to fix this by making rules and decisions explicit.

Mental Model

Concept Purpose Question Answered
Specification Fact Is this true?
Policy Permission May we proceed?
Diagnostics Explanation Why did this fail?
Application Flow What should happen next?

Each concept has one job.


Core Concepts Overview

Specifications

  • Express facts
  • Pure and side-effect free
  • Independently testable
  • Composable

Policies

Policies no longer construct failures or return evaluation results.

  • Express business intent
  • Declare whether progression is allowed, and
  • Declare what is permitted after failure via PolicyFailureSemantics.
  • Do not orchestrate

Evaluation, diagnostics, and failure projection are performed externally by a PolicyDiagnosticEvaluator.

Diagnostics

  • Observe evaluation
  • Immutable and structured
  • Explain failures without changing behaviour

Application Layer

  • Coordinates flow
  • Owns retries, compensation, escalation
  • Never enforces business truth

Specifications

A specification answers one factual question.

Example Domain: Orders

public sealed class Order
{
    private readonly List<OrderLine> _lines = new();

    public IReadOnlyCollection<OrderLine> Lines => _lines;
    public decimal Total => _lines.Sum(l => l.Price);

    public void AddLine(OrderLine line) => _lines.Add(line);
}
//if the discountThreshold were set in stone then you could do this
public sealed class OrderDiscountSpec : ISpecification<Order>
{
    private const decimal DiscountThreshold = 500m;

    public bool IsSatisfiedBy(Order order)
        => order.Total > DiscountThreshold;
}

//if the discountThreshold is variable then you can do this

public sealed record OrderDiscountSpec(decimal DiscountThreshold)
    : ISpecification<Order>
{
    public OrderDiscountSpec(decimal discountThreshold)
        : this(Validate(discountThreshold))
    {
    }

    public bool IsSatisfiedBy(Order order)
        => order.Total > DiscountThreshold;

    private static decimal Validate(decimal value)
    {
        if (value <= 0)
            throw new ArgumentOutOfRangeException(
                nameof(value),
                "Discount threshold must be greater than zero.");

        return value;
    }
}

Each specification:

  • answers one question
  • has no business intent
  • has no side effects

IPolicyJustification

A Policy Justification defines which facts must hold for a specific policy
to be considered satisfied.

It exists to answer a single question:

“Why is this policy satisfied or not?”

A policy justification:

  • belongs to the domain

  • is defined per policy

  • lists the specifications that justify that policy

  • contains no evaluation logic

  • contains no business intent

The evaluator uses a policy justification to:

  • evaluate the required facts,

  • bind those facts to a specific policy,

  • produce diagnostics that explain which facts failed.

Policies themselves:

  • do not evaluate specifications,

  • do not construct failures,

  • do not explain outcomes.

They only declare:

  • whether progression is allowed, and

  • what is permitted after failure via PolicyFailureSemantics.


Mental model

  • SpecificationIs this fact true?

  • PolicyMay we proceed if the facts hold?

  • Policy JustificationWhich facts justify this policy?

  • DiagnosticsWhich facts failed, and why?

public sealed class OrderDiscountPolicyJustification: IPolicyJustification<Order>
{
    public string PolicyName => nameof(OrderDiscountPolicy);

    public IReadOnlyCollection<ISpecification<Order>> Specifications { get; }

    public OrderDiscountPolicyJustification(
        OrderDiscountSpec orderDiscountSpec)
    {
        Specifications = new ISpecification<Order>[]
        {
            orderDiscountSpec
        };
    }
}

Design rules

  • There is exactly one policy justification per policy.

  • Specifications may be reused across justifications.

  • A justification must match the policy it justifies.

  • The evaluator enforces this contract at runtime.


Policies

A policy declares whether progression is allowed.

Note on IsSatisfiedBy

  • IsSatisfiedBy exists as a coarse compatibility check and for simple gating scenarios.
  • It must not be used to explain why a policy failed.
  • All meaningful policy evaluation in Optima.Net.Domain is performed via PolicyDiagnosticEvaluator, which produces immutable diagnostics.
public sealed class OrderDiscountPolicy : IPolicy<Order>, INamedPolicy
{
		public const string PolicyId = "OrderDiscountPolicy";
		public string PolicyName => PolicyId;
    public bool IsSatisfiedBy(Order order)
        => true;

    public PolicyFailureSemantics FailureSemantics
        => PolicyFailureSemantics.Correctable;
}

Policies:

  • do not return results
  • do not create failures
  • do not orchestrate

They declare intent only.


Policy Failure Semantics

Failure semantics describe what is allowed after a failure, not whether something failed.

[Flags]
public enum PolicyFailureSemantics
{
    Terminal     = 0,
    Correctable  = 1 << 0,
    Replaceable  = 1 << 1
}

Examples:

  • Terminal → stop immediately if failed
  • Correctable → user may fix input
  • Replaceable → alternative intent may be proposed

Evaluating Policies

Policies are evaluated using a PolicyDiagnosticEvaluator.

var evaluator = new PolicyDiagnosticEvaluator();

var diagnostic = evaluator.EvaluateAll(
    OrderPolicies.All,
    order);

Policies are evaluated as collections, not composites.


Diagnostics and Failure Projection

Evaluation produces a diagnostic tree.

var failures = diagnostic.GetFailures();

Rules:

  • only failed leaf policies are projected
  • parent/group nodes are never failures
  • failures are immutable facts

A leaf represents the lowest-level diagnostic node. In the current model, specifications are leaf nodes, but consumers must not assume that failures always correspond to specifications.


End-to-End Example: Password Policy

Specifications:

public sealed class PasswordLengthSpec : ISpecification<string>
{
    public bool IsSatisfiedBy(string password)
        => password.Length >= 12;
}

public sealed record PasswordEntropySpec : ISpecification<string>
{
    private const int MinimumEntropyBits = 60;

    public bool IsSatisfiedBy(string password)
    {
        if (string.IsNullOrEmpty(password))
            return false;

        var entropy = CalculateEntropy(password);
        return entropy >= MinimumEntropyBits;
    }

    private static double CalculateEntropy(string password)
    {
        var distinctChars = password.Distinct().Count();
        if (distinctChars == 0)
            return 0;

        // Shannon-style approximation:
        // entropy ≈ length × log2(character set size)
        return password.Length * Math.Log2(distinctChars);
    }
}

PolicyJustification:

public sealed class PasswordPolicyJustification
    : IPolicyJustification<string>
{
    public string PolicyName => PasswordPolicy.PolicyId;

    public IReadOnlyCollection<ISpecification<string>> Specifications { get; }

    public PasswordPolicyJustification(
        PasswordLengthSpec passwordLengthSpec,
        PasswordEntropySpec passwordEntropySpec)
    {
        Specifications = new ISpecification<string>[]
        {
            passwordLengthSpec,
            passwordEntropySpec
        };
    }
}

Policy

public sealed class PasswordPolicy : IPolicy<string>, INamedPolicy
{
		public const string PolicyId = "PasswordPolicy ";
		public string PolicyName => PolicyId;
		
    public bool IsSatisfiedBy(string candidate)
        => true;

    public PolicyFailureSemantics FailureSemantics
        => PolicyFailureSemantics.Correctable | PolicyFailureSemantics.Replaceable;
}

Policy Evaluation

var password= "UnderstoodEventually";
var justification = new PasswordPolicyJustification(
    lengthSpec,
    entropySpec);
//pair policies with justfications
var policies = new[]
{
    (Policy: (IPolicy<string>)policy,
     Justification: (IPolicyJustification<string>)justification)
};
var evaluator = new PolicyDiagnosticEvaluator(new SpecificationEvaluator());

var diagnostic = evaluator.EvaluateAll(policies, password);

PolicyDiagnosticEvaluator is the sole entry point for producing diagnostics. Specification-level evaluators are internal implementation details and are not used directly by consumers.

End-to-End Example: Credit Approval

The Domain Model

public sealed class CreditApplication
{
    public decimal Amount { get; }
    public bool HasPriorDefaults { get; }
    public bool PassedAmlChecks { get; }

    public CreditApplication(
        decimal amount,
        bool hasPriorDefaults,
        bool passedAmlChecks)
    {
        Amount = amount;
        HasPriorDefaults = hasPriorDefaults;
        PassedAmlChecks = passedAmlChecks;
    }
}

Specifications

public sealed record CreditWithinLimitSpec(decimal MaxAmount)
    : ISpecification<CreditApplication>
{
    public bool IsSatisfiedBy(CreditApplication app)
        => app.Amount <= 1000000;
}

public sealed record NoPriorDefaultsSpec
    : ISpecification<CreditApplication>
{
    public bool IsSatisfiedBy(CreditApplication app)
        => !app.HasPriorDefaults;
}

public sealed record PassedAmlChecksSpec
    : ISpecification<CreditApplication>
{
    public bool IsSatisfiedBy(CreditApplication app)
        => app.PassedAmlChecks;
}

Policies

public sealed class CreditApprovalPolicy
    : IPolicy<CreditApplication>, INamedPolicy
{
    public const string PolicyId = "CreditApprovalPolicy";
		public string PolicyName => PolicyId;

    public bool IsSatisfiedBy(CreditApplication candidate)
        => true;

    public PolicyFailureSemantics FailureSemantics
        => PolicyFailureSemantics.Replaceable;
}

public sealed class AmlClearancePolicy
    : IPolicy<CreditApplication>, INamedPolicy
{
    public const string PolicyId = "AmlClearancePolicy";
		public string PolicyName => PolicyId;

    public bool IsSatisfiedBy(CreditApplication candidate)
        => true;

    public PolicyFailureSemantics FailureSemantics
        => PolicyFailureSemantics.Terminal;
}

PolicyJustification


public sealed class CreditApprovalPolicyJustification
    : IPolicyJustification<CreditApplication>
{
    public string PolicyName => CreditApprovalPolicy.PolicyId;

    public IReadOnlyCollection<ISpecification<CreditApplication>> Specifications { get; }

    public CreditApprovalPolicyJustification()
    {
        Specifications = new ISpecification<CreditApplication>[]
        {
            new CreditWithinLimitSpec(100_000m),
						new NoPriorDefaultsSpec()
        };
    }
}

public sealed class AmlClearancePolicyJustification
    : IPolicyJustification<CreditApplication>
{
    public string PolicyName => AmlClearancePolicy.PolicyId;

    public IReadOnlyCollection<ISpecification<CreditApplication>> Specifications { get; }

    public AmlClearancePolicyJustification()
    {
        Specifications = new ISpecification<CreditApplication>[]
        {
            new PassedAmlChecksSpec()
        };
    }
}

Evaluate the Policies


var application = new CreditApplication(
    amount: 120_000m,
    hasPriorDefaults: false,
    passedAmlChecks: true);

// Build policies
var creditPolicy = new CreditApprovalPolicy();
var amlPolicy    = new AmlClearancePolicy();

// Build justifications
var creditJustification =
    new CreditApprovalPolicyJustification();

var amlJustification = new AmlClearancePolicyJustification();

// Pair policies with justifications
var policies = new[]
{
    (Policy: (IPolicy<CreditApplication>)creditPolicy,
     Justification: (IPolicyJustification<CreditApplication>)creditJustification),

    (Policy: (IPolicy<CreditApplication>)amlPolicy,
     Justification: (IPolicyJustification<CreditApplication>)amlJustification)
};

// Evaluate
var evaluator = new PolicyDiagnosticEvaluator(
    new SpecificationEvaluator());

var diagnostic = evaluator.EvaluateAll(
    policies,
    application);

This is the authoritative evaluation path. Boolean policy checks should not be used for decision explanation.

Interpreting the result

if (diagnostic.Fulfilled)
{
    // Credit may proceed
}
else
{
    var failures = diagnostics.GetFailures();

		// These are failed *leaf* diagnostics
		// (currently specifications, but not assumed to be forever)

    if (diagnostic.Semantics.HasFlag(PolicyFailureSemantics.Replaceable))
    {
        // Offer alternative product
    }

    if (diagnostic.Semantics.HasFlag(PolicyFailureSemantics.Terminal))
    {
        // Stop immediately
    }
}

Compatibility-level usage of `IsSatisfiedBy

// Compatibility-level check only.
// Real decision-making must use diagnostics.
bool creditPolicyAllowsProgression =
    creditPolicy.IsSatisfiedBy(application);

bool amlPolicyAllowsProgression =
    amlPolicy.IsSatisfiedBy(application);

Meaning:

If credit approval fails, an alternative product may be offered.


Advanced Usage Patterns

Multiple semantics

PolicyFailureSemantics.Correctable | PolicyFailureSemantics.Replaceable

Async Variation

Async policy support

Optima.Net.Domain currently provides IAsyncPolicy<T> for permission checks that depend on asynchronous facts.

Full asynchronous policy diagnostics (including async justifications and evaluators) are intentionally not part of the current release and may be introduced in a future version if sufficient demand exists.

public interface ICreditConfigProvider
{
    Task<decimal> GetMaxCreditAmountAsync(
        CancellationToken ct = default);
}
///////////////////////////////////////////////
public sealed class CreditWithinLimitSpec
    : IAsyncSpecification<CreditApplication>
{
    private readonly ICreditConfigProvider _config;
		
    public CreditWithinLimitSpec(ICreditConfigProvider config)
    {
        _config = config ?? throw new ArgumentNullException(nameof(config));
    }

    public async Task<bool> IsSatisfiedByAsync(
        CreditApplication application,
        CancellationToken ct = default)
    {
        var maxAmount = await _config.GetMaxCreditAmountAsync(ct);
        return application.Amount <= maxAmount;
    }
}
//////////////////////////////////////////////////////////////////////////
public sealed class NoPriorDefaultsSpec
    : IAsyncSpecification<CreditApplication>
{
    public Task<bool> IsSatisfiedByAsync(
        CreditApplication app,
        CancellationToken ct = default)
        => Task.FromResult(!app.HasPriorDefaults);
}

/////////////////////////////////////////////////////////////////////////
public sealed class CreditApprovalPolicy
    : IAsyncPolicy<CreditApplication>, INamedPolicy
{
    public string PolicyName => nameof(CreditApprovalPolicy);

		//## Why diagnostics don’t apply here (by design)
		//You cannot reliably explain:
		//   why a credit bureau returned a score
    //   why a fraud engine said “no”
		//   why a regulator blocked something
	  //   Trying to force diagnostics here would be lying.
	  
    public async Task<bool> IsSatisfiedByAsync(CreditApplication application,  
		    CancellationToken ct = default)
    {
        var score = await _riskService.GetRiskScoreAsync(
            application.CustomerId,
            ct);

        return score <= 650;
    }

    public PolicyFailureSemantics FailureSemantics
        => PolicyFailureSemantics.Replaceable;
}
//////////////////////////////////////////////////////////////////////////
 CreditApprovalPolicy  policy = new CreditApprovalPolicy();  ;
`var allowed = await _policy.IsSatisfiedByAsync(application, ct);

        if (!allowed)
        {
            // No diagnostics here — this is a permission gate
            return ApplicationResult.Rejected(
                "Credit approval policy not satisfied");
        }

        // Proceed
        return ApplicationResult.Completed();``

Async diagnostics mirror the synchronous policy model but are optional and additive.

Most domains resolve asynchronous facts (databases, services, configuration) before policy evaluation and then evaluate policies synchronously.

Optima.Net.Domain provides async policy and specification primitives for cases where end-to-end async evaluation is required, but it does not mandate diagnostic symmetry.

Why does an async policy contain a predicate?

An IAsyncPolicy<T> may contain inline boolean logic that resembles a specification. This is intentional.

The predicate is not a domain fact. It is an interpretation of an asynchronous, external signal (e.g. fraud engines, regulators, credit bureaus) and is scoped strictly to the policy.

Because these facts are:

  • external,
  • volatile,
  • non-deterministic, and
  • not explainable in-domain,

they must not be modeled as specifications.

Async policies therefore act as permission gates, not diagnostic participants.

Anti-Corruption Layers (ACL)

An ACL protects the domain from foreign models and semantics.

Inbound ACLs translate meaning, not data.

public interface IInboundTranslator<in TForeign, TDomain>
{
    TranslationResult<TDomain> Translate(TForeign input);
}

ACLs:

  • absorb versioning
  • reject ambiguity
  • protect invariants

Application Layer: Use Cases and Sagas

Use Case

A Use Case represents a single, synchronous application intent.

Characteristics:

  • single entry point

  • deterministic

  • no retries

  • no compensation

  • completes or rejects immediately

Mental model

“Given this input, do we proceed or not?”

Minimal Contract

namespace Optima.Net.Domain.Application;

public interface IUseCase<in TRequest, out TResult>
{
    TResult Execute(TRequest request);
}

There is no async variant by default. Async is a transport concern, not a modeling concern.

Realistic Example – Create Disbursement Instruction

public sealed class CreateDisbursementInstructionUseCase
    : IUseCase<CreateDisbursementRequest, ApplicationResult>
{
    private readonly IInboundTranslator<CreateDisbursementDto, DisbursementInstruction> _translator;
    private readonly PolicyDiagnosticEvaluator _policyEvaluator;
    private readonly IReadOnlyCollection<
        (IPolicy<DisbursementInstruction> Policy,
         IPolicyJustification<DisbursementInstruction> Justification)
    > _policies;
    private readonly DisbursementRepository _repository;

    public CreateDisbursementInstructionUseCase(
        IInboundTranslator<CreateDisbursementDto, DisbursementInstruction> translator,
        PolicyDiagnosticEvaluator policyEvaluator,
        IReadOnlyCollection<
            (IPolicy<DisbursementInstruction>,
             IPolicyJustification<DisbursementInstruction>)
        > policies,
        DisbursementRepository repository)
    {
        _translator = translator;
        _policyEvaluator = policyEvaluator;
        _policies = policies;
        _repository = repository;
    }

    public ApplicationResult Execute(CreateDisbursementRequest request)
    {
        // Step 1 — Translate meaning (ACL)
        var translation = _translator.Translate(request.Payload);
        if (!translation.IsSuccess)
            return ApplicationResult.Rejected(translation.Issues);

        var instruction = translation.Value!;

        // Step 2 — Ask the domain if we may proceed
        var diagnostics =
            _policyEvaluator.EvaluateAll(_policies, instruction);

        if (!diagnostics.Fulfilled)
        {
            var failures = diagnostics.GetFailures();
            return ApplicationResult.Rejected(failures);
        }

        // Step 3 — Persist intent
        _repository.Save(instruction);

        return ApplicationResult.Completed();
    }
}


Notice the following Key observations:

  • No business rules here

  • No IO except through collaborators

  • No retries

  • No workflow branching

  • Pure orchestration

Saga

Please take note that policies, and specifications as explained above should absolutely be used with a Saga. For brevity however I'm only showing the saga paradigm

A Saga represents a multi-step or failure-aware process.

Characteristics:

  • multiple steps

  • may span time

  • owns failure semantics

  • may retry, compensate, escalate, or stop

  • does not return a value

Mental model

“How do we try to make this happen safely?”

Minimal Contract

namespace Optima.Net.Domain.Application;

public interface ISaga<in TInput>
{
    void Execute(TInput input);
}

If a Saga returns a value, it is lying. Outcomes are observed via state, decisions, or events.

Realistic Example – Execute Disbursement Saga

public sealed class ExecuteDisbursementSaga
    : ISaga<DisbursementInstruction>, ICompensatingSaga
{
    private readonly PolicyDiagnosticEvaluator _policyEvaluator;
    private readonly IReadOnlyCollection<
        (IPolicy<DisbursementInstruction> Policy,
         IPolicyJustification<DisbursementInstruction> Justification)
    > _policies;

    private readonly PaymentsEngineGateway _payments;
    private readonly DisbursementRepository _repository;

    public ExecuteDisbursementSaga(
        PolicyDiagnosticEvaluator policyEvaluator,
        IReadOnlyCollection<
            (IPolicy<DisbursementInstruction>,
             IPolicyJustification<DisbursementInstruction>)
        > policies,
        PaymentsEngineGateway payments,
        DisbursementRepository repository)
    {
        _policyEvaluator = policyEvaluator;
        _policies = policies;
        _payments = payments;
        _repository = repository;
    }

    public void Execute(DisbursementInstruction instruction)
    {
        // STEP 0 — Ask the domain if execution may proceed
        var diagnostics =
            _policyEvaluator.EvaluateAll(_policies, instruction);

        if (!diagnostics.Fulfilled)
        {
            instruction.MarkRejected(diagnostics.GetFailures());
            _repository.Save(instruction);
            return;
        }

        try
        {
            // STEP 1 — Mark intent
            instruction.MarkInProgress();

            // STEP 2 — Execute side effects
            foreach (var line in instruction.Lines)
            {
                _payments.Execute(line);
                instruction.MarkLineCompleted(line);
            }

            // STEP 3 — Complete
            instruction.MarkCompleted();
            _repository.Save(instruction);
        }
        catch (Exception ex)
        {
            Compensate(instruction, ex);
            throw;
        }
    }

    private void Compensate(
        DisbursementInstruction instruction,
        Exception exception)
    {
        instruction.MarkFailed(exception.Message);
        _repository.Save(instruction);

        // Best-effort rollback / escalation
        // No assumption of full reversibility
    }
}


Key observations:

  • Compensation is explicit

  • Compensation is contextual

  • No fake symmetry

  • The Saga owns failure semantics

  • Infrastructure is called, but not owned


** Compensating Saga Marker**

Some Sagas include explicit compensation logic.

To make that intent visible and enforceable, Optima provides a marker interface.


public interface ICompensatingSaga { }

Design Rule

If a Saga contains compensation or reversal logic, it must implement ICompensatingSaga.

This is:

  • a semantic signal

  • enforceable via code review or analyzers

  • intentionally behavior-free

There is no Compensate() method on the interface. Compensation is designed explicitly, not abstracted.


Design Philosophy and Boundaries

Optima.Net.Domain intentionally avoids:

  • rule engines
  • workflow engines
  • infrastructure concerns

Its purpose is singular:

Make domain intent explicit, enforceable, and auditable.

If a rule matters, model it as a specification.
If a decision matters, model it as a policy.
If integration matters, protect it with an ACL.

Product 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. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.
  • net8.0

    • No dependencies.

NuGet packages (1)

Showing the top 1 NuGet packages that depend on Optima.Net.Domain:

Package Downloads
Optima.Net.NegotiatR

NegotiatR is a deterministic, single-pass negotiation engine that proposes alternative intents when policies fail. It consumes policy diagnostics, never re-evaluates rules, and guarantees outcomes. Designed for domain-driven systems, it keeps fallback decisions explicit, auditable, and separate from policy evaluation, workflows, and execution logic, without retries or implicit control flow.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
3.1.3 159 1/7/2026
3.1.2 147 12/29/2025
3.1.1 110 12/29/2025
3.1.0 111 12/27/2025
3.0.1 128 12/26/2025
3.0.0 150 12/26/2025
2.0.0 190 12/24/2025
1.0.2 194 12/23/2025
1.0.1 188 12/22/2025
1.0.0 189 12/22/2025

RELEASENOTES.md