Skip to content

PolicyWrap Policy

Marco Breveglieri edited this page Jul 20, 2026 · 1 revision

Policy Wrap API

Unit: Murphy.Policy.Wrap

Policy Wrap combines multiple IExecutablePolicy instances into a single executable policy, applied outermost-first.

Overview

Policy Wrap is useful for building a sophisticated resilience strategy out of simpler patterns, such as:

  • Retry wrapping Timeout, so a timed-out attempt is retried
  • Circuit Breaker wrapping Bulkhead, so persistent rejection opens the circuit
  • Any combination of IExecutablePolicy implementations, nested to any depth

Note: Unlike other builders, TPolicyWrapBuilder does not derive from TPolicyBuilder<TPolicy> and has no Handle() method - a wrap has no exception filter of its own, since each wrapped policy applies its own filter.

Table of Contents


IPolicyWrap Interface

The interface for the Policy Wrap pattern. Being itself an IExecutablePolicy, a wrap can be nested inside another wrap.

Declaration

type
  IPolicyWrap = interface(IExecutablePolicy)
    ['{DA04B25E-C53F-4C39-BB0D-EFC7E6A5537D}']
  end;

Methods

Execute

procedure Execute(AProc: TProc);

Description: Executes AProc through every wrapped policy, applied outermost-first.

Behavior: Wrap([A, B]) executes A(B(action)) - A is the outermost policy and sees the composed behavior of everything nested inside it. Internally this is implemented via recursion rather than a pre-built closure chain, so a wrap instance holds no per-call mutable state and can be reused, including concurrently.


TPolicyWrap Class

Concrete implementation of the Policy Wrap pattern.

Declaration

type
  TPolicyWrap = class(TPolicy, IPolicyWrap)
  private
    FPolicies: TArray<IExecutablePolicy>;
    procedure ExecuteFrom(AIndex: Integer; AProc: TProc);
  public
    constructor CreateWrap(const APolicies: TArray<IExecutablePolicy>);
    procedure Execute(AProc: TProc);
  end;

Constructor

constructor CreateWrap(const APolicies: TArray<IExecutablePolicy>);

Parameters:

  • APolicies: TArray<IExecutablePolicy> - Policies to combine, outermost-first

Raises: EArgumentException if APolicies is empty

Note: Typically called by TPolicyWrapBuilder.Wrap, not directly by users.

Implementation Details

ExecuteFrom recurses through FPolicies by index: at each step it calls the current policy's Execute, passing a closure that recurses into the next index. When the index passes the end of the array, the original action finally runs. Because each recursive call captures only its own index and the immutable FPolicies array, the same TPolicyWrap instance is safe to execute repeatedly and concurrently.


TPolicyWrapBuilder Class

Builder class for creating Policy Wrap instances.

Declaration

type
  TPolicyWrapBuilder = class sealed
  public
    class function Wrap(const APolicies: TArray<IExecutablePolicy>): IPolicyWrap;
  end;

Wrap

class function Wrap(const APolicies: TArray<IExecutablePolicy>): IPolicyWrap;

Description: Combines the given policies outermost-first: Wrap([A, B]) executes A(B(action)).

Parameters:

  • APolicies: TArray<IExecutablePolicy> - Policies to combine, outermost-first

Returns: IPolicyWrap - A single executable policy representing the whole chain

Raises: EArgumentException if APolicies is empty

Note: Only types implementing IExecutablePolicy can be wrapped - this includes Retry, Circuit Breaker, Rate Limit, Timeout, Bulkhead, Hedging, and other IPolicyWrap instances. ICachePolicy<TResult> does not implement IExecutablePolicy (it returns a value via TFunc<TResult>) and cannot be wrapped.


Usage Examples

Example 1: Retry Wrapping Timeout

uses Murphy.Policy.Retry, Murphy.Policy.Timeout, Murphy.Policy.Wrap;

var
  Policy: IPolicyWrap;
begin
  Policy := TPolicyWrapBuilder.Wrap([
    TRetryBuilder
      .Handle(ETimeoutRejectedException)
      .Retry(3),
    TTimeoutBuilder
      .Handle([])
      .After(TTimeSpan.FromSeconds(1))
  ]);

  Policy.Execute(procedure begin CallExternalService; end);
end;

Example 2: Three-Policy Composition

uses Murphy.Policy.Retry, Murphy.Policy.CircuitBreaker, Murphy.Policy.Bulkhead, Murphy.Policy.Wrap;

var
  Policy: IPolicyWrap;
begin
  Policy := TPolicyWrapBuilder.Wrap([
    TRetryBuilder.Handle(Exception).Retry(3).Wait(TTimeSpan.FromSeconds(1)),
    TCircuitBreakerBuilder.Handle(Exception).Fail(5).Within(TTimeSpan.FromSeconds(30)),
    TBulkheadBuilder.Handle([]).Limit(10)
  ]);

  // Retry(CircuitBreaker(Bulkhead(action)))
  Policy.Execute(procedure begin CallExternalService; end);
end;

Example 3: Nesting Wraps

var
  InnerPolicy: IPolicyWrap;
  OuterPolicy: IPolicyWrap;
begin
  InnerPolicy := TPolicyWrapBuilder.Wrap([
    TTimeoutBuilder.Handle([]).After(TTimeSpan.FromSeconds(2)),
    TBulkheadBuilder.Handle([]).Limit(5)
  ]);

  OuterPolicy := TPolicyWrapBuilder.Wrap([
    TRetryBuilder.Handle(Exception).Retry(3),
    InnerPolicy
  ]);

  OuterPolicy.Execute(procedure begin CallExternalService; end);
end;

See Also


← Hedging Policy | API Reference Index | Pattern Guides

Clone this wiki locally