Feature/messaging hardening#213
Open
Vulthil wants to merge 46 commits into
Open
Conversation
…on ISender Move the CQRS pipeline from ISender-only dispatch to direct handler decoration. Pipeline behaviors are now composed lazily at handler-resolution time via a PipelineHandlerDecorator wrapping every registered ICommandHandler/IQueryHandler/ IDomainEventHandler, so callers can inject the typed handler interface (e.g. ICommandHandler<TCmd, TResult>) and still receive the full pipeline (validation, logging, transactional behavior, etc.) without going through ISender. - Add HandlerRegistrar, HandlerInterfaceAdapters, IInnerHandler and PipelineHandlerDecorator under Vulthil.SharedKernel.Application/Pipeline. - Replace Scrutor-based assembly scanning with explicit registration; drop the Scrutor package reference. - Expose AddOpenPipelineHandler / AddOpenDomainEventPipelineHandler for open-generic behavior registration; behaviors apply regardless of when they are registered relative to AddHandlers. - Update the WebApi sample to inject typed handlers in its endpoints, group routes under /api, and rename the route prefix to /main-entities. - Add HttpResponseMessageExtensions.GetResponseAsync<T> in Vulthil.Extensions.Testing for the refreshed integration tests. - Document the new pipeline shape in docs/articles/cqrs-pipeline.md and the Vulthil.SharedKernel.Application package doc. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Bring the messaging libraries up to a production-ready baseline with structured logging, observability, health checking, and configuration-driven topology declaration. Consumers gain an in-context PublishAsync that auto-propagates correlation metadata. Public surface (Abstractions): - IMessageContext.PublishAsync<T>(message, configure?) auto-propagates CorrelationId/ConversationId/InitiatorId from the incoming context. - IMessageContext.CancellationToken exposes the delivery's cancellation. Public surface (Vulthil.Messaging): - MessageConfiguration.Exchange is now non-nullable; the new required constructor takes the exchange name. MessageConfiguration<T> defaults it to typeof(T).FullName. - AddMessaging eagerly loads Messaging:Queues:* and Messaging:Messages:* from IConfiguration before the configurator action runs; code calls merge onto the loaded values and win on conflict. Services can now declare topology entirely in appsettings. - IMessageConfigurationProvider.QueueDefinitions exposes the assembled queue set; transports pull queues from there rather than from individual DI singletons. - Removed half-baked IFaultConfigurator / ConfigureFaults / MessagingOptions.AutoDeclareFaultStatus (still unshipped; will be redesigned in a follow-up). Public surface (Vulthil.Messaging.RabbitMq): - MessagingInstrumentation.ActivitySourceName plus the TracerProviderBuilder extension AddVulthilMessagingInstrumentation(). UseRabbitMq registers it automatically; gated on RabbitMQClientSettings.DisableTracing so the Aspire flag toggles the full pipeline. - vulthil_messaging_rabbitmq_bus health check that flips to Healthy after RabbitMqBus.StartAsync completes; gated on DisableHealthChecks. Internal changes: - Producer-only services no longer initialize the reply queue eagerly; ResponseListener is lazy-initialized on the first IRequester call. - RabbitMqBus declares per-message exchanges using MessageConfiguration via IMessageConfigurationProvider, so the bus and publisher use the same source of truth and never disagree on topology. - Worker/publisher/requester gained LoggerMessage-based structured logs; silent catches on fault publish, poison messages, and missing execution plans now surface as warnings/errors. - Fault snapshots embed JsonElement (faithful payload roundtrip) instead of a deserialized object. Docs: - Refreshed docs/articles/messaging.md with sections on in-context publish, message configuration, configuration-driven setup, observability, and health checks. - Pointer additions in the per-package docs for Abstractions, Vulthil.Messaging, and Vulthil.Messaging.RabbitMq. Chore: - Moved documentation-guidelines.instructions.md under .github/instructions/ where the scoped-instructions loader in CLAUDE.md expects it. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…ross-cutting concerns Introduce a middleware-style consume pipeline so cross-cutting concerns (logging, validation, scoped resource management, telemetry, etc.) can be composed around the consumer invocation without modifying transport or consumer code. Public surface (Abstractions): - ConsumeDelegate<TMessage>: delegate signature for the next pipeline stage. - IConsumeFilter<TMessage>: middleware-style filter that wraps consumer invocation. Filters may short-circuit by not invoking next. Public surface (Vulthil.Messaging): - IMessagingConfigurator.AddConsumeFilter<TFilter>(): closed-generic registration; auto-discovers all IConsumeFilter<TMessage> interfaces the implementation declares. - IMessagingConfigurator.AddOpenConsumeFilter(Type openFilterType): open-generic registration for filters that apply to every message type (mirrors the existing AddOpenPipelineHandler pattern in Vulthil.SharedKernel.Application). Internal changes (Vulthil.Messaging.RabbitMq): - ConsumePipelineFactory.Build<TMessage>(): composes the registered filters around a terminal delegate. First-registered = outermost. Returns the terminal unchanged when no filters are registered (no allocation on the cold path). - ConsumerInvoker<TConsumer, TMessage>: terminal is the consumer call. - RpcInvoker<TConsumer, TRequest, TResponse>: terminal captures the response via closure; outer try/catch guarantees a response is sent even when a filter throws or short-circuits (MessageResult.Failure with explanatory text). IMessageConfigurationProvider is now resolved at invoke-time so its JsonSerializerOptions can come from the scoped provider. Tests (5 new in ConsumeFilterPipelineTests): - No filters routes straight to the consumer. - Multiple filters compose outer-first / inner-last around the consumer. - Short-circuiting a filter prevents consumer invocation. - RPC pipeline wraps the consumer call and still publishes the response. - RPC short-circuit produces a failure response instead of a timeout. Test-infra fixes: - MessageTypeCacheTests nested fixture types (TestMessage, TestRequest, TestResponse) promoted from private to internal so Castle.DynamicProxy can proxy generic instantiations of IConsumeFilter<T> against the AutoMocker service provider. - MessageTypeCacheTests registers an empty IEnumerable<IConsumeFilter<T>> for each tested message type so AutoMocker does not auto-mock the pipeline into a silent no-op. Docs: - New "Consume Filters" section in docs/articles/messaging.md covering the interface shape, registration (open + closed), filter ordering, short-circuit semantics, and RPC behavior. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Ship a default open-generic LoggingConsumeFilter<TMessage> registered as the outermost layer of every consume pipeline. It emits structured Debug logs at consume entry/exit and a Warning log on uncaught exceptions, with timing information from Stopwatch.GetTimestamp. New public surface: - ConsumeFilterOptions class exposing per-default-filter toggles, currently EnableLogging (default true). - MessagingOptions.ConsumeFilters property returning the live options object; bind via Messaging:Options:ConsumeFilters in appsettings or mutate via ConfigureMessagingOptions. Registration: - AddMessaging registers LoggingConsumeFilter<> via TryAddEnumerable BEFORE running the configurator action so user-registered filters compose INSIDE the defaults (logging wraps everything). - The filter checks EnableLogging at construction time; when disabled, it passes through to next without logging or timing — the filter stays in DI for test resolution, only its behavior is suppressed. Tests: - DisabledFilterPassesThroughWithoutLogging - EnabledFilterLogsConsumingAndConsumedOnSuccess - EnabledFilterLogsWarningAndRethrowsOnException Tests use a RecordingLogger<T> fake rather than Moq because LoggerMessage's generated code short-circuits on ILogger.IsEnabled, which a default Moq returns false from. Docs: - New "Built-in filters" subsection in docs/articles/messaging.md showing the logging output, the option toggle in appsettings, and the code-side ConfigureMessagingOptions path. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…d gate default filters at registration
…publisher confirms
…into feature/messaging-hardening
…, rpc fault to public primitives
… SetTimeout returns void
…aging and drop the IVT
…field patterns across tests
…through AutoMocker
… on consume failure
…to 13.4.2 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… getters Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…actions Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…t to avoid CI dev-cert TLS failures Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
Related Issue
Type of Change
Checklist
dotnet build -c Release).dotnet test -c Release).docs/, README) andPublicAPI.*.txtfiles where applicable.Additional Notes