# Policy Wrap Pattern Guide Policy Wrap combines multiple executable policies into a single one, so a sophisticated resilience strategy can be built, configured once, and executed as a unit. ## When to Use - Combining two or more patterns (Retry, Timeout, Circuit Breaker, Bulkhead, Hedging) into a single reusable policy - Replacing deeply nested manual `Execute` calls with a flat, readable composition - Building a resilience strategy that itself needs to be passed around or reused as a single object ## When NOT to Use - A single pattern is enough - don't wrap just one policy - Combining `ICachePolicy`, which is not an `IExecutablePolicy` (it uses `TFunc`, not `TProc`) and therefore cannot be part of a wrap ## Quick Start ```pascal 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; ``` `Wrap([A, B])` executes `A(B(action))` - policies are applied **outermost-first**. In the example above, Retry is outermost: it retries the whole timed operation whenever it times out. ## Common Scenarios ### Retry + Circuit Breaker + Bulkhead ```pascal 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; ``` ### Nesting a Wrap Inside Another Wrap A `IPolicyWrap` is itself an `IExecutablePolicy`, so wraps can be composed further: ```pascal 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; ``` ### Reusing a Wrap Across Calls Since a wrap holds no per-call mutable state of its own, the same instance can be executed repeatedly, including concurrently: ```pascal var ResilientCall: IPolicyWrap; begin ResilientCall := TPolicyWrapBuilder.Wrap([ TRetryBuilder.Handle(Exception).Retry(2), TTimeoutBuilder.Handle([]).After(TTimeSpan.FromSeconds(5)) ]); ResilientCall.Execute(procedure begin CallServiceA; end); ResilientCall.Execute(procedure begin CallServiceB; end); end; ``` ## Best Practices 1. **Order outermost-first** - the first policy in the array wraps everything after it; put the policy that should see failures from the whole chain (often Retry) first 2. **Only `IExecutablePolicy` implementations qualify** - Retry, Circuit Breaker, Rate Limit, Timeout, Bulkhead, Hedging, and other wraps; Cache does not, since it returns a value via `TFunc` 3. **Pass at least one policy** - an empty array raises `EArgumentException` 4. **Each policy keeps its own exception filter** - a wrap has no `Handle()` of its own; configure filtering on each wrapped policy individually 5. **Prefer a wrap over manual nesting** - it reads top-to-bottom instead of growing indentation with every added pattern ## See Also - [Policy Wrap API Reference](../API-Reference/PolicyWrap-Policy.md) - [Combining Patterns](Combining-Patterns.md) --- [Back to Index](../Home.md)