diff --git a/src/Azure/src/Eventuous.Azure.ServiceBus/Producers/ServiceBusMessageBuilder.cs b/src/Azure/src/Eventuous.Azure.ServiceBus/Producers/ServiceBusMessageBuilder.cs
index 42078a734..171d8ba56 100644
--- a/src/Azure/src/Eventuous.Azure.ServiceBus/Producers/ServiceBusMessageBuilder.cs
+++ b/src/Azure/src/Eventuous.Azure.ServiceBus/Producers/ServiceBusMessageBuilder.cs
@@ -8,27 +8,13 @@ namespace Eventuous.Azure.ServiceBus.Producers;
 
 using Shared;
 
-class ServiceBusMessageBuilder {
-    readonly IEventSerializer _serializer;
-    readonly string _streamName;
-    readonly ServiceBusProduceOptions? _options;
-    readonly ServiceBusMessageAttributeNames _attributes;
-    readonly Action<string>? _setActivityMessageType;
-
-    public ServiceBusMessageBuilder(
-            IEventSerializer serializer,
-            string streamName,
-            ServiceBusMessageAttributeNames attributes,
-            ServiceBusProduceOptions? options = null,
-            Action<string>? setActivityMessageType = null
-        ) {
-        _serializer = serializer;
-        _streamName = streamName;
-        _options = options;
-        _attributes = attributes;
-        _setActivityMessageType = setActivityMessageType;
-    }
-
+class ServiceBusMessageBuilder(
+        IEventSerializer                serializer,
+        string                          streamName,
+        ServiceBusMessageAttributeNames attributes,
+        ServiceBusProduceOptions?       options                = null,
+        Action<string>?                 setActivityMessageType = null
+    ) {
     /// <summary>
     /// Creates a <see cref="ServiceBusMessage"/> from the provided <see cref="ProducedMessage"/>.
     /// This method serializes the event, sets the necessary properties, and adds custom application properties
@@ -37,22 +23,22 @@ public ServiceBusMessageBuilder(
     /// <param name="message"></param>
     /// <returns></returns>
     internal ServiceBusMessage CreateServiceBusMessage(ProducedMessage message) {
-        var (messageType, contentType, payload) = _serializer.SerializeEvent(message.Message);
-        _setActivityMessageType?.Invoke(messageType);
+        var (messageType, contentType, payload) = serializer.SerializeEvent(message.Message);
+        setActivityMessageType?.Invoke(messageType);
 
         var metadata = message.Metadata;
 
         var serviceBusMessage = new ServiceBusMessage(payload) {
             ContentType = contentType,
-            MessageId = metadata?.GetValueOrDefault(_attributes.MessageId, message.MessageId)?.ToString(),
-            Subject = metadata?.GetValueOrDefault(_attributes.Subject, _options?.Subject)?.ToString(),
-            TimeToLive = _options?.TimeToLive ?? TimeSpan.MaxValue,
+            MessageId = metadata?.GetValueOrDefault(attributes.MessageId, message.MessageId)?.ToString(),
+            Subject = metadata?.GetValueOrDefault(attributes.Subject, options?.Subject)?.ToString(),
+            TimeToLive = options?.TimeToLive ?? TimeSpan.MaxValue,
             CorrelationId = message.Metadata?.GetCorrelationId(),
-            To = metadata?.GetValueOrDefault(_attributes.To, _options?.To)?.ToString(),
-            ReplyTo = metadata?.GetValueOrDefault(_attributes.ReplyTo, _options?.ReplyTo)?.ToString()
+            To = metadata?.GetValueOrDefault(attributes.To, options?.To)?.ToString(),
+            ReplyTo = metadata?.GetValueOrDefault(attributes.ReplyTo, options?.ReplyTo)?.ToString()
         };
 
-        var reservedAttributes = _attributes.ReservedNames();
+        var reservedAttributes = attributes.ReservedNames();
 
         foreach (var property in GetCustomApplicationProperties(message, messageType, reservedAttributes)) {
             serviceBusMessage.ApplicationProperties.Add(property);
@@ -64,8 +50,8 @@ internal ServiceBusMessage CreateServiceBusMessage(ProducedMessage message) {
     IEnumerable<KeyValuePair<string, object>> GetCustomApplicationProperties(ProducedMessage message, string messageType, HashSet<string> reservedAttributes)
         => (message.Metadata ?? [])
             .Concat(message.AdditionalHeaders ?? [])
-            .Concat([new(_attributes.MessageType, messageType), new(_attributes.StreamName, _streamName)])
+            .Concat([new(attributes.MessageType, messageType), new(attributes.StreamName, streamName)])
             .Where(pair => !reservedAttributes.Contains(pair.Key))
             .Where(pair => IsSerialisableByServiceBus(pair.Value))
-            .Select(pair => new KeyValuePair<string, object>(pair.Key, pair.Value!)); 
+            .Select(pair => new KeyValuePair<string, object>(pair.Key, pair.Value!));
 }
\ No newline at end of file
diff --git a/src/Azure/src/Eventuous.Azure.ServiceBus/Shared/ServiceBusHelper.cs b/src/Azure/src/Eventuous.Azure.ServiceBus/Shared/ServiceBusHelper.cs
index adf45973b..b857e885e 100644
--- a/src/Azure/src/Eventuous.Azure.ServiceBus/Shared/ServiceBusHelper.cs
+++ b/src/Azure/src/Eventuous.Azure.ServiceBus/Shared/ServiceBusHelper.cs
@@ -1,31 +1,28 @@
-using System;
-using System.IO;
+// Copyright (C) Eventuous HQ OÜ. All rights reserved
+// Licensed under the Apache License, Version 2.0.
 
 namespace Eventuous.Azure.ServiceBus.Shared;
 
-public static class ServiceBusHelper
-{
- public static bool IsSerialisableByServiceBus(object? value) => 
-    value is not null && (
-        value is string ||
-        value is bool ||
-        value is byte ||
-        value is sbyte ||
-        value is short ||
-        value is ushort ||
-        value is int ||
-        value is uint ||
-        value is long ||
-        value is ulong ||
-        value is float ||
-        value is double ||
-        value is decimal ||
-        value is char ||
-        value is Guid ||
-        value is DateTime ||
-        value is DateTimeOffset ||
-        value is Stream ||
-        value is Uri ||
-        value is TimeSpan
-    );
+public static class ServiceBusHelper {
+    public static bool IsSerialisableByServiceBus(object? value) =>
+        value is string
+            or bool
+            or byte
+            or sbyte
+            or short
+            or ushort
+            or int
+            or uint
+            or long
+            or ulong
+            or float
+            or double
+            or decimal
+            or char
+            or Guid
+            or DateTime
+            or DateTimeOffset
+            or Stream
+            or Uri
+            or TimeSpan;
 }
diff --git a/src/Azure/test/Eventuous.Tests.Azure.ServiceBus/IsSerialisableByServiceBus.cs b/src/Azure/test/Eventuous.Tests.Azure.ServiceBus/IsSerialisableByServiceBus.cs
index f191d560d..8d03c0b0c 100644
--- a/src/Azure/test/Eventuous.Tests.Azure.ServiceBus/IsSerialisableByServiceBus.cs
+++ b/src/Azure/test/Eventuous.Tests.Azure.ServiceBus/IsSerialisableByServiceBus.cs
@@ -1,39 +1,38 @@
 using static Eventuous.Azure.ServiceBus.Shared.ServiceBusHelper;
-using System.Threading.Tasks;
 
 namespace Eventuous.Tests.Azure.ServiceBus;
 
 public class IsSerialisableByServiceBus {
     public static IEnumerable<Func<object?>> PassingTestData() {
         yield return () => "string";
-        yield return () => 123; // int
-        yield return () => 123L; // long
+        yield return () => 123;
+        yield return () => 123L;
         yield return () => (short)1;
         yield return () => (byte)1;
-        yield return () => 123u; // uint
-        yield return () => 123UL; // ulong
-        yield return () => 1.23f; // float
-        yield return () => 1.23d; // double
-        yield return () => 12.34m; // decimal
-        yield return () => true; // bool
-        yield return () => 'c'; // char
+        yield return () => 123u;
+        yield return () => 123UL;
+        yield return () => 1.23f;
+        yield return () => 1.23d;
+        yield return () => 12.34m;
+        yield return () => true;
+        yield return () => 'c';
         yield return () => Guid.NewGuid();
         yield return () => DateTime.UtcNow;
         yield return () => DateTimeOffset.UtcNow;
         yield return () => TimeSpan.FromMinutes(5);
         yield return () => new Uri("https://example.com");
-        yield return () => new MemoryStream(); // Stream
+        yield return () => new MemoryStream();
     }
 
     public static IEnumerable<Func<object?>> FailingTestData() {
         yield return () => null;
-        yield return () => new object(); // plain object
-        yield return () => new Dictionary<object, string> { [new object()] = "v" }; // dictionary with non-string key
-        yield return () => new Dictionary<string, object> { ["k"] = new object() }; // dictionary with non-serializable value
-        yield return () => new List<object> { new() }; // list with non-serializable item
-        yield return () => Task.CompletedTask; // Task
-        yield return () => new Action(() => { }); // delegate
-        yield return () => new WeakReference(new object()); // complex type
+        yield return () => new();                                              // plain object
+        yield return () => new Dictionary<object, string> { [new()] = "v" };   // dictionary with non-string key
+        yield return () => new Dictionary<string, object> { ["k"]   = new() }; // dictionary with non-serializable value
+        yield return () => new List<object> { new() };                         // list with non-serializable item
+        yield return () => Task.CompletedTask;                                 // Task
+        yield return () => new Action(() => { });                              // delegate
+        yield return () => new WeakReference(new());                           // complex type
     }
 
     [Test]
diff --git a/src/Core/src/Eventuous.Subscriptions/Registrations/SubscriptionBuilder.cs b/src/Core/src/Eventuous.Subscriptions/Registrations/SubscriptionBuilder.cs
index edefe9995..a04f40a77 100644
--- a/src/Core/src/Eventuous.Subscriptions/Registrations/SubscriptionBuilder.cs
+++ b/src/Core/src/Eventuous.Subscriptions/Registrations/SubscriptionBuilder.cs
@@ -63,8 +63,19 @@ public SubscriptionBuilder AddEventHandler<THandler>(THandler handler) where THa
         return this;
     }
 
-    public SubscriptionBuilder AddCompositionEventHandler
-        <[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] THandler, TWrappingHandler>(Func<THandler, TWrappingHandler> getWrappingHandler)
+    /// <summary>
+    /// Adds a composition event handler to the subscription.
+    /// The inner handler of type <typeparamref name="THandler"/> will be resolved from the container
+    /// (keyed by <see cref="SubscriptionId"/>), and then wrapped by <typeparamref name="TWrappingHandler"/>
+    /// using the provided factory.
+    /// </summary>
+    /// <typeparam name="THandler">Inner event handler type to be resolved from the service provider</typeparam>
+    /// <typeparam name="TWrappingHandler">Wrapping event handler type produced by the factory</typeparam>
+    /// <param name="getWrappingHandler">Factory that takes the resolved inner handler and returns the wrapping handler</param>
+    /// <returns>The current <see cref="SubscriptionBuilder"/> instance</returns>
+    public SubscriptionBuilder AddCompositionEventHandler<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] THandler, TWrappingHandler>(
+            Func<THandler, TWrappingHandler> getWrappingHandler
+        )
         where THandler : class, IEventHandler where TWrappingHandler : class, IEventHandler {
         Services.TryAddKeyedSingleton<THandler>(SubscriptionId);
         AddHandlerResolve(sp => getWrappingHandler(sp.GetRequiredKeyedService<THandler>(SubscriptionId)));
@@ -72,16 +83,46 @@ public SubscriptionBuilder AddCompositionEventHandler
         return this;
     }
 
+    /// <summary>
+    /// Adds a composition event handler to the subscription with a custom inner handler resolver.
+    /// The inner handler is created via <paramref name="getInnerHandler"/> and then wrapped into
+    /// <typeparamref name="TWrappingHandler"/> using <paramref name="getWrappingHandler"/>.
+    /// </summary>
+    /// <typeparam name="THandler">Inner event handler type</typeparam>
+    /// <typeparam name="TWrappingHandler">Wrapping event handler type</typeparam>
+    /// <param name="getInnerHandler">Function that resolves or creates the inner handler using the service provider</param>
+    /// <param name="getWrappingHandler">Factory that produces the wrapping handler from the inner handler</param>
+    /// <returns>The current <see cref="SubscriptionBuilder"/> instance</returns>
     public SubscriptionBuilder AddCompositionEventHandler<THandler, TWrappingHandler>(
             Func<IServiceProvider, THandler> getInnerHandler,
             Func<THandler, TWrappingHandler> getWrappingHandler
         ) where THandler : class, IEventHandler where TWrappingHandler : class, IEventHandler {
-        Services.TryAddKeyedSingleton(SubscriptionId, getInnerHandler);
+        Services.TryAddKeyedSingleton(SubscriptionId, (sp, _) => getInnerHandler(sp));
         AddHandlerResolve(sp => getWrappingHandler(sp.GetRequiredKeyedService<THandler>(SubscriptionId)));
 
         return this;
     }
 
+    /// <summary>
+    /// Adds a composition event handler to the subscription with a custom inner handler resolver.
+    /// The inner handler is created via <paramref name="getInnerHandler"/> and then wrapped into
+    /// <typeparamref name="TWrappingHandler"/> using <paramref name="getWrappingHandler"/>.
+    /// </summary>
+    /// <typeparam name="THandler">Inner event handler type</typeparam>
+    /// <typeparam name="TWrappingHandler">Wrapping event handler type</typeparam>
+    /// <param name="getInnerHandler">Function that resolves or creates the inner handler using the service provider</param>
+    /// <param name="getWrappingHandler">Factory that produces the wrapping handler from the inner handler</param>
+    /// <returns>The current <see cref="SubscriptionBuilder"/> instance</returns>
+    public SubscriptionBuilder AddCompositionEventHandler<THandler, TWrappingHandler>(
+            Func<IServiceProvider, THandler> getInnerHandler,
+            Func<THandler, IServiceProvider, TWrappingHandler> getWrappingHandler
+        ) where THandler : class, IEventHandler where TWrappingHandler : class, IEventHandler {
+        Services.TryAddKeyedSingleton(SubscriptionId, (sp, _) => getInnerHandler(sp));
+        AddHandlerResolve(sp => getWrappingHandler(sp.GetRequiredKeyedService<THandler>(SubscriptionId), sp));
+
+        return this;
+    }
+
     /// <summary>
     /// Allows using a custom consumer instead of the <see cref="DefaultConsumer"/> one.
     /// Can also be used to change the default consumer instantiation.
@@ -141,6 +182,11 @@ public class SubscriptionBuilder
     TOptions> : SubscriptionBuilder
     where T : EventSubscription<TOptions>
     where TOptions : SubscriptionOptions {
+    /// <summary>
+    /// Creates a new subscription builder for a specific subscription id.
+    /// </summary>
+    /// <param name="services">The service collection to register handlers and dependencies with</param>
+    /// <param name="subscriptionId">The subscription identifier used to key registrations</param>
     public SubscriptionBuilder(IServiceCollection services, string subscriptionId) : base(services, subscriptionId) {
         ResolveConsumer  = ResolveDefaultConsumer;
         ConfigureOptions = options => options.SubscriptionId = subscriptionId;
@@ -182,6 +228,14 @@ IMessageConsumer ResolveDefaultConsumer(IServiceProvider sp) {
         return _resolvedConsumer;
     }
 
+    /// <summary>
+    /// Resolves and builds the subscription instance of type <typeparamref name="T"/>.
+    /// Applies tracing and consumer filters to the consume pipe when diagnostics are enabled,
+    /// resolves the configured consumer, and creates the subscription using options keyed by
+    /// <see cref="SubscriptionId"/>.
+    /// </summary>
+    /// <param name="sp">Service provider used to resolve dependencies</param>
+    /// <returns>The resolved and configured subscription instance</returns>
     public T ResolveSubscription(IServiceProvider sp) {
         if (_resolvedSubscription != null) {
             return _resolvedSubscription;
diff --git a/src/Core/test/Eventuous.Tests.Subscriptions/CompositionHandlerTests.cs b/src/Core/test/Eventuous.Tests.Subscriptions/CompositionHandlerTests.cs
new file mode 100644
index 000000000..8fba70246
--- /dev/null
+++ b/src/Core/test/Eventuous.Tests.Subscriptions/CompositionHandlerTests.cs
@@ -0,0 +1,126 @@
+using Eventuous.Subscriptions;
+using Eventuous.Subscriptions.Context;
+using Eventuous.Subscriptions.Filters;
+using Microsoft.AspNetCore.Builder;
+using Microsoft.AspNetCore.Hosting;
+using Microsoft.AspNetCore.TestHost;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Hosting;
+using Microsoft.Extensions.Logging.Abstractions;
+using Shouldly;
+
+namespace Eventuous.Tests.Subscriptions;
+
+public class CompositionHandlerTests {
+    TestServer _server = null!;
+    IHost      _host   = null!;
+
+    [Before(Test)]
+    public async Task Setup() {
+        _host = new HostBuilder()
+            .ConfigureWebHost(webHostBuilder => webHostBuilder
+                .UseTestServer()
+                .UseStartup<Startup>()
+            )
+            .Build();
+        await _host.StartAsync();
+
+        _server = _host.GetTestServer();
+    }
+
+    [After(Test)]
+    public async Task Teardown() {
+        _server.Dispose();
+        await _host.StopAsync();
+        _host.Dispose();
+    }
+
+    [Test]
+    public async Task ShouldResolveCompositionHandlerWithFactory() {
+        // This test validates that AddCompositionEventHandler correctly registers
+        // handlers when using a factory function
+        var handler = _server.Services.GetRequiredKeyedService<TestHandler>("sub-with-factory");
+        await Assert.That(handler).IsNotNull();
+        await Assert.That(handler.Dependency.Value).IsEqualTo("test-value");
+    }
+
+    [Test]
+    public async Task ShouldHandleEventWithCompositionHandler() {
+        var logger = _server.Services.GetRequiredService<TestHandlerLogger>();
+        var subs   = _server.Services.GetServices<TestSub>().ToArray();
+        var sub    = subs.FirstOrDefault(x => x.SubscriptionId == "sub-with-factory");
+
+        sub.ShouldNotBeNull();
+
+        var ctx = new MessageConsumeContext(
+            Guid.NewGuid().ToString(),
+            Guid.NewGuid().ToString(),
+            Guid.NewGuid().ToString(),
+            Guid.NewGuid().ToString(),
+            0,
+            0,
+            0,
+            0,
+            DateTime.UtcNow,
+            new TestEvent(),
+            new(),
+            sub.SubscriptionId,
+            default
+        ) { LogContext = new(sub.SubscriptionId, NullLoggerFactory.Instance) };
+
+        await sub.Pipe.Send(ctx);
+
+        var handled = logger.Records.Where(x => x.Context.SubscriptionId == sub.SubscriptionId).ToArray();
+        await Assert.That(handled.Length).IsEqualTo(1);
+        var handledMessage = handled[0];
+        await Assert.That(handledMessage.HandlerType).IsEqualTo(typeof(CompositionWrapper));
+        await Assert.That(handledMessage.Context.MessageId).IsEqualTo(ctx.MessageId);
+    }
+
+    class Startup {
+        public static void ConfigureServices(IServiceCollection services) {
+            services.AddSingleton(new TestHandlerLogger());
+            services.AddSingleton<TestDependency>();
+
+            // Test the AddCompositionEventHandler with a factory function
+            services.AddSubscription<TestSub, TestOptions>(
+                "sub-with-factory",
+                builder => builder.AddCompositionEventHandler<TestHandler, CompositionWrapper>(
+                    sp => new(sp.GetRequiredService<TestDependency>(), sp.GetRequiredService<TestHandlerLogger>()),
+                    (handler, sp) => new(handler, sp.GetRequiredService<TestHandlerLogger>())
+                )
+            );
+        }
+
+        public void Configure(IApplicationBuilder app) { }
+    }
+
+    record TestOptions : SubscriptionOptions;
+
+    class TestSub(TestOptions options, ConsumePipe consumePipe)
+        : EventSubscription<TestOptions>(options, consumePipe, NullLoggerFactory.Instance, null) {
+        protected override ValueTask Subscribe(CancellationToken cancellationToken) => default;
+
+        protected override ValueTask Unsubscribe(CancellationToken cancellationToken) => default;
+    }
+
+    public class TestDependency {
+        public string Value { get; } = "test-value";
+    }
+
+    public class TestHandler(TestDependency dependency, TestHandlerLogger logger) : BaseEventHandler {
+        public TestDependency Dependency { get; } = dependency;
+
+        public override ValueTask<EventHandlingStatus> HandleEvent(IMessageConsumeContext ctx)
+            => logger.EventReceived(GetType(), ctx);
+    }
+
+    public class CompositionWrapper(IEventHandler innerHandler, TestHandlerLogger logger) : BaseEventHandler {
+        public override ValueTask<EventHandlingStatus> HandleEvent(IMessageConsumeContext ctx) {
+            // Wrap the inner handler call - this simulates what PollyEventHandler does
+            return logger.EventReceived(GetType(), ctx);
+        }
+    }
+
+    record TestEvent;
+}
diff --git a/src/EventStore/src/Eventuous.EventStore/EsdbEventStore.cs b/src/EventStore/src/Eventuous.EventStore/EsdbEventStore.cs
index d014d53e7..5004ad086 100644
--- a/src/EventStore/src/Eventuous.EventStore/EsdbEventStore.cs
+++ b/src/EventStore/src/Eventuous.EventStore/EsdbEventStore.cs
@@ -123,7 +123,8 @@ public async Task<StreamEvent[]> ReadEvents(StreamName stream, StreamReadPositio
         try {
             return await TryExecute(
                 async () => {
-                    var resolvedEvents = await read.ToArrayAsync(cancellationToken).NoContext();
+                    var  resolvedEvents = await read.ToArrayAsync(cancellationToken).NoContext();
+                    bool x              = true;
 
                     return ToStreamEvents(resolvedEvents);
                 },
