Skip to content

feat: Enhance SubscribeFilter to Support Multiple Instances #1709

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

mSabzali
Copy link

@mSabzali mSabzali commented Jul 1, 2025

Feature: Enhance SubscribeFilter to Support Multiple Instances

Summary

This PR enhances the filtering mechanism by allowing the use of multiple filters instead of just one. It provides more flexibility and enables advanced scenarios where multiple filters need to be applied in sequence or conditionally.

What Has Changed

  • Modified the configuration model to support multiple filters.
  • Preserved backward compatibility: single-filter configurations still work.

Why It's Useful

This feature enables scenarios where users need to:

  • Apply layered processing logic
  • Compose filters in different modules
  • Have more granular control over message handling

Testing

  • All existing tests pass

Notes

  • This is my first contribution to CAP.
  • Let me know if you'd like any changes, improvements, or if you'd prefer an issue to be created for tracking.

Additional Request

Hello, thank you for your efforts on the CAP project. This PR is ready for review to support multiple filter instances, and the tests have passed successfully. Is there any change or additional information needed? @yang-xiaodong

@mSabzali mSabzali changed the title feat: support multiple SubscribeFilter feat: Enhance SubscribeFilter to Support Multiple Instances Jul 17, 2025
@mSabzali mSabzali marked this pull request as draft July 17, 2025 07:20
@mSabzali mSabzali marked this pull request as ready for review July 17, 2025 07:29
@mSabzali
Copy link
Author

Hi @yang-xiaodong, just a kind reminder — this PR is still waiting for review. Please let me know if any change is needed or if you'd prefer to track this via an issue. Thank you!

@yang-xiaodong
Copy link
Member

yang-xiaodong commented Jul 28, 2025

Currently, users can implement middleware-like piping behavior through existing filters, but the current implementation may have some performance impact. Additionally, introducing multiple filters raises concerns about added complexity. For example, in the past when users inquired about how to skipping consumer execution, having multiple filters would prevent simply throwing an exception and require more detailed explanations to users.

@mSabzali
Copy link
Author

Hi @yang-xiaodong,
Thank you very much for your thoughtful feedback and for taking the time to review this PR 🙏

I'd like to provide some clarification regarding the concerns you mentioned, especially around complexity and performance impact.


Middleware Behavior and SRP Violation

Currently, only a single filter can be used via .AddSubscribeFilter(). While this allows for some middleware-like behavior, it forces developers to place all responsibilities—such as:

  • Logging
  • Metrics collection
  • Error handling
  • Message validation
  • Message modification

...into a single class. This not only violates the Single Responsibility Principle (SRP) but also makes filters harder to test, maintain, and reuse across modules.

By allowing multiple filters, each with a specific and isolated responsibility, developers can write cleaner, more maintainable code, for example:

.AddSubscribeFilter<LoggingFilter>()
.AddSubscribeFilter<MetricsFilter>()
.AddSubscribeFilter<ValidationFilter>()
.AddSubscribeFilter<ErrorHandlingFilter>()

This separation is especially useful in large-scale systems where cross-cutting concerns must be modularized.


Performance Considerations

The overhead introduced is minimal—just a simple iteration over a small list (typically a few filters).
Filters are executed using ConfigureAwait(false), and we're using in-memory stacks for reversed post-execution—both lightweight operations.
If only one filter is registered, the behavior remains the same, so backward compatibility is fully preserved.


On Complexity & Developer Experience

I understand the concern about added complexity. However, from a user perspective, this design actually makes filters easier to reason about and reuse.
We could document some usage patterns (e.g. skipping consumer execution) to guide users, similar to how ASP.NET middleware is documented.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants