Optima.Net.Domain 1.0.0

Prefix Reserved
There is a newer version of this package available.
See the version list below for details.
dotnet add package Optima.Net.Domain --version 1.0.0
                    
NuGet\Install-Package Optima.Net.Domain -Version 1.0.0
                    
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="1.0.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Optima.Net.Domain" Version="1.0.0" />
                    
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 1.0.0
                    
#r "nuget: Optima.Net.Domain, 1.0.0"
                    
#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@1.0.0
                    
#: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=1.0.0
                    
Install as a Cake Addin
#tool nuget:?package=Optima.Net.Domain&version=1.0.0
                    
Install as a Cake Tool

Optima.Net.Domain

Optima.Net.Domain is a lightweight, framework-agnostic toolkit that provides explicit building blocks for writing clear, maintainable, and auditable Domain-Driven Design (DDD) code.

This library is intentionally focused. It does not try to be a framework. Instead, it provides a small set of primitives that help you express facts, decisions, and outcomes in a way that scales from simple business rules to finance-grade decision systems.

This document is both:

  • a comprehensive reference, and
  • a step-by-step tutorial for engineers new to DDD-style modelling.

If you read it top-to-bottom, you should be able to confidently apply Specifications and Policies in real systems.

If you like what I did here or it inspired you to learn something new, please consider buying me a coffee: https://buymeacoffee.com/snamretsuek The support would be greatly appreciated!


Table of Contents

  1. Motivation and Mental Model
  2. Core Concepts Overview
  3. Specifications (Sync)
  4. Composite Specifications
  5. Policies (Sync)
  6. Composite Policies
  7. Async Specifications and Policies
  8. Diagnostics
  9. Diagnostic Helpers
  10. Decisions / Outcomes
  11. Identity and Naming
  12. End-to-End Examples
  13. Design Philosophy and Boundaries

1. Motivation and Mental Model

In many systems, business rules end up:

  • buried in 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.

The Mental Model

Concept Purpose Question Answered
Specification Fact Is this true?
Policy Decision May we proceed?
Diagnostics Explanation Why did this fail?
Decision Outcome What happened?

This separation is the foundation of the library.


2. Core Concepts Overview

Specifications

  • Express facts
  • Are small and composable
  • Contain no side effects
  • Can be reused across policies

Policies

  • Express business intent
  • Are fulfilled only if all required facts hold
  • Often composed from multiple specifications
  • Can be composed from other policies

Diagnostics

  • Observe evaluation
  • Never change behaviour
  • Produce immutable result trees

Decisions

  • Capture evaluation outcomes
  • Are immutable and serializable
  • Suitable for logging and auditing

3. Specifications (Sync)

A specification answers a single factual question.

Example Domain: Orders

public sealed class OrderLine
{
    public string Product { get; }
    public decimal Price { get; }
    public bool CanShipByAir { get; }

    public OrderLine(string product, decimal price, bool canShipByAir)
    {
        Product = product;
        Price = price;
        CanShipByAir = canShipByAir;
    }
}
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);
}

Individual Specifications

public sealed class OrderHasLinesSpec : Specification<Order>
{
    public override bool IsSatisfiedBy(Order order)
        => order.Lines.Any();
}
public sealed class OrderCanShipByAirSpec : Specification<Order>
{
    public override bool IsSatisfiedBy(Order order)
        => order.Lines.All(l => l.CanShipByAir);
}
public sealed class OrderTotalExceedsSpec : Specification<Order>
{
    private readonly decimal _threshold;

    public OrderTotalExceedsSpec(decimal threshold)
        => _threshold = threshold;

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

Each specification:

  • Answers one question
  • Has no knowledge of business intent
  • Is independently testable

4. Composite Specifications

Specifications can be composed to express more complex facts.

var shippableOrderSpec =
    new OrderHasLinesSpec()
        .And(new OrderCanShipByAirSpec());
var discountEligibleSpec =
    new OrderHasLinesSpec()
        .And(new OrderTotalExceedsSpec(500m));

Composite specifications still express facts. They do not represent decisions.


5. Policies (Sync)

Policies express decisions and are typically built from specifications.

Order Fulfillment Policy

public sealed class OrderFulfillmentPolicy : Policy<Order>
{
    private static readonly ISpecification<Order> _specification =
        new OrderHasLinesSpec()
            .And(new OrderCanShipByAirSpec());

    public override bool IsSatisfiedBy(Order order)
        => _specification.IsSatisfiedBy(order);
}

This policy answers:

May this order be fulfilled?


Discount Eligibility Policy

public sealed class OrderDiscountPolicy : Policy<Order>
{
    private static readonly ISpecification<Order> _specification =
        new OrderHasLinesSpec()
            .And(new OrderTotalExceedsSpec(500m));

    public override bool IsSatisfiedBy(Order order)
        => _specification.IsSatisfiedBy(order);
}

6. Composite Policies

In many domains, multiple policies must be fulfilled before progression is allowed.

Finance Example: Credit Application

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 class CreditWithinLimitSpec : Specification<CreditApplication>
{
    public override bool IsSatisfiedBy(CreditApplication app)
        => app.Amount <= 100_000m;
}
public sealed class NoPriorDefaultsSpec : Specification<CreditApplication>
{
    public override bool IsSatisfiedBy(CreditApplication app)
        => !app.HasPriorDefaults;
}
public sealed class PassedAmlChecksSpec : Specification<CreditApplication>
{
    public override bool IsSatisfiedBy(CreditApplication app)
        => app.PassedAmlChecks;
}

Policies Built from Specifications

public sealed class CreditApprovalPolicy : Policy<CreditApplication>
{
    private static readonly ISpecification<CreditApplication> _specification =
        new CreditWithinLimitSpec()
            .And(new NoPriorDefaultsSpec());

    public override bool IsSatisfiedBy(CreditApplication app)
        => _specification.IsSatisfiedBy(app);
}
public sealed class AmlClearancePolicy : Policy<CreditApplication>
{
    private static readonly ISpecification<CreditApplication> _specification =
        new PassedAmlChecksSpec();

    public override bool IsSatisfiedBy(CreditApplication app)
        => _specification.IsSatisfiedBy(app);
}

Composite Policy Gate

public sealed class CreditIssuancePolicy
    : CompositePolicy<CreditApplication>
{
    protected override IReadOnlyCollection<IPolicy<CreditApplication>> Policies =>
        new IPolicy<CreditApplication>[]
        {
            new CreditApprovalPolicy(),
            new AmlClearancePolicy()
        };
}

This expresses a progression gate:

Credit may only be issued if all required policies are fulfilled.


7. Async Specifications and Policies

Async variants exist for:

  • Database checks
  • External services
  • Fraud / AML systems
public sealed class AsyncAmlSpec : AsyncSpecification<CreditApplication>
{
    public override Task<bool> IsSatisfiedByAsync(
        CreditApplication app,
        CancellationToken ct = default)
        => Task.FromResult(app.PassedAmlChecks);
}

Async policies mirror sync policies exactly. Sync and async are never mixed.


8. Diagnostics

Diagnostics explain why something passed or failed.

Specification Diagnostics

var evaluator = new DiagnosticSpecificationEvaluator();
var diagnostics = evaluator.Evaluate(specification, order);

Policy Diagnostics

var evaluator = new PolicyDiagnosticEvaluator();
var diagnostics = evaluator.Evaluate(policy, application);

Diagnostics produce immutable trees describing evaluation.


9. Diagnostic Helpers

Helpers make diagnostics usable without changing behaviour.

var failedSpecs = diagnostics.FailedSpecificationTypes();
var failedPolicies = diagnostics.FailedPolicyTypes();
var leafFailures = diagnostics.FailedLeaves();

10. Decisions / Outcomes

Decisions capture evaluation outcomes.

var decision = policy.Decide(application);
public sealed record PolicyDecision(
    string PolicyType,
    bool Fulfilled);

Decisions are:

  • Immutable
  • Serializable
  • Suitable for audit trails

11. Identity and Naming

Diagnostics use structural identity by default:

PolicyType = policy.GetType().Name

Optional interfaces allow human-readable naming without affecting diagnostics.


12. End-to-End Example

if (!creditIssuancePolicy.IsSatisfiedBy(application))
{
    var diagnostics =
        policyDiagnosticEvaluator.Evaluate(
            creditIssuancePolicy,
            application);

    var failedPolicies = diagnostics.FailedPolicyTypes();
}

This flow is:

  • Explicit
  • Deterministic
  • Auditable
  • Framework-free

13. Design Philosophy and Boundaries

Optima.Net.Domain intentionally avoids:

  • Rule engines
  • Workflow engines
  • Infrastructure concerns
  • Base entities or repositories

Its purpose is singular:

Make domain intent explicit and executable.

If a rule matters, model it as a specification.
If a decision matters, model it as a policy.


© Optima.Net.Domain

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