Repository: reagento/adaptix (language: python)

Issue:
Develop



Discussion / hints:
## Coverage report


<img title="Coverage for the whole project is 92.78%. Previous coverage rate is not available, cannot report on evolution." src="https://img.shields.io/badge/Coverage-92%25-brightgreen.svg"> <img title="100% of the statement lines added by this PR are covered" src="https://img.shields.io/badge/PR%20Coverage-100%25-brightgreen.svg"><details><summary>Click to see where and how coverage changed</summary><table><thead>
  <tr><th>File</th><th>Statements</th><th>Missing</th><th>Coverage</th><th>Coverage<br>(new stmts)</th><th>Lines missing</th></tr>
</thead>
<tbody><tr>
<td colspan="6">&nbsp;&nbsp;<b>src/adaptix</b></td><tr>
<td>&nbsp;&nbsp;<a href="https://github.com/reagento/adaptix/pull/313/files#diff-d8ed0d76c11e2db6b1ac5c8c9d6f99a2c22139de49782e9df3df91087e5a2f70">__init__.py</a></td>
<td align="center"><a href="https://github.com/reagento/adaptix/pull/313/files#diff-d8ed0d76c11e2db6b1ac5c8c9d6f99a2c22139de49782e9df3df91087e5a2f70"><img title="This PR adds 15 statements to src/adaptix/__init__.py. The file did not seem to exist on the base branch." src="https://img.shields.io/badge/15-%28%2B15%29-007ec6.svg"></a></td><td align="center"><a href="https://github.com/reagento/adaptix/pull/313/files#diff-d8ed0d76c11e2db6b1ac5c8c9d6f99a2c22139de49782e9df3df91087e5a2f70"><img title="This PR adds 0 statements missing coverage to src/adaptix/__init__.py. The file did not seem to exist on the base branch." src="https://img.shields.io/badge/0-%28%C2%B10%29-lightgrey.svg"></a></td><td align="center"><a href="https://github.com/reagento/adaptix/pull/313/files#diff-d8ed0d76c11e2db6b1ac5c8c9d6f99a2c22139de49782e9df3df91087e5a2f70"><img title="The coverage rate of src/adaptix/__init__.py is 100% (15/15). The file did not seem to exist on the base branch." src="https://img.shields.io/badge/100%25-%2815/15%29-brightgreen.svg"></a></td><td align="center"><a href="https://github.com/reagento/adaptix/pull/313/files#diff-d8ed0d76c11e2db6b1ac5c8c9d6f99a2c22139de49782e9df3df91087e5a2f70"><img title="In this PR, 3 new statements are added to src/adaptix/__init__.py, 3 of which are covered (100%)." src="https://img.shields.io/badge/100%25-%283/3%29-brightgreen.svg"></a></td><td></td><tr>
<td>&nbsp;&nbsp;<a href="https://github.com/reagento/adaptix/pull/313/files#diff-9c966e60494948d3c65256a49da186c34506a9c1e8317c78115341aeef50b285">retort.py</a></td>
<td align="center"><a href="https://github.com/reagento/adaptix/pull/313/files#diff-9c966e60494948d3c65256a49da186c34506a9c1e8317c78115341aeef50b285"><img title="This PR adds 5 statements to src/adaptix/retort.py. The file did not seem to exist on the base branch." src="https://img.shields.io/badge/5-%28%2B5%29-007ec6.svg"></a></td><td align="center"><a href="https://github.com/reagento/adaptix/pull/313/files#diff-9c966e60494948d3c65256a49da186c34506a9c1e8317c78115341aeef50b285"><img title="This PR adds 0 statements missing coverage to src/adaptix/retort.py. The file did not seem to exist on the base branch." src="https://img.shields.io/badge/0-%28%C2%B10%29-lightgrey.svg"></a></td><td align="center"><a href="https://github.com/reagento/adaptix/pull/313/files#diff-9c966e60494948d3c65256a49da186c34506a9c1e8317c78115341aeef50b285"><img title="The coverage rate of src/adaptix/retort.py is 100% (5/5). The file did not seem to exist on the base branch." src="https://img.shields.io/badge/100%25-%285/5%29-brightgreen.svg"></a></td><td align="center"><a href="https://github.com/reagento/adaptix/pull/313/files#diff-9c966e60494948d3c65256a49da186c34506a9c1e8317c78115341aeef50b285"><img title="In this PR, 3 new statements are added to src/adaptix/retort.py, 3 of which are covered (100%)." src="https://img.shields.io/badge/100%25-%283/3%29-brightgreen.svg"></a></td><td></td><tr>
<td colspan="6">&nbsp;&nbsp;<b>src/adaptix/_internal/conversion</b></td><tr>
<td>&nbsp;&nbsp;<a href="https://github.com/reagento/adaptix/pull/313/files#diff-956a90ff8f78f3db32dbcdff3104fc1a9a1462fae75a0c564e61d5909c212f51">coercer_provider.py</a></td>
<td align="center"><a href="https://github.com/reagento/adaptix/pull/313/files#diff-956a90ff8f78f3db32dbcdff3104fc1a9a1462fae75a0c564e61d5909c212f51"><img title="This PR adds 139 statements to src/adaptix/_internal/conversion/coercer_provider.py. The file did not seem to exist on the base branch." src="https://img.shields.io/badge/139-%28%2B139%29-007ec6.svg"></a></td><td align="center"><a href="https://github.com/reagento/adaptix/pull/313/files#diff-956a90ff8f78f3db32dbcdff3104fc1a9a1462fae75a0c564e61d5909c212f51"><img title="This PR adds 10 statements missing coverage to src/adaptix/_internal/conversion/coercer_provider.py. The file did not seem to exist on the base branch." src="https://img.shields.io/badge/10-%28%2B10%29-red.svg"></a></td><td align="center"><a href="https://github.com/reagento/adaptix/pull/313/files#diff-956a90ff8f78f3db32dbcdff3104fc1a9a1462fae75a0c564e61d5909c212f51"><img title="The coverage rate of src/adaptix/_internal/conversion/coercer_provider.py is 92.8% (129/139). The file did not seem to exist on the base branch." src="https://img.shields.io/badge/92%25-%28129/139%29-brightgreen.svg"></a></td><td align="center"><a href="https://github.com/reagento/adaptix/pull/313/files#diff-956a90ff8f78f3db32dbcdff3104fc1a9a1462fae75a0c564e61d5909c212f51"><img title="In this PR, 3 new statements are added to src/adaptix/_internal/conversion/coercer_provider.py, 3 of which are covered (100%)." src="https://img.shields.io/badge/100%25-%283/3%29-brightgreen.svg"></a></td><td></td><tr>
<td colspan="6">&nbsp;&nbsp;<b>src/adaptix/_internal/morphing</b></td><tr>
<td>&nbsp;&nbsp;<a href="https://github.com/reagento/adaptix/pull/313/files#diff-f992e5f0c43921e7620f7c41adbf80d546420073aa4805010070691a16b2a31f">generic_provider.py</a></td>
<td align="center"><a href="https://github.com/reagento/adaptix/pull/313/files#diff-f992e5f0c43921e7620f7c41adbf80d546420073aa4805010070691a16b2a31f"><img title="This PR adds 267 statements to src/adaptix/_internal/morphing/generic_provider.py. The file did not seem to exist on the base branch." src="https://img.shields.io/badge/267-%28%2B267%29-007ec6.svg"></a></td><td align="center"><a href="https://github.com/reagento/adaptix/pull/313/files#diff-f992e5f0c43921e7620f7c41adbf80d546420073aa4805010070691a16b2a31f"><img title="This PR adds 6 statements missing coverage to src/adaptix/_internal/morphing/generic_provider.py. The file did not seem to exist on the base branch." src="https://img.shields.io/badge/6-%28%2B6%29-red.svg"></a></td><td align="center"><a href="https://github.com/reagento/adaptix/pull/313/files#diff-f992e5f0c43921e7620f7c41adbf80d546420073aa4805010070691a16b2a31f"><img title="The coverage rate of src/adaptix/_internal/morphing/generic_provider.py is 97.75% (261/267). The file did not seem to exist on the base branch." src="https://img.shields.io/badge/97%25-%28261/267%29-brightgreen.svg"></a></td><td align="center"><a href="https://github.com/reagento/adaptix/pull/313/files#diff-f992e5f0c43921e7620f7c41adbf80d546420073aa4805010070691a16b2a31f"><img title="In this PR, 5 new statements are added to src/adaptix/_internal/morphing/generic_provider.py, 5 of which are covered (100%)." src="https://img.shields.io/badge/100%25-%285/5%29-brightgreen.svg"></a></td><td></td><tr>
<td colspan="6">&nbsp;&nbsp;<b>src/adaptix/_internal/retort</b></td><tr>
<td>&nbsp;&nbsp;<a href="https://github.com/reagento/adaptix/pull/313/files#diff-99b17f2727d3e4cb0530bc395ff10c4445d6ac9b19ac589f6778a1702eb75d70">operating_retort.py</a></td>
<td align="center"><a href="https://github.com/reagento/adaptix/pull/313/files#diff-99b17f2727d3e4cb0530bc395ff10c4445d6ac9b19ac589f6778a1702eb75d70"><img title="This PR adds 99 statements to src/adaptix/_internal/retort/operating_retort.py. The file did not seem to exist on the base branch." src="https://img.shields.io/badge/99-%28%2B99%29-007ec6.svg"></a></td><td align="center"><a href="https://github.com/reagento/adaptix/pull/313/files#diff-99b17f2727d3e4cb0530bc395ff10c4445d6ac9b19ac589f6778a1702eb75d70"><img title="This PR adds 2 statements missing coverage to src/adaptix/_internal/retort/operating_retort.py. The file did not seem to exist on the base branch." src="https://img.shields.io/badge/2-%28%2B2%29-red.svg"></a></td><td align="center"><a href="https://github.com/reagento/adaptix/pull/313/files#diff-99b17f2727d3e4cb0530bc395ff10c4445d6ac9b19ac589f6778a1702eb75d70"><img title="The coverage rate of src/adaptix/_internal/retort/operating_retort.py is 97.97% (97/99). The file did not seem to exist on the base branch." src="https://img.shields.io/badge/97%25-%2897/99%29-brightgreen.svg"></a></td><td align="center"><a href="https://github.com/reagento/adaptix/pull/313/files#diff-99b17f2727d3e4cb0530bc395ff10c4445d6ac9b19ac589f6778a1702eb75d70"><img title="In this PR, 2 new statements are added to src/adaptix/_internal/retort/operating_retort.py, 2 of which are covered (100%)." src="https://img.shields.io/badge/100%25-%282/2%29-brightgreen.svg"></a></td><td></td><tr>
<td colspan="6">&nbsp;&nbsp;<b>src/adaptix/_internal/type_tools</b></td><tr>
<td>&nbsp;&nbsp;<a href="https://github.com/reagento/adaptix/pull/313/files#diff-d4b23bd32c562da8e52fbdd4cda049a1559fb33e0de6ed6043c892677a13462b">basic_utils.py</a></td>
<td align="center"><a href="https://github.com/reagento/adaptix/pull/313/files#diff-d4b23bd32c562da8e52fbdd4cda049a1559fb33e0de6ed6043c892677a13462b"><img title="This PR adds 56 statements to src/adaptix/_internal/type_tools/basic_utils.py. The file did not seem to exist on the base branch." src="https://img.shields.io/badge/56-%28%2B56%29-007ec6.svg"></a></td><td align="center"><a href="https://github.com/reagento/adaptix/pull/313/files#diff-d4b23bd32c562da8e52fbdd4cda049a1559fb33e0de6ed6043c892677a13462b"><img title="This PR adds 0 statements missing coverage to src/adaptix/_internal/type_tools/basic_utils.py. The file did not seem to exist on the base branch." src="https://img.shields.io/badge/0-%28%C2%B10%29-lightgrey.svg"></a></td><td align="center"><a href="https://github.com/reagento/adaptix/pull/313/files#diff-d4b23bd32c562da8e52fbdd4cda049a1559fb33e0de6ed6043c892677a13462b"><img title="The coverage rate of src/adaptix/_internal/type_tools/basic_utils.py is 100% (56/56). The file did not seem to exist on the base branch." src="https://img.shields.io/badge/100%25-%2856/56%29-brightgreen.svg"></a></td><td align="center"><a href="https://github.com/reagento/adaptix/pull/313/files#diff-d4b23bd32c562da8e52fbdd4cda049a1559fb33e0de6ed6043c892677a13462b"><img title="In this PR, 1 new statements are added to src/adaptix/_internal/type_tools/basic_utils.py, 1 of which are covered (100%)." src="https://img.shields.io/badge/100%25-%281/1%29-brightgreen.svg"></a></td><td></td></tbody>
<tfoot>
<tr>
<td><b>Project Total</b></td>
<td align="center"><a href="https://github.com/reagento/adaptix/pull/313/files#diff-4b0bf2efa3367c0072ac2bf1e234e703dc46b47aaa4fe9d3b01737b1a15752b1"><img title="This PR adds 7647 statements to the whole project. The file did not seem to exist on the base branch." src="https://img.shields.io/badge/7647-%28%2B7647%29-007ec6.svg"></a></td><td align="center"><a href="https://github.com/reagento/adaptix/pull/313/files#diff-4b0bf2efa3367c0072ac2bf1e234e703dc46b47aaa4fe9d3b01737b1a15752b1"><img title="This PR adds 552 statements missing coverage to the whole project. The file did not seem to exist on the base branch." src="https://img.shields.io/badge/552-%28%2B552%29-red.svg"></a></td><td align="center"><a href="https://github.com/reagento/adaptix/pull/313/files#diff-4b0bf2efa3367c0072ac2bf1e234e703dc46b47aaa4fe9d3b01737b1a15752b1"><img title="The coverage rate of the whole project is 92.78% (7095/7647). The file did not seem to exist on the base branch." src="https://img.shields.io/badge/92%25-%287095/7647%29-brightgreen.svg"></a></td><td align="center"><a href="https://github.com/reagento/adaptix/pull/313/files#diff-4b0bf2efa3367c0072ac2bf1e234e703dc46b47aaa4fe9d3b01737b1a15752b1"><img title="In this PR, 17 new statements are added to the whole project, 17 of which are covered (100%)." src="https://img.shields.io/badge/100%25-%2817/17%29-brightgreen.svg"></a></td><td>&nbsp;</td>
</tr>
</tfoot>
</table>

<sub>

This report was generated by [python-coverage-comment-action](https://github.com/py-cov-action/python-coverage-comment-action)

</sub>
</details>



<!-- This comment was produced by python-coverage-comment-action -->

Fix patch:
diff --git a/docs/changelog/fragments/+provider-routing.other.rst b/docs/changelog/fragments/+provider-routing.other.rst
new file mode 100644
index 00000000..b0265f35
--- /dev/null
+++ b/docs/changelog/fragments/+provider-routing.other.rst
@@ -0,0 +1,2 @@
+Refactor internal provider routing system. It becomes more simple and readable.
+The loader generation has been accelerated by 20%.
diff --git a/pyproject.toml b/pyproject.toml
index dc21b88a..9ccad097 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -80,6 +80,11 @@ branch = true
 relative_files = true
 include = ["src/**"]
 
+[tool.coverage.report]
+exclude_also = [
+    "^\\s*\\.\\.\\.\\s*(:?#.*)?$",
+]
+
 #   ┌             ┐
 #   │   LINTING   │
 #   └             ┘
diff --git a/src/adaptix/_internal/conversion/coercer_provider.py b/src/adaptix/_internal/conversion/coercer_provider.py
index edc0eea2..b3ea5414 100644
--- a/src/adaptix/_internal/conversion/coercer_provider.py
+++ b/src/adaptix/_internal/conversion/coercer_provider.py
@@ -5,10 +5,10 @@
 from typing import Any, Callable, Tuple, Union, final
 
 from ..common import Coercer, OneArgCoercer, TypeHint
+from ..morphing.utils import try_normalize_type
 from ..provider.essential import CannotProvide, Mediator
 from ..provider.loc_stack_filtering import LocStackChecker
 from ..provider.location import GenericParamLoc
-from ..provider.request_cls import try_normalize_type
 from ..special_cases_optimization import as_is_stub, as_is_stub_with_ctx
 from ..type_tools import BaseNormType, is_generic, is_parametrized, is_subclass_soft, normalize_type, strip_tags
 from .provider_template import CoercerProvider
diff --git a/src/adaptix/_internal/conversion/converter_provider.py b/src/adaptix/_internal/conversion/converter_provider.py
index 7f49cf7a..3891fa71 100644
--- a/src/adaptix/_internal/conversion/converter_provider.py
+++ b/src/adaptix/_internal/conversion/converter_provider.py
@@ -12,8 +12,8 @@
 from ..model_tools.definitions import DefaultValue, NoDefault
 from ..morphing.model.basic_gen import compile_closure_with_globals_capturing, fetch_code_gen_hook
 from ..provider.essential import CannotProvide, Mediator
-from ..provider.location import FieldLoc
-from ..provider.request_cls import LocStack, TypeHintLoc
+from ..provider.loc_stack_filtering import LocStack
+from ..provider.location import FieldLoc, TypeHintLoc
 from .provider_template import ConverterProvider
 
 
diff --git a/src/adaptix/_internal/conversion/linking_provider.py b/src/adaptix/_internal/conversion/linking_provider.py
index 043cd1a0..1bb0f1cf 100644
--- a/src/adaptix/_internal/conversion/linking_provider.py
+++ b/src/adaptix/_internal/conversion/linking_provider.py
@@ -7,8 +7,8 @@
 from ..provider.essential import CannotProvide, Mediator, mandatory_apply_by_iterable
 from ..provider.fields import input_field_to_loc
 from ..provider.loc_stack_filtering import LocStackChecker
+from ..provider.loc_stack_tools import format_loc_stack
 from ..provider.location import FieldLoc
-from ..provider.request_cls import format_loc_stack
 from ..utils import add_note
 from .provider_template import LinkingProvider
 from .request_cls import (
diff --git a/src/adaptix/_internal/conversion/model_coercer_provider.py b/src/adaptix/_internal/conversion/model_coercer_provider.py
index 0189ddcd..8729f9d3 100644
--- a/src/adaptix/_internal/conversion/model_coercer_provider.py
+++ b/src/adaptix/_internal/conversion/model_coercer_provider.py
@@ -30,8 +30,8 @@
 from ..morphing.model.basic_gen import compile_closure_with_globals_capturing, fetch_code_gen_hook
 from ..provider.essential import CannotProvide, Mediator, mandatory_apply_by_iterable
 from ..provider.fields import input_field_to_loc, output_field_to_loc
+from ..provider.loc_stack_filtering import LocStack
 from ..provider.location import AnyLoc, InputFieldLoc, InputFuncFieldLoc, OutputFieldLoc
-from ..provider.request_cls import LocStack
 from ..provider.shape_provider import InputShapeRequest, OutputShapeRequest, provide_generic_resolved_shape
 from ..utils import add_note
 from .provider_template import CoercerProvider
diff --git a/src/adaptix/_internal/conversion/policy_provider.py b/src/adaptix/_internal/conversion/policy_provider.py
index 4c1ec800..82ea46d7 100644
--- a/src/adaptix/_internal/conversion/policy_provider.py
+++ b/src/adaptix/_internal/conversion/policy_provider.py
@@ -1,13 +1,13 @@
 from ..provider.essential import Mediator
-from ..provider.static_provider import StaticProvider, static_provision_action
+from ..provider.methods_provider import MethodsProvider, method_handler
 from .request_cls import UnlinkedOptionalPolicy, UnlinkedOptionalPolicyRequest
 
 
-class UnlinkedOptionalPolicyProvider(StaticProvider):
+class UnlinkedOptionalPolicyProvider(MethodsProvider):
     def __init__(self, *, is_allowed: bool):
         self._is_allowed = is_allowed
 
-    @static_provision_action
+    @method_handler
     def _unlinked_optional_policy(
         self,
         mediator: Mediator,
diff --git a/src/adaptix/_internal/conversion/provider_template.py b/src/adaptix/_internal/conversion/provider_template.py
index 2f015336..a7f7d186 100644
--- a/src/adaptix/_internal/conversion/provider_template.py
+++ b/src/adaptix/_internal/conversion/provider_template.py
@@ -3,13 +3,13 @@
 
 from ..common import Coercer, Converter
 from ..provider.essential import Mediator
-from ..provider.static_provider import StaticProvider, static_provision_action
+from ..provider.methods_provider import MethodsProvider, method_handler
 from .request_cls import CoercerRequest, ConverterRequest, LinkingRequest, LinkingResult
 
 
-class ConverterProvider(StaticProvider, ABC):
+class ConverterProvider(MethodsProvider, ABC):
     @final
-    @static_provision_action
+    @method_handler
     def _outer_provide_converter(self, mediator: Mediator, request: ConverterRequest):
         return self._provide_converter(mediator, request)
 
@@ -18,15 +18,15 @@ def _provide_converter(self, mediator: Mediator, request: ConverterRequest) -> C
         ...
 
 
-class CoercerProvider(StaticProvider, ABC):
-    @static_provision_action
+class CoercerProvider(MethodsProvider, ABC):
+    @method_handler
     @abstractmethod
     def _provide_coercer(self, mediator: Mediator, request: CoercerRequest) -> Coercer:
         ...
 
 
-class LinkingProvider(StaticProvider, ABC):
-    @static_provision_action
+class LinkingProvider(MethodsProvider, ABC):
+    @method_handler
     @abstractmethod
     def _provide_linking(self, mediator: Mediator, request: LinkingRequest) -> LinkingResult:
         ...
diff --git a/src/adaptix/_internal/conversion/request_cls.py b/src/adaptix/_internal/conversion/request_cls.py
index 1ad526f1..f22817c8 100644
--- a/src/adaptix/_internal/conversion/request_cls.py
+++ b/src/adaptix/_internal/conversion/request_cls.py
@@ -5,8 +5,9 @@
 from ..common import Coercer, VarTuple
 from ..model_tools.definitions import DefaultFactory, DefaultValue, InputField, ParamKind
 from ..provider.essential import Request
+from ..provider.loc_stack_filtering import LocStack
+from ..provider.located_request import LocatedRequest
 from ..provider.location import FieldLoc, GenericParamLoc, InputFieldLoc, InputFuncFieldLoc, OutputFieldLoc, TypeHintLoc
-from ..provider.request_cls import LocatedRequest, LocStack
 
 
 @dataclass(frozen=True)
diff --git a/src/adaptix/_internal/conversion/request_filtering.py b/src/adaptix/_internal/conversion/request_filtering.py
index ffc58973..a6598024 100644
--- a/src/adaptix/_internal/conversion/request_filtering.py
+++ b/src/adaptix/_internal/conversion/request_filtering.py
@@ -1,5 +1,4 @@
-from adaptix._internal.provider.loc_stack_filtering import DirectMediator, LocStackChecker
-from adaptix._internal.provider.request_cls import LocStack
+from adaptix._internal.provider.loc_stack_filtering import DirectMediator, LocStack, LocStackChecker
 
 
 class FromCtxParam(LocStackChecker):
diff --git a/src/adaptix/_internal/integrations/pydantic/native.py b/src/adaptix/_internal/integrations/pydantic/native.py
index 5f2c3dd7..be9b3e1d 100644
--- a/src/adaptix/_internal/integrations/pydantic/native.py
+++ b/src/adaptix/_internal/integrations/pydantic/native.py
@@ -33,7 +33,7 @@ def __init__(
     def _skip_omitted(self, mapping: Mapping[str, T]) -> Mapping[str, T]:
         return {k: v for k, v in mapping.items() if v != Omitted()}
 
-    def _provide_loader(self, mediator: Mediator, request: LoaderRequest) -> Loader:
+    def provide_loader(self, mediator: Mediator, request: LoaderRequest) -> Loader:
         validation_params = self._skip_omitted(self._validation_params)
         validator = TypeAdapter(request.last_loc.type, config=self._config).validator.validate_python
 
@@ -54,7 +54,7 @@ def native_pydantic_loader(data):
 
         return native_pydantic_loader
 
-    def _provide_dumper(self, mediator: Mediator, request: DumperRequest) -> Dumper:
+    def provide_dumper(self, mediator: Mediator, request: DumperRequest) -> Dumper:
         serialization_params = self._skip_omitted(self._serialization_params)
         serializer = TypeAdapter(request.last_loc.type, config=self._config).serializer.to_python
 
diff --git a/src/adaptix/_internal/morphing/concrete_provider.py b/src/adaptix/_internal/morphing/concrete_provider.py
index 2726a67b..295d24dc 100644
--- a/src/adaptix/_internal/morphing/concrete_provider.py
+++ b/src/adaptix/_internal/morphing/concrete_provider.py
@@ -13,13 +13,12 @@
 from ..feature_requirement import HAS_PY_311, HAS_SELF_TYPE
 from ..provider.essential import CannotProvide, Mediator
 from ..provider.loc_stack_filtering import P, create_loc_stack_checker
-from ..provider.provider_template import for_predicate
-from ..provider.request_cls import LocatedRequest, StrictCoercionRequest, find_owner_with_field
-from ..provider.static_provider import static_provision_action
+from ..provider.loc_stack_tools import find_owner_with_field
+from ..provider.located_request import LocatedRequest, for_predicate
 from ..special_cases_optimization import as_is_stub
 from .load_error import FormatMismatchLoadError, TypeLoadError, ValueLoadError
-from .provider_template import DumperProvider, LoaderProvider, ProviderWithAttachableLSC
-from .request_cls import DumperRequest, LoaderRequest
+from .provider_template import DumperProvider, LoaderProvider
+from .request_cls import DumperRequest, LoaderRequest, StrictCoercionRequest
 
 T = TypeVar("T")
 
@@ -31,7 +30,7 @@ class IsoFormatProvider(LoaderProvider, DumperProvider):
     def __post_init__(self):
         self._loc_stack_checker = create_loc_stack_checker(self.cls)
 
-    def _provide_loader(self, mediator: Mediator, request: LoaderRequest) -> Loader:
+    def provide_loader(self, mediator: Mediator, request: LoaderRequest) -> Loader:
         raw_loader = self.cls.fromisoformat
 
         def isoformat_loader(data):
@@ -44,7 +43,7 @@ def isoformat_loader(data):
 
         return isoformat_loader
 
-    def _provide_dumper(self, mediator: Mediator, request: DumperRequest) -> Dumper:
+    def provide_dumper(self, mediator: Mediator, request: DumperRequest) -> Dumper:
         return self.cls.isoformat
 
 
@@ -53,7 +52,7 @@ def _provide_dumper(self, mediator: Mediator, request: DumperRequest) -> Dumper:
 class DatetimeFormatProvider(LoaderProvider, DumperProvider):
     format: str
 
-    def _provide_loader(self, mediator: Mediator, request: LoaderRequest) -> Loader:
+    def provide_loader(self, mediator: Mediator, request: LoaderRequest) -> Loader:
         fmt = self.format
 
         def datetime_format_loader(data):
@@ -66,7 +65,7 @@ def datetime_format_loader(data):
 
         return datetime_format_loader
 
-    def _provide_dumper(self, mediator: Mediator, request: DumperRequest) -> Dumper:
+    def provide_dumper(self, mediator: Mediator, request: DumperRequest) -> Dumper:
         fmt = self.format
 
         def datetime_format_dumper(data: datetime):
@@ -79,7 +78,7 @@ def datetime_format_dumper(data: datetime):
 class SecondsTimedeltaProvider(LoaderProvider, DumperProvider):
     _OK_TYPES = (int, float, Decimal)
 
-    def _provide_loader(self, mediator: Mediator, request: LoaderRequest) -> Loader:
+    def provide_loader(self, mediator: Mediator, request: LoaderRequest) -> Loader:
         ok_types = self._OK_TYPES
 
         def timedelta_loader(data):
@@ -89,7 +88,7 @@ def timedelta_loader(data):
 
         return timedelta_loader
 
-    def _provide_dumper(self, mediator: Mediator, request: DumperRequest) -> Dumper:
+    def provide_dumper(self, mediator: Mediator, request: DumperRequest) -> Dumper:
         return timedelta.total_seconds
 
 
@@ -101,15 +100,15 @@ def none_loader(data):
 
 @for_predicate(None)
 class NoneProvider(LoaderProvider, DumperProvider):
-    def _provide_loader(self, mediator: Mediator, request: LoaderRequest) -> Loader:
+    def provide_loader(self, mediator: Mediator, request: LoaderRequest) -> Loader:
         return none_loader
 
-    def _provide_dumper(self, mediator: Mediator, request: DumperRequest) -> Dumper:
+    def provide_dumper(self, mediator: Mediator, request: DumperRequest) -> Dumper:
         return as_is_stub
 
 
 class Base64DumperMixin(DumperProvider):
-    def _provide_dumper(self, mediator: Mediator, request: DumperRequest) -> Dumper:
+    def provide_dumper(self, mediator: Mediator, request: DumperRequest) -> Dumper:
         def bytes_base64_dumper(data):
             return b2a_base64(data, newline=False).decode("ascii")
 
@@ -121,7 +120,7 @@ def bytes_base64_dumper(data):
 
 @for_predicate(bytes)
 class BytesBase64Provider(LoaderProvider, Base64DumperMixin):
-    def _provide_loader(self, mediator: Mediator, request: LoaderRequest) -> Loader:
+    def provide_loader(self, mediator: Mediator, request: LoaderRequest) -> Loader:
         def bytes_base64_loader(data):
             try:
                 encoded = data.encode("ascii")
@@ -141,15 +140,15 @@ def bytes_base64_loader(data):
 
 @for_predicate(BytesIO)
 class BytesIOBase64Provider(BytesBase64Provider):
-    def _provide_loader(self, mediator: Mediator, request: LoaderRequest) -> Loader:
-        bytes_base64_loader = super()._provide_loader(mediator, request)
+    def provide_loader(self, mediator: Mediator, request: LoaderRequest) -> Loader:
+        bytes_base64_loader = super().provide_loader(mediator, request)
 
         def bytes_io_base64_loader(data):
             return BytesIO(bytes_base64_loader(data))
 
         return bytes_io_base64_loader
 
-    def _provide_dumper(self, mediator: Mediator, request: DumperRequest) -> Dumper:
+    def provide_dumper(self, mediator: Mediator, request: DumperRequest) -> Dumper:
         def bytes_io_base64_dumper(data: BytesIO):
             return b2a_base64(data.getvalue(), newline=False).decode("ascii")
 
@@ -158,7 +157,7 @@ def bytes_io_base64_dumper(data: BytesIO):
 
 @for_predicate(typing.IO[bytes])
 class IOBytesBase64Provider(BytesIOBase64Provider):
-    def _provide_dumper(self, mediator: Mediator, request: DumperRequest) -> Dumper:
+    def provide_dumper(self, mediator: Mediator, request: DumperRequest) -> Dumper:
         def io_bytes_base64_dumper(data: typing.IO[bytes]):
             if data.seekable():
                 data.seek(0)
@@ -172,8 +171,8 @@ def io_bytes_base64_dumper(data: typing.IO[bytes]):
 class BytearrayBase64Provider(LoaderProvider, Base64DumperMixin):
     _BYTES_PROVIDER = BytesBase64Provider()
 
-    def _provide_loader(self, mediator: Mediator, request: LoaderRequest) -> Loader:
-        bytes_loader = self._BYTES_PROVIDER.apply_provider(
+    def provide_loader(self, mediator: Mediator, request: LoaderRequest) -> Loader:
+        bytes_loader = self._BYTES_PROVIDER.provide_loader(
             mediator,
             replace(request, loc_stack=request.loc_stack.replace_last_type(bytes)),
         )
@@ -193,7 +192,7 @@ class RegexPatternProvider(LoaderProvider, DumperProvider):
     def __init__(self, flags: re.RegexFlag = re.RegexFlag(0)):
         self.flags = flags
 
-    def _provide_loader(self, mediator: Mediator, request: LoaderRequest) -> Loader:
+    def provide_loader(self, mediator: Mediator, request: LoaderRequest) -> Loader:
         flags = self.flags
         re_compile = re.compile
 
@@ -208,7 +207,7 @@ def regex_loader(data):
 
         return regex_loader
 
-    def _provide_dumper(self, mediator: Mediator, request: DumperRequest) -> Dumper:
+    def provide_dumper(self, mediator: Mediator, request: DumperRequest) -> Dumper:
         return _regex_dumper
 
 
@@ -219,7 +218,7 @@ def __init__(self, pred: Type[T], strict_coercion_loader: Loader[T], lax_coercio
         self._strict_coercion_loader = strict_coercion_loader
         self._lax_coercion_loader = lax_coercion_loader
 
-    def _provide_loader(self, mediator: Mediator, request: LoaderRequest) -> Loader:
+    def provide_loader(self, mediator: Mediator, request: LoaderRequest) -> Loader:
         strict_coercion = mediator.mandatory_provide(StrictCoercionRequest(loc_stack=request.loc_stack))
         return self._strict_coercion_loader if strict_coercion else self._lax_coercion_loader
 
@@ -383,11 +382,8 @@ def complex_lax_coercion_loader(data):
 
 
 @for_predicate(typing.Self if HAS_SELF_TYPE else ~P.ANY)
-class SelfTypeProvider(ProviderWithAttachableLSC):
-    @static_provision_action
-    def _provide_substitute(self, mediator: Mediator, request: LocatedRequest) -> Loader:
-        self._apply_loc_stack_checker(mediator, request)
-
+class SelfTypeProvider(LoaderProvider, DumperProvider):
+    def _substituting_provide(self, mediator: Mediator, request: LocatedRequest) -> Loader:
         try:
             owner_loc, _field_loc = find_owner_with_field(request.loc_stack)
         except ValueError:
@@ -404,12 +400,18 @@ def _provide_substitute(self, mediator: Mediator, request: LocatedRequest) -> Lo
             ),
         )
 
+    def provide_loader(self, mediator: Mediator[Loader], request: LoaderRequest) -> Loader:
+        return self._substituting_provide(mediator, request)
+
+    def provide_dumper(self, mediator: Mediator[Dumper], request: DumperRequest) -> Dumper:
+        return self._substituting_provide(mediator, request)
+
 
 @for_predicate(typing.LiteralString if HAS_PY_311 else ~P.ANY)
 class LiteralStringProvider(LoaderProvider, DumperProvider):
-    def _provide_loader(self, mediator: Mediator, request: LoaderRequest) -> Loader:
+    def provide_loader(self, mediator: Mediator, request: LoaderRequest) -> Loader:
         strict_coercion = mediator.mandatory_provide(StrictCoercionRequest(loc_stack=request.loc_stack))
         return str_strict_coercion_loader if strict_coercion else str  # type: ignore[return-value]
 
-    def _provide_dumper(self, mediator: Mediator, request: DumperRequest) -> Dumper:
+    def provide_dumper(self, mediator: Mediator, request: DumperRequest) -> Dumper:
         return as_is_stub
diff --git a/src/adaptix/_internal/morphing/constant_length_tuple_provider.py b/src/adaptix/_internal/morphing/constant_length_tuple_provider.py
index 3f9bc188..488751cb 100644
--- a/src/adaptix/_internal/morphing/constant_length_tuple_provider.py
+++ b/src/adaptix/_internal/morphing/constant_length_tuple_provider.py
@@ -8,9 +8,8 @@
 from ..definitions import DebugTrail
 from ..feature_requirement import HAS_UNPACK
 from ..provider.essential import CannotProvide, Mediator
+from ..provider.located_request import for_predicate
 from ..provider.location import GenericParamLoc
-from ..provider.provider_template import for_predicate
-from ..provider.request_cls import DebugTrailRequest, StrictCoercionRequest, get_type_from_request, try_normalize_type
 from ..struct_trail import append_trail, render_trail_as_note
 from .load_error import (
     AggregateLoadError,
@@ -21,15 +20,16 @@
     TypeLoadError,
 )
 from .provider_template import DumperProvider, LoaderProvider
-from .request_cls import DumperRequest, LoaderRequest
+from .request_cls import DebugTrailRequest, DumperRequest, LoaderRequest, StrictCoercionRequest
+from .utils import try_normalize_type
 
 CollectionsMapping = collections.abc.Mapping
 
 
 @for_predicate(Tuple)
 class ConstantLengthTupleProvider(LoaderProvider, DumperProvider):
-    def _provide_loader(self, mediator: Mediator, request: LoaderRequest) -> Loader:
-        norm = try_normalize_type(get_type_from_request(request))
+    def provide_loader(self, mediator: Mediator, request: LoaderRequest) -> Loader:
+        norm = try_normalize_type(request.last_loc.type)
         if len(norm.args) > 1 and norm.args[1] == Ellipsis:
             raise CannotProvide
         if HAS_UNPACK and any(arg.origin == typing.Unpack for arg in norm.args if arg != Ellipsis):
@@ -214,8 +214,8 @@ def dt_disable_sc_loader(data):
 
         return dt_disable_sc_loader
 
-    def _provide_dumper(self, mediator: Mediator, request: DumperRequest) -> Dumper:
-        norm = try_normalize_type(get_type_from_request(request))
+    def provide_dumper(self, mediator: Mediator, request: DumperRequest) -> Dumper:
+        norm = try_normalize_type(request.last_loc.type)
         if len(norm.args) > 1 and norm.args[1] == Ellipsis:
             raise CannotProvide
         if HAS_UNPACK and any(arg.origin == typing.Unpack for arg in norm.args if arg != Ellipsis):
diff --git a/src/adaptix/_internal/morphing/dict_provider.py b/src/adaptix/_internal/morphing/dict_provider.py
index 5036a033..55e1fb07 100644
--- a/src/adaptix/_internal/morphing/dict_provider.py
+++ b/src/adaptix/_internal/morphing/dict_provider.py
@@ -8,13 +8,13 @@
 from ..definitions import DebugTrail
 from ..morphing.provider_template import DumperProvider, LoaderProvider
 from ..provider.essential import Mediator
+from ..provider.located_request import LocatedRequest, for_predicate
 from ..provider.location import GenericParamLoc
-from ..provider.provider_template import for_predicate
-from ..provider.request_cls import DebugTrailRequest, LocatedRequest, get_type_from_request, try_normalize_type
 from ..struct_trail import ItemKey, append_trail, render_trail_as_note
 from ..type_tools import BaseNormType
 from .load_error import AggregateLoadError, LoadError, TypeLoadError
-from .request_cls import DumperRequest, LoaderRequest
+from .request_cls import DebugTrailRequest, DumperRequest, LoaderRequest
+from .utils import try_normalize_type
 
 CollectionsMapping = collections.abc.Mapping
 
@@ -22,10 +22,10 @@
 @for_predicate(Dict)
 class DictProvider(LoaderProvider, DumperProvider):
     def _extract_key_value(self, request: LocatedRequest) -> Tuple[BaseNormType, BaseNormType]:
-        norm = try_normalize_type(get_type_from_request(request))
+        norm = try_normalize_type(request.last_loc.type)
         return norm.args
 
-    def _provide_loader(self, mediator: Mediator, request: LoaderRequest) -> Loader:
+    def provide_loader(self, mediator: Mediator, request: LoaderRequest) -> Loader:
         key, value = self._extract_key_value(request)
 
         key_loader = mediator.mandatory_provide(
@@ -154,7 +154,7 @@ def dict_loader_dt_all(data):
 
         return dict_loader_dt_all
 
-    def _provide_dumper(self, mediator: Mediator, request: DumperRequest) -> Dumper:
+    def provide_dumper(self, mediator: Mediator, request: DumperRequest) -> Dumper:
         key, value = self._extract_key_value(request)
 
         key_dumper = mediator.mandatory_provide(
@@ -274,13 +274,13 @@ def __init__(self, default_factory: Optional[Callable] = None):
         self.default_factory = default_factory
 
     def _extract_key_value(self, request: LocatedRequest) -> Tuple[BaseNormType, BaseNormType]:
-        norm = try_normalize_type(get_type_from_request(request))
+        norm = try_normalize_type(request.last_loc.type)
         return norm.args
 
-    def _provide_loader(self, mediator: Mediator, request: LoaderRequest) -> Loader:
+    def provide_loader(self, mediator: Mediator, request: LoaderRequest) -> Loader:
         key, value = self._extract_key_value(request)
         dict_type_hint = Dict[key.source, value.source]  # type: ignore[misc, name-defined]
-        dict_loader = self._DICT_PROVIDER.apply_provider(
+        dict_loader = self._DICT_PROVIDER.provide_loader(
             mediator,
             replace(request, loc_stack=request.loc_stack.replace_last_type(dict_type_hint)),
         )
@@ -291,11 +291,11 @@ def defaultdict_loader(data):
 
         return defaultdict_loader
 
-    def _provide_dumper(self, mediator: Mediator, request: DumperRequest) -> Dumper:
+    def provide_dumper(self, mediator: Mediator, request: DumperRequest) -> Dumper:
         key, value = self._extract_key_value(request)
         dict_type_hint = Dict[key.source, value.source]  # type: ignore[misc, name-defined]
 
-        return self._DICT_PROVIDER.apply_provider(
+        return self._DICT_PROVIDER.provide_dumper(
             mediator,
             replace(request, loc_stack=request.loc_stack.replace_last_type(dict_type_hint)),
         )
diff --git a/src/adaptix/_internal/morphing/enum_provider.py b/src/adaptix/_internal/morphing/enum_provider.py
index 5fd39a30..865327ec 100644
--- a/src/adaptix/_internal/morphing/enum_provider.py
+++ b/src/adaptix/_internal/morphing/enum_provider.py
@@ -10,9 +10,9 @@
 from ..morphing.provider_template import DumperProvider, LoaderProvider
 from ..name_style import NameStyle, convert_snake_style
 from ..provider.essential import CannotProvide, Mediator
-from ..provider.loc_stack_filtering import DirectMediator, LastLocMapChecker
-from ..provider.provider_template import for_predicate
-from ..provider.request_cls import StrictCoercionRequest, TypeHintLoc, get_type_from_request
+from ..provider.loc_stack_filtering import DirectMediator, LastLocChecker
+from ..provider.located_request import for_predicate
+from ..provider.location import TypeHintLoc
 from ..type_tools import is_subclass_soft, normalize_type
 from .load_error import (
     BadVariantLoadError,
@@ -23,7 +23,7 @@
     OutOfRangeLoadError,
     TypeLoadError,
 )
-from .request_cls import DumperRequest, LoaderRequest
+from .request_cls import DumperRequest, LoaderRequest, StrictCoercionRequest
 
 EnumT = TypeVar("EnumT", bound=Enum)
 FlagT = TypeVar("FlagT", bound=Flag)
@@ -73,7 +73,7 @@ def _generate_mapping(self, cases: Iterable[EnumT]) -> Mapping[EnumT, str]:
         return result
 
 
-class AnyEnumLSC(LastLocMapChecker):
+class AnyEnumLSC(LastLocChecker):
     def _check_location(self, mediator: DirectMediator, loc: TypeHintLoc) -> bool:
         try:
             norm = normalize_type(loc.type)
@@ -83,7 +83,7 @@ def _check_location(self, mediator: DirectMediator, loc: TypeHintLoc) -> bool:
         return isinstance(origin, EnumMeta) and not is_subclass_soft(origin, Flag)
 
 
-class FlagEnumLSC(LastLocMapChecker):
+class FlagEnumLSC(LastLocChecker):
     def _check_location(self, mediator: DirectMediator, loc: TypeHintLoc) -> bool:
         try:
             norm = normalize_type(loc.type)
@@ -107,8 +107,8 @@ class EnumNameProvider(BaseEnumProvider):
     def __init__(self, mapping_generator: BaseEnumMappingGenerator):
         self._mapping_generator = mapping_generator
 
-    def _provide_loader(self, mediator: Mediator, request: LoaderRequest) -> Loader:
-        enum = get_type_from_request(request)
+    def provide_loader(self, mediator: Mediator, request: LoaderRequest) -> Loader:
+        enum = request.last_loc.type
         mapping = self._mapping_generator.generate_for_loading(enum.__members__.values())
         variants = list(mapping.keys())
 
@@ -122,8 +122,8 @@ def enum_loader(data):
 
         return enum_loader
 
-    def _provide_dumper(self, mediator: Mediator, request: DumperRequest) -> Dumper:
-        enum = get_type_from_request(request)
+    def provide_dumper(self, mediator: Mediator, request: DumperRequest) -> Dumper:
+        enum = request.last_loc.type
         mapping = self._mapping_generator.generate_for_dumping(enum.__members__.values())
 
         def enum_dumper(data: Enum) -> str:
@@ -136,8 +136,8 @@ class EnumValueProvider(BaseEnumProvider):
     def __init__(self, value_type: TypeHint):
         self._value_type = value_type
 
-    def _provide_loader(self, mediator: Mediator, request: LoaderRequest) -> Loader:
-        enum = get_type_from_request(request)
+    def provide_loader(self, mediator: Mediator, request: LoaderRequest) -> Loader:
+        enum = request.last_loc.type
         value_loader = mediator.mandatory_provide(
             LoaderRequest(
                 loc_stack=request.loc_stack.append_with(
@@ -155,7 +155,7 @@ def enum_loader(data):
 
         return enum_loader
 
-    def _provide_dumper(self, mediator: Mediator, request: DumperRequest) -> Dumper:
+    def provide_dumper(self, mediator: Mediator, request: DumperRequest) -> Dumper:
         value_dumper = mediator.mandatory_provide(
             DumperRequest(
                 loc_stack=request.loc_stack.append_with(
@@ -175,8 +175,8 @@ class EnumExactValueProvider(BaseEnumProvider):
     by their value without any processing
     """
 
-    def _provide_loader(self, mediator: Mediator, request: LoaderRequest) -> Loader:
-        return self._make_loader(get_type_from_request(request))
+    def provide_loader(self, mediator: Mediator, request: LoaderRequest) -> Loader:
+        return self._make_loader(request.last_loc.type)
 
     def _make_loader(self, enum):
         variants = [case.value for case in enum]
@@ -216,8 +216,8 @@ def _get_exact_value_to_member(self, enum: Type[Enum]) -> Optional[Mapping[Any,
 
         return value_to_member
 
-    def _provide_dumper(self, mediator: Mediator, request: DumperRequest) -> Dumper:
-        member_to_value = {member: member.value for member in get_type_from_request(request)}
+    def provide_dumper(self, mediator: Mediator, request: DumperRequest) -> Dumper:
+        member_to_value = {member: member.value for member in request.last_loc.type}
 
         def enum_exact_value_dumper(data):
             return member_to_value[data]
@@ -226,8 +226,8 @@ def enum_exact_value_dumper(data):
 
 
 class FlagByExactValueProvider(BaseFlagProvider):
-    def _provide_loader(self, mediator: Mediator, request: LoaderRequest) -> Loader:
-        enum = get_type_from_request(request)
+    def provide_loader(self, mediator: Mediator, request: LoaderRequest) -> Loader:
+        enum = request.last_loc.type
         flag_mask = reduce(or_, enum.__members__.values()).value
 
         if flag_mask < 0:
@@ -258,7 +258,7 @@ def flag_loader(data):
 
         return flag_loader
 
-    def _provide_dumper(self, mediator: Mediator, request: DumperRequest) -> Dumper:
+    def provide_dumper(self, mediator: Mediator, request: DumperRequest) -> Dumper:
         def flag_exact_value_dumper(data):
             return data.value
 
@@ -288,8 +288,8 @@ def _get_cases(self, enum: Type[FlagT]) -> Sequence[FlagT]:
             return list(enum.__members__.values())
         return _extract_non_compound_cases_from_flag(enum)
 
-    def _provide_loader(self, mediator: Mediator, request: LoaderRequest) -> Loader:
-        enum = get_type_from_request(request)
+    def provide_loader(self, mediator: Mediator, request: LoaderRequest) -> Loader:
+        enum = request.last_loc.type
 
         strict_coercion = mediator.mandatory_provide(StrictCoercionRequest(loc_stack=request.loc_stack))
         allow_single_value = self._allow_single_value
@@ -338,8 +338,8 @@ def flag_loader(data) -> Flag:
 
         return flag_loader
 
-    def _provide_dumper(self, mediator: Mediator, request: DumperRequest) -> Dumper:
-        enum = get_type_from_request(request)
+    def provide_dumper(self, mediator: Mediator, request: DumperRequest) -> Dumper:
+        enum = request.last_loc.type
 
         cases = self._get_cases(enum)
         need_to_reverse = self._allow_compound and cases != _extract_non_compound_cases_from_flag(enum)
diff --git a/src/adaptix/_internal/morphing/facade/provider.py b/src/adaptix/_internal/morphing/facade/provider.py
index 2da3eb03..439a31cc 100644
--- a/src/adaptix/_internal/morphing/facade/provider.py
+++ b/src/adaptix/_internal/morphing/facade/provider.py
@@ -19,9 +19,9 @@
     create_loc_stack_checker,
 )
 from ...provider.overlay_schema import OverlayProvider
-from ...provider.provider_template import ValueProvider
 from ...provider.provider_wrapper import Chain, ChainingProvider
 from ...provider.shape_provider import PropertyExtender
+from ...provider.value_provider import ValueProvider
 from ...special_cases_optimization import as_is_stub
 from ...utils import Omittable, Omitted
 from ..dict_provider import DefaultDictProvider
@@ -151,9 +151,9 @@ def _name_mapping_convert_map(name_map: Omittable[NameMap]) -> VarTuple[Provider
         else:
             pred, value = element
             result.append(
-                FuncNameMappingProvider(create_loc_stack_checker(pred), value)
+                bound(pred, FuncNameMappingProvider(value))
                 if callable(value) else
-                ConstNameMappingProvider(create_loc_stack_checker(pred), value),
+                bound(pred, ConstNameMappingProvider(value)),
             )
     return tuple(result)
 
diff --git a/src/adaptix/_internal/morphing/facade/retort.py b/src/adaptix/_internal/morphing/facade/retort.py
index f4983613..3edfb184 100644
--- a/src/adaptix/_internal/morphing/facade/retort.py
+++ b/src/adaptix/_internal/morphing/facade/retort.py
@@ -11,10 +11,10 @@
 from ...common import Dumper, Loader, TypeHint, VarTuple
 from ...definitions import DebugTrail
 from ...provider.essential import Provider, Request
-from ...provider.loc_stack_filtering import P
-from ...provider.provider_template import ValueProvider
-from ...provider.request_cls import DebugTrailRequest, LocStack, StrictCoercionRequest, TypeHintLoc
+from ...provider.loc_stack_filtering import LocStack, P
+from ...provider.location import TypeHintLoc
 from ...provider.shape_provider import BUILTIN_SHAPE_PROVIDER
+from ...provider.value_provider import ValueProvider
 from ...retort.operating_retort import OperatingRetort
 from ...struct_trail import render_trail_as_note
 from ...type_tools.basic_utils import is_generic_class
@@ -55,7 +55,7 @@
 from ..name_layout.name_mapping import SkipPrivateFieldsNameMappingProvider
 from ..name_layout.provider import BuiltinNameLayoutProvider
 from ..provider_template import ABCProxy
-from ..request_cls import DumperRequest, LoaderRequest
+from ..request_cls import DebugTrailRequest, DumperRequest, LoaderRequest, StrictCoercionRequest
 from .provider import as_is_dumper, as_is_loader, dumper, enum_by_exact_value, flag_by_exact_value, loader, name_mapping
 
 
diff --git a/src/adaptix/_internal/morphing/generic_provider.py b/src/adaptix/_internal/morphing/generic_provider.py
index 04926293..b8a89092 100644
--- a/src/adaptix/_internal/morphing/generic_provider.py
+++ b/src/adaptix/_internal/morphing/generic_provider.py
@@ -1,83 +1,61 @@
 import collections.abc
-from dataclasses import dataclass, replace
+from dataclasses import dataclass
 from enum import Enum
 from os import PathLike
 from pathlib import Path
-from typing import Any, Collection, Dict, Iterable, Literal, Optional, Sequence, Set, Type, Union
+from typing import Any, Collection, Dict, Iterable, Literal, Optional, Sequence, Set, Type, TypeVar, Union
 
-from ..common import Dumper, Loader
+from ..common import Dumper, Loader, TypeHint
 from ..compat import CompatExceptionGroup
 from ..datastructures import ClassDispatcher
 from ..definitions import DebugTrail
 from ..feature_requirement import HAS_PY_39
 from ..provider.essential import CannotProvide, Mediator
-from ..provider.location import GenericParamLoc
-from ..provider.provider_template import for_predicate
-from ..provider.request_cls import (
-    DebugTrailRequest,
-    LocatedRequest,
-    LocStack,
-    StrictCoercionRequest,
-    TypeHintLoc,
-    get_type_from_request,
-    try_normalize_type,
-)
-from ..provider.static_provider import StaticProvider, static_provision_action
+from ..provider.loc_stack_filtering import LocStack
+from ..provider.located_request import LocatedRequestDelegatingProvider, LocatedRequestT, for_predicate
+from ..provider.location import GenericParamLoc, TypeHintLoc
 from ..special_cases_optimization import as_is_stub
 from ..type_tools import BaseNormType, NormTypeAlias, is_new_type, is_subclass_soft, strip_tags
 from .load_error import BadVariantLoadError, LoadError, TypeLoadError, UnionLoadError
 from .provider_template import DumperProvider, LoaderProvider
-from .request_cls import DumperRequest, LoaderRequest
+from .request_cls import DebugTrailRequest, DumperRequest, LoaderRequest, StrictCoercionRequest
+from .utils import try_normalize_type
 
+ResponseT = TypeVar("ResponseT")
 
-class NewTypeUnwrappingProvider(StaticProvider):
-    @static_provision_action
-    def _provide_unwrapping(self, mediator: Mediator, request: LocatedRequest) -> Loader:
-        loc = request.last_loc.cast_or_raise(TypeHintLoc, CannotProvide)
 
-        if not is_new_type(loc.type):
+class NewTypeUnwrappingProvider(LocatedRequestDelegatingProvider):
+    REQUEST_CLASSES = (LoaderRequest, DumperRequest)
+
+    def get_delegated_type(self, mediator: Mediator[LocatedRequestT], request: LocatedRequestT) -> TypeHint:
+        if not is_new_type(request.last_loc.type):
             raise CannotProvide
 
-        return mediator.delegating_provide(
-            replace(
-                request,
-                loc_stack=request.loc_stack.replace_last_type(loc.type.__supertype__),
-            ),
-        )
+        return request.last_loc.type.__supertype__
 
 
-class TypeHintTagsUnwrappingProvider(StaticProvider):
-    @static_provision_action
-    def _provide_unwrapping(self, mediator: Mediator, request: LocatedRequest) -> Loader:
-        loc = request.last_loc.cast_or_raise(TypeHintLoc, CannotProvide)
-        norm = try_normalize_type(loc.type)
+class TypeHintTagsUnwrappingProvider(LocatedRequestDelegatingProvider):
+    REQUEST_CLASSES = (LoaderRequest, DumperRequest)
+
+    def get_delegated_type(self, mediator: Mediator[LocatedRequestT], request: LocatedRequestT) -> TypeHint:
+        tp = request.last_loc.type
+        norm = try_normalize_type(tp)
         unwrapped = strip_tags(norm)
-        if unwrapped.source == loc.type:  # type has not changed, continue search
+        if unwrapped.source == tp:  # type has not changed, continue search
             raise CannotProvide
 
-        return mediator.delegating_provide(
-            replace(
-                request,
-                loc_stack=request.loc_stack.replace_last_type(unwrapped.source),
-            ),
-        )
+        return unwrapped.source
+
 
+class TypeAliasUnwrappingProvider(LocatedRequestDelegatingProvider):
+    REQUEST_CLASSES = (LoaderRequest, DumperRequest)
 
-class TypeAliasUnwrappingProvider(StaticProvider):
-    @static_provision_action
-    def _provide_unwrapping(self, mediator: Mediator, request: LocatedRequest) -> Loader:
-        loc = request.last_loc.cast_or_raise(TypeHintLoc, CannotProvide)
-        norm = try_normalize_type(loc.type)
+    def get_delegated_type(self, mediator: Mediator[LocatedRequestT], request: LocatedRequestT) -> TypeHint:
+        norm = try_normalize_type(request.last_loc.type)
         if not isinstance(norm, NormTypeAlias):
             raise CannotProvide
 
-        unwrapped = norm.value[tuple(arg.source for arg in norm.args)] if norm.args else norm.value
-        return mediator.delegating_provide(
-            replace(
-                request,
-                loc_stack=request.loc_stack.replace_last_type(unwrapped),
-            ),
-        )
+        return norm.value[tuple(arg.source for arg in norm.args)] if norm.args else norm.value
 
 
 def _is_exact_zero_or_one(arg):
@@ -99,7 +77,7 @@ def _get_allowed_values_repr(self, args: Collection, mediator: Mediator, loc_sta
         if not enum_cases:
             return set(args)
 
-        literal_dumper = self._provide_dumper(mediator, DumperRequest(loc_stack))
+        literal_dumper = self.provide_dumper(mediator, DumperRequest(loc_stack))
         return {literal_dumper(arg) if isinstance(arg, Enum) else arg for arg in args}
 
     def _get_enum_types(self, cases: Collection) -> Collection:
@@ -177,8 +155,8 @@ def wrapped_loader_with_enums(data):
 
         return wrapped_loader_with_enums
 
-    def _provide_loader(self, mediator: Mediator, request: LoaderRequest) -> Loader:
-        norm = try_normalize_type(get_type_from_request(request))
+    def provide_loader(self, mediator: Mediator, request: LoaderRequest) -> Loader:
+        norm = try_normalize_type(request.last_loc.type)
         strict_coercion = mediator.mandatory_provide(StrictCoercionRequest(loc_stack=request.loc_stack))
 
         enum_cases = [arg for arg in norm.args if isinstance(arg, Enum)]
@@ -212,8 +190,8 @@ def literal_loader(data):
 
         return self._get_literal_loader_with_enum(literal_loader, enum_loaders, allowed_values)
 
-    def _provide_dumper(self, mediator: Mediator, request: DumperRequest) -> Dumper:
-        norm = try_normalize_type(get_type_from_request(request))
+    def provide_dumper(self, mediator: Mediator, request: DumperRequest) -> Dumper:
+        norm = try_normalize_type(request.last_loc.type)
         enum_cases = [arg for arg in norm.args if isinstance(arg, Enum)]
 
         if not enum_cases:
@@ -241,8 +219,8 @@ def literal_dumper_with_enums(data):
 
 @for_predicate(Union)
 class UnionProvider(LoaderProvider, DumperProvider):
-    def _provide_loader(self, mediator: Mediator, request: LoaderRequest) -> Loader:
-        norm = try_normalize_type(get_type_from_request(request))
+    def provide_loader(self, mediator: Mediator, request: LoaderRequest) -> Loader:
+        norm = try_normalize_type(request.last_loc.type)
         debug_trail = mediator.mandatory_provide(DebugTrailRequest(loc_stack=request.loc_stack))
 
         if self._is_single_optional(norm):
@@ -357,8 +335,8 @@ def _is_single_optional(self, norm: BaseNormType) -> bool:
     def _is_class_origin(self, origin) -> bool:
         return (origin is None or isinstance(origin, type)) and not is_subclass_soft(origin, collections.abc.Callable)
 
-    def _provide_dumper(self, mediator: Mediator, request: DumperRequest) -> Dumper:
-        request_type = get_type_from_request(request)
+    def provide_dumper(self, mediator: Mediator, request: DumperRequest) -> Dumper:
+        request_type = request.last_loc.type
         norm = try_normalize_type(request_type)
 
         if self._is_single_optional(norm):
@@ -472,7 +450,7 @@ def path_like_dumper(data):
 class PathLikeProvider(LoaderProvider, DumperProvider):
     _impl = Path
 
-    def _provide_loader(self, mediator: Mediator, request: LoaderRequest) -> Loader:
+    def provide_loader(self, mediator: Mediator, request: LoaderRequest) -> Loader:
         return mediator.mandatory_provide(
             LoaderRequest(
                 loc_stack=request.loc_stack.replace_last_type(self._impl),
@@ -480,5 +458,5 @@ def _provide_loader(self, mediator: Mediator, request: LoaderRequest) -> Loader:
             lambda x: f"Cannot create loader for {PathLike}. Loader for {Path} cannot be created",
         )
 
-    def _provide_dumper(self, mediator: Mediator, request: DumperRequest) -> Dumper:
+    def provide_dumper(self, mediator: Mediator, request: DumperRequest) -> Dumper:
         return path_like_dumper
diff --git a/src/adaptix/_internal/morphing/iterable_provider.py b/src/adaptix/_internal/morphing/iterable_provider.py
index abdca17b..e92f6f93 100644
--- a/src/adaptix/_internal/morphing/iterable_provider.py
+++ b/src/adaptix/_internal/morphing/iterable_provider.py
@@ -8,18 +8,12 @@
 from ..definitions import DebugTrail
 from ..morphing.provider_template import DumperProvider, LoaderProvider
 from ..provider.essential import CannotProvide, Mediator
+from ..provider.located_request import LocatedRequest, for_predicate
 from ..provider.location import GenericParamLoc
-from ..provider.provider_template import for_predicate
-from ..provider.request_cls import (
-    DebugTrailRequest,
-    LocatedRequest,
-    StrictCoercionRequest,
-    get_type_from_request,
-    try_normalize_type,
-)
 from ..struct_trail import append_trail, render_trail_as_note
 from .load_error import AggregateLoadError, ExcludedTypeLoadError, LoadError, TypeLoadError
-from .request_cls import DumperRequest, LoaderRequest
+from .request_cls import DebugTrailRequest, DumperRequest, LoaderRequest, StrictCoercionRequest
+from .utils import try_normalize_type
 
 CollectionsMapping = collections.abc.Mapping
 
@@ -51,7 +45,7 @@ def _get_iter_factory(self, origin) -> Callable[[Iterable], Iterable]:
         raise CannotProvide
 
     def _fetch_norm_and_arg(self, request: LocatedRequest):
-        norm = try_normalize_type(get_type_from_request(request))
+        norm = try_normalize_type(request.last_loc.type)
 
         if len(norm.args) != 1 and not (norm.origin == tuple and norm.args[-1] == Ellipsis):
             raise CannotProvide
@@ -66,7 +60,7 @@ def _fetch_norm_and_arg(self, request: LocatedRequest):
 
         return norm, arg
 
-    def _provide_loader(self, mediator: Mediator, request: LoaderRequest) -> Loader:
+    def provide_loader(self, mediator: Mediator, request: LoaderRequest) -> Loader:
         norm, arg = self._fetch_norm_and_arg(request)
 
         iter_factory = self._get_iter_factory(norm.origin)
@@ -207,7 +201,7 @@ def iter_loader(data):
 
         return iter_loader
 
-    def _provide_dumper(self, mediator: Mediator, request: DumperRequest) -> Dumper:
+    def provide_dumper(self, mediator: Mediator, request: DumperRequest) -> Dumper:
         norm, arg = self._fetch_norm_and_arg(request)
 
         iter_factory = self._get_iter_factory(norm.origin)
diff --git a/src/adaptix/_internal/morphing/model/basic_gen.py b/src/adaptix/_internal/morphing/model/basic_gen.py
index a5dd30af..d7bec780 100644
--- a/src/adaptix/_internal/morphing/model/basic_gen.py
+++ b/src/adaptix/_internal/morphing/model/basic_gen.py
@@ -22,8 +22,9 @@
 from ...code_tools.utils import get_literal_expr
 from ...model_tools.definitions import InputField, OutputField
 from ...provider.essential import CannotProvide, Mediator
-from ...provider.request_cls import LocatedRequest, LocStack
-from ...provider.static_provider import StaticProvider, static_provision_action
+from ...provider.loc_stack_filtering import LocStack
+from ...provider.located_request import LocatedRequest
+from ...provider.methods_provider import MethodsProvider, method_handler
 from .crown_definitions import (
     BaseCrown,
     BaseDictCrown,
@@ -69,13 +70,13 @@ def fetch_code_gen_hook(mediator: Mediator, loc_stack: LocStack) -> CodeGenHook:
         return stub_code_gen_hook
 
 
-class CodeGenAccumulator(StaticProvider):
+class CodeGenAccumulator(MethodsProvider):
     """Accumulates all generated code. It may be useful for debugging"""
 
     def __init__(self) -> None:
         self.list: List[Tuple[CodeGenHookRequest, CodeGenHookData]] = []
 
-    @static_provision_action
+    @method_handler
     def _provide_code_gen_hook(self, mediator: Mediator, request: CodeGenHookRequest) -> CodeGenHook:
         def hook(data: CodeGenHookData):
             self.list.append((request, data))
diff --git a/src/adaptix/_internal/morphing/model/crown_definitions.py b/src/adaptix/_internal/morphing/model/crown_definitions.py
index b4f2d299..b47c50d2 100644
--- a/src/adaptix/_internal/morphing/model/crown_definitions.py
+++ b/src/adaptix/_internal/morphing/model/crown_definitions.py
@@ -3,7 +3,7 @@
 
 from ...common import VarTuple
 from ...model_tools.definitions import BaseShape, DefaultFactory, DefaultValue, InputShape, OutputShape
-from ...provider.request_cls import LocatedRequest
+from ...provider.located_request import LocatedRequest
 from ...utils import SingletonMeta
 
 T = TypeVar("T")
diff --git a/src/adaptix/_internal/morphing/model/dumper_provider.py b/src/adaptix/_internal/morphing/model/dumper_provider.py
index 7a105375..71a54419 100644
--- a/src/adaptix/_internal/morphing/model/dumper_provider.py
+++ b/src/adaptix/_internal/morphing/model/dumper_provider.py
@@ -8,10 +8,9 @@
 from ...definitions import DebugTrail
 from ...model_tools.definitions import OutputShape
 from ...provider.essential import Mediator
-from ...provider.request_cls import DebugTrailRequest
 from ...provider.shape_provider import OutputShapeRequest, provide_generic_resolved_shape
 from ..provider_template import DumperProvider
-from ..request_cls import DumperRequest
+from ..request_cls import DebugTrailRequest, DumperRequest
 from .basic_gen import (
     ModelDumperGen,
     compile_closure_with_globals_capturing,
@@ -28,7 +27,7 @@ class ModelDumperProvider(DumperProvider):
     def __init__(self, *, name_sanitizer: NameSanitizer = BuiltinNameSanitizer()):
         self._name_sanitizer = name_sanitizer
 
-    def _provide_dumper(self, mediator: Mediator, request: DumperRequest) -> Dumper:
+    def provide_dumper(self, mediator: Mediator, request: DumperRequest) -> Dumper:
         dumper_gen = self._fetch_model_dumper_gen(mediator, request)
         closure_name = self._get_closure_name(request)
         dumper_code, dumper_namespace = dumper_gen.produce_code(closure_name=closure_name)
diff --git a/src/adaptix/_internal/morphing/model/loader_provider.py b/src/adaptix/_internal/morphing/model/loader_provider.py
index fb20240b..b3361f7b 100644
--- a/src/adaptix/_internal/morphing/model/loader_provider.py
+++ b/src/adaptix/_internal/morphing/model/loader_provider.py
@@ -8,11 +8,10 @@
 from ...definitions import DebugTrail
 from ...model_tools.definitions import InputShape
 from ...provider.essential import Mediator
-from ...provider.request_cls import DebugTrailRequest, StrictCoercionRequest
 from ...provider.shape_provider import InputShapeRequest, provide_generic_resolved_shape
 from ..model.loader_gen import BuiltinModelLoaderGen, ModelLoaderProps
 from ..provider_template import LoaderProvider
-from ..request_cls import LoaderRequest
+from ..request_cls import DebugTrailRequest, LoaderRequest, StrictCoercionRequest
 from .basic_gen import (
     ModelLoaderGen,
     compile_closure_with_globals_capturing,
@@ -36,7 +35,7 @@ def __init__(
         self._name_sanitizer = name_sanitizer
         self._props = props
 
-    def _provide_loader(self, mediator: Mediator, request: LoaderRequest) -> Loader:
+    def provide_loader(self, mediator: Mediator, request: LoaderRequest) -> Loader:
         loader_gen = self._fetch_model_loader_gen(mediator, request)
         closure_name = self._get_closure_name(request)
         loader_code, loader_namespace = loader_gen.produce_code(closure_name=closure_name)
diff --git a/src/adaptix/_internal/morphing/model/request_filtering.py b/src/adaptix/_internal/morphing/model/request_filtering.py
index f6a897ed..af0f4d35 100644
--- a/src/adaptix/_internal/morphing/model/request_filtering.py
+++ b/src/adaptix/_internal/morphing/model/request_filtering.py
@@ -1,6 +1,5 @@
 from ...provider.essential import CannotProvide
-from ...provider.loc_stack_filtering import DirectMediator, LocStackChecker
-from ...provider.request_cls import LocStack
+from ...provider.loc_stack_filtering import DirectMediator, LocStack, LocStackChecker
 from ...provider.shape_provider import InputShapeRequest, OutputShapeRequest
 
 
diff --git a/src/adaptix/_internal/morphing/name_layout/component.py b/src/adaptix/_internal/morphing/name_layout/component.py
index 0a89b285..05960f0e 100644
--- a/src/adaptix/_internal/morphing/name_layout/component.py
+++ b/src/adaptix/_internal/morphing/name_layout/component.py
@@ -17,9 +17,9 @@
 from ...provider.essential import CannotProvide, Mediator, Provider
 from ...provider.fields import field_to_loc
 from ...provider.loc_stack_filtering import LocStackChecker
+from ...provider.located_request import LocatedRequest
 from ...provider.overlay_schema import Overlay, Schema, provide_schema
-from ...provider.request_cls import LocatedRequest
-from ...retort.operating_retort import OperatingRetort
+from ...retort.operating_retort import OperatingRetort, ProviderNotFoundError
 from ...special_cases_optimization import with_default_clause
 from ...utils import Omittable, get_prefix_groups
 from ..model.crown_definitions import (
@@ -102,6 +102,11 @@ def apply_lsc(
     return loc_stack_checker.check_loc_stack(mediator, loc_stack)
 
 
+class NameMappingRetort(OperatingRetort):
+    def provide_name_mapping(self, request: NameMappingRequest) -> Optional[KeyPath]:
+        return self._facade_provide(request, error_message="")
+
+
 class BuiltinStructureMaker(StructureMaker):
     def _generate_key(self, schema: StructureSchema, shape: BaseShape, field: BaseField) -> Key:
         if schema.as_list:
@@ -114,8 +119,8 @@ def _generate_key(self, schema: StructureSchema, shape: BaseShape, field: BaseFi
             name = convert_snake_style(name, schema.name_style)
         return name
 
-    def _create_map_provider(self, schema: StructureSchema) -> Provider:
-        return OperatingRetort(recipe=schema.map)
+    def _create_name_mapping_retort(self, schema: StructureSchema) -> NameMappingRetort:
+        return NameMappingRetort(recipe=schema.map)
 
     def _map_fields(
         self,
@@ -125,15 +130,14 @@ def _map_fields(
         extra_move: Union[InpExtraMove, OutExtraMove],
     ) -> Iterable[FieldAndPath]:
         extra_targets = extra_move.fields if isinstance(extra_move, ExtraTargets) else ()
-        map_provider = self._create_map_provider(schema)
+        retort = self._create_name_mapping_retort(schema)
         for field in request.shape.fields:
             if field.id in extra_targets:
                 continue
 
             generated_key = self._generate_key(schema, request.shape, field)
             try:
-                path = map_provider.apply_provider(
-                    mediator,
+                path = retort.provide_name_mapping(
                     NameMappingRequest(
                         shape=request.shape,
                         field=field,
@@ -141,7 +145,7 @@ def _map_fields(
                         loc_stack=request.loc_stack.append_with(field_to_loc(field)),
                     ),
                 )
-            except CannotProvide:
+            except ProviderNotFoundError:
                 path = (generated_key, )
 
             if path is None:
diff --git a/src/adaptix/_internal/morphing/name_layout/name_mapping.py b/src/adaptix/_internal/morphing/name_layout/name_mapping.py
index 0cf6d12f..6b273ee0 100644
--- a/src/adaptix/_internal/morphing/name_layout/name_mapping.py
+++ b/src/adaptix/_internal/morphing/name_layout/name_mapping.py
@@ -1,15 +1,15 @@
 from __future__ import annotations
 
+from abc import ABC, abstractmethod
 from dataclasses import dataclass
 from typing import Callable, Iterable, Mapping, Optional, Tuple, Union
 
 from ...common import EllipsisType
 from ...model_tools.definitions import BaseField, BaseShape, OutputField, is_valid_field_id
 from ...provider.essential import CannotProvide, Mediator, Provider
-from ...provider.loc_stack_filtering import LocStackChecker, Pred
-from ...provider.provider_wrapper import ProviderWithLSC
-from ...provider.request_cls import LocatedRequest
-from ...provider.static_provider import StaticProvider, static_provision_action
+from ...provider.loc_stack_filtering import Pred
+from ...provider.located_request import LocatedRequest
+from ...provider.methods_provider import MethodsProvider, method_handler
 from .base import Key, KeyPath
 
 RawKey = Union[Key, EllipsisType]
@@ -45,7 +45,14 @@ def resolve_map_result(generated_key: Key, map_result: MapResult) -> Optional[Ke
     return tuple(generated_key if isinstance(key, EllipsisType) else key for key in map_result)
 
 
-class DictNameMappingProvider(StaticProvider):
+class NameMappingProvider(MethodsProvider, ABC):
+    @abstractmethod
+    @method_handler
+    def provide_name_mapping(self, mediator: Mediator, request: NameMappingRequest) -> Optional[KeyPath]:
+        ...
+
+
+class DictNameMappingProvider(NameMappingProvider):
     def __init__(self, name_map: Mapping[str, MapResult]):
         self._name_map = name_map
         self._validate()
@@ -58,8 +65,7 @@ def _validate(self) -> None:
                 f" Keys {invalid_keys!r} does not meet this condition.",
             )
 
-    @static_provision_action
-    def _provide_name_mapping(self, mediator: Mediator, request: NameMappingRequest) -> Optional[KeyPath]:
+    def provide_name_mapping(self, mediator: Mediator, request: NameMappingRequest) -> Optional[KeyPath]:
         try:
             map_result = self._name_map[request.field.id]
         except KeyError:
@@ -67,38 +73,25 @@ def _provide_name_mapping(self, mediator: Mediator, request: NameMappingRequest)
         return resolve_map_result(request.generated_key, map_result)
 
 
-class ConstNameMappingProvider(StaticProvider, ProviderWithLSC):
-    def __init__(self, loc_stack_checker: LocStackChecker, result: MapResult):
-        self._loc_stack_checker = loc_stack_checker
+class ConstNameMappingProvider(NameMappingProvider):
+    def __init__(self, result: MapResult):
         self._result = result
 
-    def get_loc_stack_checker(self) -> Optional[LocStackChecker]:
-        return self._loc_stack_checker
-
-    @static_provision_action
-    def _provide_name_mapping(self, mediator: Mediator, request: NameMappingRequest) -> Optional[KeyPath]:
-        self._apply_loc_stack_checker(mediator, request)
+    def provide_name_mapping(self, mediator: Mediator, request: NameMappingRequest) -> Optional[KeyPath]:
         return resolve_map_result(request.generated_key, self._result)
 
 
-class FuncNameMappingProvider(StaticProvider, ProviderWithLSC):
-    def __init__(self, loc_stack_checker: LocStackChecker, func: Callable[[BaseShape, BaseField], MapResult]):
-        self._loc_stack_checker = loc_stack_checker
+class FuncNameMappingProvider(NameMappingProvider):
+    def __init__(self, func: Callable[[BaseShape, BaseField], MapResult]):
         self._func = func
 
-    def get_loc_stack_checker(self) -> Optional[LocStackChecker]:
-        return self._loc_stack_checker
-
-    @static_provision_action
-    def _provide_name_mapping(self, mediator: Mediator, request: NameMappingRequest) -> Optional[KeyPath]:
-        self._apply_loc_stack_checker(mediator, request)
+    def provide_name_mapping(self, mediator: Mediator, request: NameMappingRequest) -> Optional[KeyPath]:
         result = self._func(request.shape, request.field)
         return resolve_map_result(request.generated_key, result)
 
 
-class SkipPrivateFieldsNameMappingProvider(StaticProvider):
-    @static_provision_action
-    def _provide_name_mapping(self, mediator: Mediator, request: NameMappingRequest) -> Optional[KeyPath]:
+class SkipPrivateFieldsNameMappingProvider(NameMappingProvider):
+    def provide_name_mapping(self, mediator: Mediator, request: NameMappingRequest) -> Optional[KeyPath]:
         if not isinstance(request.field, OutputField):
             raise CannotProvide
         if request.field.id.startswith("_"):
diff --git a/src/adaptix/_internal/morphing/name_layout/provider.py b/src/adaptix/_internal/morphing/name_layout/provider.py
index 8ae15fd7..ef753a24 100644
--- a/src/adaptix/_internal/morphing/name_layout/provider.py
+++ b/src/adaptix/_internal/morphing/name_layout/provider.py
@@ -2,7 +2,7 @@
 
 from ...model_tools.definitions import InputShape, OutputShape
 from ...provider.essential import Mediator
-from ...provider.static_provider import StaticProvider, static_provision_action
+from ...provider.methods_provider import MethodsProvider, method_handler
 from ..model.crown_definitions import (
     BranchInpCrown,
     BranchOutCrown,
@@ -21,7 +21,7 @@
 T = TypeVar("T")
 
 
-class BuiltinNameLayoutProvider(StaticProvider):
+class BuiltinNameLayoutProvider(MethodsProvider):
     def __init__(
         self,
         structure_maker: StructureMaker,
@@ -34,7 +34,7 @@ def __init__(
         self._extra_policies_maker = extra_policies_maker
         self._extra_move_maker = extra_move_maker
 
-    @static_provision_action
+    @method_handler
     def _provide_input_name_layout(self, mediator: Mediator, request: InputNameLayoutRequest) -> InputNameLayout:
         extra_move = self._extra_move_maker.make_inp_extra_move(mediator, request)
         paths_to_leaves = self._structure_maker.make_inp_structure(mediator, request, extra_move)
@@ -74,7 +74,7 @@ def _create_empty_input_crown(
     ) -> BranchInpCrown:
         return InpCrownBuilder(extra_policies, {}).build_empty_crown(as_list=as_list)
 
-    @static_provision_action
+    @method_handler
     def _provide_output_name_layout(self, mediator: Mediator, request: OutputNameLayoutRequest) -> OutputNameLayout:
         extra_move = self._extra_move_maker.make_out_extra_move(mediator, request)
         paths_to_leaves = self._structure_maker.make_out_structure(mediator, request, extra_move)
diff --git a/src/adaptix/_internal/morphing/provider_template.py b/src/adaptix/_internal/morphing/provider_template.py
index 83d50a77..2fdbedb1 100644
--- a/src/adaptix/_internal/morphing/provider_template.py
+++ b/src/adaptix/_internal/morphing/provider_template.py
@@ -1,36 +1,25 @@
 from abc import ABC, abstractmethod
-from typing import final
 
 from ..common import Dumper, Loader, TypeHint
 from ..provider.essential import CannotProvide, Mediator
 from ..provider.loc_stack_filtering import ExactOriginLSC
-from ..provider.provider_template import ProviderWithAttachableLSC
-from ..provider.static_provider import static_provision_action
+from ..provider.located_request import LocatedRequestMethodsProvider
+from ..provider.methods_provider import method_handler
 from ..type_tools import normalize_type
 from .request_cls import DumperRequest, LoaderRequest
 
 
-class LoaderProvider(ProviderWithAttachableLSC, ABC):
-    @final
-    @static_provision_action
-    def _outer_provide_loader(self, mediator: Mediator, request: LoaderRequest):
-        self._apply_loc_stack_checker(mediator, request)
-        return self._provide_loader(mediator, request)
-
+class LoaderProvider(LocatedRequestMethodsProvider, ABC):
+    @method_handler
     @abstractmethod
-    def _provide_loader(self, mediator: Mediator, request: LoaderRequest) -> Loader:
+    def provide_loader(self, mediator: Mediator[Loader], request: LoaderRequest) -> Loader:
         ...
 
 
-class DumperProvider(ProviderWithAttachableLSC, ABC):
-    @final
-    @static_provision_action
-    def _outer_provide_dumper(self, mediator: Mediator, request: DumperRequest):
-        self._apply_loc_stack_checker(mediator, request)
-        return self._provide_dumper(mediator, request)
-
+class DumperProvider(LocatedRequestMethodsProvider, ABC):
+    @method_handler
     @abstractmethod
-    def _provide_dumper(self, mediator: Mediator, request: DumperRequest) -> Dumper:
+    def provide_dumper(self, mediator: Mediator[Dumper], request: DumperRequest) -> Dumper:
         ...
 
 
@@ -42,7 +31,7 @@ def __init__(self, abstract: TypeHint, impl: TypeHint, *, for_loader: bool = Tru
         self._for_loader = for_loader
         self._for_dumper = for_dumper
 
-    def _provide_loader(self, mediator: Mediator, request: LoaderRequest) -> Loader:
+    def provide_loader(self, mediator: Mediator, request: LoaderRequest) -> Loader:
         if not self._for_loader:
             raise CannotProvide
 
@@ -53,7 +42,7 @@ def _provide_loader(self, mediator: Mediator, request: LoaderRequest) -> Loader:
             lambda x: f"Cannot create loader for union. Loader for {self._impl} cannot be created",
         )
 
-    def _provide_dumper(self, mediator: Mediator, request: DumperRequest) -> Dumper:
+    def provide_dumper(self, mediator: Mediator, request: DumperRequest) -> Dumper:
         if not self._for_dumper:
             raise CannotProvide
 
diff --git a/src/adaptix/_internal/morphing/request_cls.py b/src/adaptix/_internal/morphing/request_cls.py
index 163d80bc..45427d41 100644
--- a/src/adaptix/_internal/morphing/request_cls.py
+++ b/src/adaptix/_internal/morphing/request_cls.py
@@ -1,7 +1,8 @@
 from dataclasses import dataclass
 
 from ..common import Dumper, Loader
-from ..provider.request_cls import LocatedRequest
+from ..definitions import DebugTrail
+from ..provider.located_request import LocatedRequest
 
 
 @dataclass(frozen=True)
@@ -12,3 +13,11 @@ class LoaderRequest(LocatedRequest[Loader]):
 @dataclass(frozen=True)
 class DumperRequest(LocatedRequest[Dumper]):
     pass
+
+
+class StrictCoercionRequest(LocatedRequest[bool]):
+    pass
+
+
+class DebugTrailRequest(LocatedRequest[DebugTrail]):
+    pass
diff --git a/src/adaptix/_internal/morphing/utils.py b/src/adaptix/_internal/morphing/utils.py
new file mode 100644
index 00000000..9f0e6ef5
--- /dev/null
+++ b/src/adaptix/_internal/morphing/utils.py
@@ -0,0 +1,10 @@
+from ..common import TypeHint
+from ..provider.essential import CannotProvide
+from ..type_tools import BaseNormType, normalize_type
+
+
+def try_normalize_type(tp: TypeHint) -> BaseNormType:
+    try:
+        return normalize_type(tp)
+    except ValueError:
+        raise CannotProvide(f"{tp} can not be normalized")
diff --git a/src/adaptix/_internal/provider/essential.py b/src/adaptix/_internal/provider/essential.py
index 955ca42f..3037c88c 100644
--- a/src/adaptix/_internal/provider/essential.py
+++ b/src/adaptix/_internal/provider/essential.py
@@ -1,6 +1,6 @@
 from abc import ABC, abstractmethod
 from dataclasses import dataclass
-from typing import Any, Callable, Generic, Iterable, Optional, Sequence, TypeVar, final
+from typing import Any, Callable, Generic, Iterable, Optional, Sequence, Tuple, Type, TypeVar, final
 
 from ..common import VarTuple
 from ..compat import CompatExceptionGroup
@@ -106,10 +106,7 @@ def make(
         )
 
 
-V = TypeVar("V")
-
-
-class Mediator(ABC, Generic[V]):
+class DirectMediator(ABC):
     """Mediator is an object that gives provider access to other providers
     and that stores the state of the current search.
 
@@ -125,12 +122,6 @@ def provide(self, request: Request[T]) -> T:
         :raise CannotProvide: A provider able to process the request does not be found
         """
 
-    @abstractmethod
-    def provide_from_next(self) -> V:
-        """Forward current request to providers
-        that placed after current provider at the recipe.
-        """
-
     @final
     def delegating_provide(
         self,
@@ -212,13 +203,36 @@ def mandatory_apply_by_iterable(
     return results
 
 
+ResponseT = TypeVar("ResponseT")
+
+
+class Mediator(DirectMediator, ABC, Generic[ResponseT]):
+    """Mediator is an object that gives provider access to other providers
+    and that stores the state of the current search.
+
+    Mediator is a proxy to providers of retort.
+    """
+
+    @abstractmethod
+    def provide_from_next(self) -> ResponseT:
+        """Forward current request to providers
+        that placed after current provider at the recipe.
+        """
+
+
+RequestT = TypeVar("RequestT", bound=Request)
+RequestHandler = Callable[[Mediator[ResponseT], RequestT], ResponseT]
+
+
+class RequestChecker(ABC, Generic[RequestT]):
+    @abstractmethod
+    def check_request(self, mediator: DirectMediator, request: RequestT, /) -> bool:
+        ...
+
+
 class Provider(ABC):
     """An object that can process Request instances"""
 
     @abstractmethod
-    def apply_provider(self, mediator: Mediator[T], request: Request[T]) -> T:
-        """Handle request instance and return a value of type required by request.
-        Behavior must be the same during the provider object lifetime
-
-        :raise CannotProvide: provider cannot process passed request
-        """
+    def get_request_handlers(self) -> Sequence[Tuple[Type[Request], RequestChecker, RequestHandler]]:
+        ...
diff --git a/src/adaptix/_internal/provider/facade/provider.py b/src/adaptix/_internal/provider/facade/provider.py
index a5bb4362..647246d5 100644
--- a/src/adaptix/_internal/provider/facade/provider.py
+++ b/src/adaptix/_internal/provider/facade/provider.py
@@ -3,15 +3,15 @@
 from ...utils import Omitted
 from ..essential import Provider
 from ..loc_stack_filtering import OrLocStackChecker, Pred, create_loc_stack_checker
-from ..provider_wrapper import BoundingProvider
+from ..located_request import LocStackBoundingProvider
 
 
 def bound_by_any(preds: Sequence[Pred], provider: Provider) -> Provider:
     if len(preds) == 0:
         return provider
     if len(preds) == 1:
-        return BoundingProvider(create_loc_stack_checker(preds[0]), provider)
-    return BoundingProvider(
+        return LocStackBoundingProvider(create_loc_stack_checker(preds[0]), provider)
+    return LocStackBoundingProvider(
         OrLocStackChecker([create_loc_stack_checker(pred) for pred in preds]),
         provider,
     )
@@ -20,4 +20,4 @@ def bound_by_any(preds: Sequence[Pred], provider: Provider) -> Provider:
 def bound(pred: Pred, provider: Provider) -> Provider:
     if pred == Omitted():
         return provider
-    return BoundingProvider(create_loc_stack_checker(pred), provider)
+    return LocStackBoundingProvider(create_loc_stack_checker(pred), provider)
diff --git a/src/adaptix/_internal/provider/loc_stack_filtering.py b/src/adaptix/_internal/provider/loc_stack_filtering.py
index f480d0c7..a0c4e048 100644
--- a/src/adaptix/_internal/provider/loc_stack_filtering.py
+++ b/src/adaptix/_internal/provider/loc_stack_filtering.py
@@ -3,12 +3,13 @@
 import re
 from abc import ABC, abstractmethod
 from copy import copy
-from dataclasses import dataclass
+from dataclasses import dataclass, replace
 from functools import reduce
 from inspect import isabstract, isgenerator
-from typing import Any, Callable, ClassVar, Iterable, Optional, Pattern, Protocol, Sequence, Type, TypeVar, Union, final
+from typing import Any, ClassVar, Iterable, Optional, Pattern, Sequence, Type, TypeVar, Union, final
 
 from ..common import TypeHint, VarTuple
+from ..datastructures import ImmutableStack
 from ..type_tools import (
     BaseNormType,
     NormTV,
@@ -20,39 +21,16 @@
     normalize_type,
 )
 from ..type_tools.normalize_type import NotSubscribedError
-from .essential import CannotProvide, Request
-from .location import FieldLoc, GenericParamLoc, TypeHintLoc
-from .request_cls import LocStack
+from .essential import DirectMediator
+from .location import AnyLoc, FieldLoc, GenericParamLoc, TypeHintLoc
 
-T = TypeVar("T")
+LocStackT = TypeVar("LocStackT", bound="LocStack")
+AnyLocT_co = TypeVar("AnyLocT_co", bound=AnyLoc, covariant=True)
 
 
-class DirectMediator(Protocol):
-    """This is a copy of Mediator protocol but without provide_from_next() method"""
-
-    def provide(self, request: Request[T]) -> T:
-        ...
-
-    def delegating_provide(
-        self,
-        request: Request[T],
-        error_describer: Optional[Callable[[CannotProvide], str]] = None,
-    ) -> T:
-        ...
-
-    def mandatory_provide(
-        self,
-        request: Request[T],
-        error_describer: Optional[Callable[[CannotProvide], str]] = None,
-    ) -> T:
-        ...
-
-    def mandatory_provide_by_iterable(
-        self,
-        requests: Iterable[Request[T]],
-        error_describer: Optional[Callable[[], str]] = None,
-    ) -> Iterable[T]:
-        ...
+class LocStack(ImmutableStack[AnyLocT_co]):
+    def replace_last_type(self: LocStackT, tp: TypeHint, /) -> LocStackT:
+        return self.replace_last(replace(self.last, type=tp))
 
 
 class LocStackChecker(ABC):
@@ -119,7 +97,7 @@ def _reduce(self, elements: Iterable[bool], /) -> bool:
         return reduce(operator.xor, elements)
 
 
-class LastLocMapChecker(LocStackChecker, ABC):
+class LastLocChecker(LocStackChecker, ABC):
     _expected_location: ClassVar[type]
 
     def __init_subclass__(cls, **kwargs):
@@ -139,7 +117,7 @@ def _check_location(self, mediator: DirectMediator, loc: Any) -> bool:
 
 
 @dataclass(frozen=True)
-class ExactFieldNameLSC(LastLocMapChecker):
+class ExactFieldNameLSC(LastLocChecker):
     field_id: str
 
     def _check_location(self, mediator: DirectMediator, loc: FieldLoc) -> bool:
@@ -147,7 +125,7 @@ def _check_location(self, mediator: DirectMediator, loc: FieldLoc) -> bool:
 
 
 @dataclass(frozen=True)
-class ReFieldNameLSC(LastLocMapChecker):
+class ReFieldNameLSC(LastLocChecker):
     pattern: Pattern[str]
 
     def _check_location(self, mediator: DirectMediator, loc: FieldLoc) -> bool:
@@ -155,7 +133,7 @@ def _check_location(self, mediator: DirectMediator, loc: FieldLoc) -> bool:
 
 
 @dataclass(frozen=True)
-class ExactTypeLSC(LastLocMapChecker):
+class ExactTypeLSC(LastLocChecker):
     norm: BaseNormType
 
     def _check_location(self, mediator: DirectMediator, loc: TypeHintLoc) -> bool:
@@ -167,7 +145,7 @@ def _check_location(self, mediator: DirectMediator, loc: TypeHintLoc) -> bool:
 
 
 @dataclass(frozen=True)
-class OriginSubclassLSC(LastLocMapChecker):
+class OriginSubclassLSC(LastLocChecker):
     type_: type
 
     def _check_location(self, mediator: DirectMediator, loc: TypeHintLoc) -> bool:
@@ -179,7 +157,7 @@ def _check_location(self, mediator: DirectMediator, loc: TypeHintLoc) -> bool:
 
 
 @dataclass(frozen=True)
-class ExactOriginLSC(LastLocMapChecker):
+class ExactOriginLSC(LastLocChecker):
     origin: Any
 
     def _check_location(self, mediator: DirectMediator, loc: TypeHintLoc) -> bool:
@@ -191,7 +169,7 @@ def _check_location(self, mediator: DirectMediator, loc: TypeHintLoc) -> bool:
 
 
 @dataclass(frozen=True)
-class GenericParamLSC(LastLocMapChecker):
+class GenericParamLSC(LastLocChecker):
     pos: int
 
     def _check_location(self, mediator: DirectMediator, loc: GenericParamLoc) -> bool:
diff --git a/src/adaptix/_internal/provider/request_cls.py b/src/adaptix/_internal/provider/loc_stack_tools.py
similarity index 51%
rename from src/adaptix/_internal/provider/request_cls.py
rename to src/adaptix/_internal/provider/loc_stack_tools.py
index 1dbc477d..41d9454c 100644
--- a/src/adaptix/_internal/provider/request_cls.py
+++ b/src/adaptix/_internal/provider/loc_stack_tools.py
@@ -1,22 +1,11 @@
-from dataclasses import dataclass, replace
-from typing import Tuple, TypeVar
+from typing import Tuple
 
 from ..common import TypeHint
-from ..datastructures import ImmutableStack
-from ..definitions import DebugTrail
-from ..type_tools import BaseNormType, is_parametrized, normalize_type
+from ..type_tools import is_parametrized
 from ..utils import pairs
-from .essential import CannotProvide, Request
+from .loc_stack_filtering import LocStack
 from .location import AnyLoc, FieldLoc, InputFuncFieldLoc, TypeHintLoc
 
-LocStackT = TypeVar("LocStackT", bound="LocStack")
-AnyLocT_co = TypeVar("AnyLocT_co", bound=AnyLoc, covariant=True)
-
-
-class LocStack(ImmutableStack[AnyLocT_co]):
-    def replace_last_type(self: LocStackT, tp: TypeHint, /) -> LocStackT:
-        return self.replace_last(replace(self.last, type=tp))
-
 
 def _format_type(tp: TypeHint) -> str:
     if isinstance(tp, type) and not is_parametrized(tp):
@@ -48,37 +37,6 @@ def format_loc_stack(loc_stack: LocStack[AnyLoc]) -> str:
     return fmt_tp
 
 
-T = TypeVar("T")
-
-
-@dataclass(frozen=True)
-class LocatedRequest(Request[T]):
-    loc_stack: LocStack
-
-    @property
-    def last_loc(self) -> AnyLoc:
-        return self.loc_stack.last
-
-
-def get_type_from_request(request: LocatedRequest) -> TypeHint:
-    return request.last_loc.cast_or_raise(TypeHintLoc, CannotProvide).type
-
-
-def try_normalize_type(tp: TypeHint) -> BaseNormType:
-    try:
-        return normalize_type(tp)
-    except ValueError:
-        raise CannotProvide(f"{tp} can not be normalized")
-
-
-class StrictCoercionRequest(LocatedRequest[bool]):
-    pass
-
-
-class DebugTrailRequest(LocatedRequest[DebugTrail]):
-    pass
-
-
 def find_owner_with_field(stack: LocStack) -> Tuple[TypeHintLoc, FieldLoc]:
     for next_loc, prev_loc in pairs(reversed(stack)):
         if next_loc.is_castable(FieldLoc):
diff --git a/src/adaptix/_internal/provider/located_request.py b/src/adaptix/_internal/provider/located_request.py
new file mode 100644
index 00000000..c5f09e73
--- /dev/null
+++ b/src/adaptix/_internal/provider/located_request.py
@@ -0,0 +1,108 @@
+from abc import ABC, abstractmethod
+from dataclasses import dataclass, replace
+from typing import Sequence, Tuple, Type, TypeVar, final
+
+from ..common import TypeHint, VarTuple
+from .essential import DirectMediator, Mediator, Provider, Request, RequestChecker, RequestHandler
+from .loc_stack_filtering import AnyLocStackChecker, LocStack, LocStackChecker, Pred, create_loc_stack_checker
+from .location import AnyLoc
+from .methods_provider import MethodsProvider
+from .request_checkers import AlwaysTrueRequestChecker
+
+T = TypeVar("T")
+
+
+@dataclass(frozen=True)
+class LocatedRequest(Request[T]):
+    loc_stack: LocStack
+
+    @property
+    def last_loc(self) -> AnyLoc:
+        return self.loc_stack.last
+
+
+class LocatedRequestChecker(RequestChecker[LocatedRequest]):
+    __slots__ = ("loc_stack_checker", )
+
+    def __init__(self, loc_stack_checker: LocStackChecker):
+        self.loc_stack_checker = loc_stack_checker
+
+    def check_request(self, mediator: DirectMediator, request: LocatedRequest, /) -> bool:
+        return self.loc_stack_checker.check_loc_stack(mediator, request.loc_stack)
+
+
+class LocatedRequestMethodsProvider(MethodsProvider):
+    _loc_stack_checker: LocStackChecker = AnyLocStackChecker()
+
+    @classmethod
+    def _validate_request_cls(cls, request_cls: Type[Request]) -> None:
+        if not issubclass(request_cls, LocatedRequest):
+            raise TypeError(
+                f"@method_handler of {LocatedRequestMethodsProvider} can process only child of {LocatedRequest}",
+            )
+
+    def _get_request_checker(self) -> RequestChecker:
+        return LocatedRequestChecker(self._loc_stack_checker)
+
+
+def for_predicate(pred: Pred):
+    def decorator(cls: Type[LocatedRequestMethodsProvider]):
+        if not (isinstance(cls, type) and issubclass(cls, LocatedRequestMethodsProvider)):
+            raise TypeError(f"Only {LocatedRequestMethodsProvider} child is allowed")
+
+        cls._loc_stack_checker = create_loc_stack_checker(pred)
+        return cls
+
+    return decorator
+
+
+class LocStackBoundingProvider(Provider):
+    def __init__(self, loc_stack_checker: LocStackChecker, provider: Provider):
+        self._loc_stack_checker = loc_stack_checker
+        self._provider = provider
+
+    def get_request_handlers(self) -> Sequence[Tuple[Type[Request], RequestChecker, RequestHandler]]:
+        return [
+            (request_cls, self._process_request_checker(request_cls, checker), handler)
+            for request_cls, checker, handler in self._provider.get_request_handlers()
+        ]
+
+    def _process_request_checker(self, request_cls: Type[Request], checker: RequestChecker) -> RequestChecker:
+        if issubclass(request_cls, LocatedRequest):
+            if isinstance(checker, AlwaysTrueRequestChecker):
+                return LocatedRequestChecker(self._loc_stack_checker)
+            if isinstance(checker, LocatedRequestChecker):
+                return LocatedRequestChecker(self._loc_stack_checker & checker.loc_stack_checker)
+        return checker
+
+
+LocatedRequestT = TypeVar("LocatedRequestT", bound=LocatedRequest)
+
+
+class LocatedRequestDelegatingProvider(Provider, ABC):
+    REQUEST_CLASSES: VarTuple[Type[LocatedRequest]] = ()
+
+    @final
+    def get_request_handlers(self) -> Sequence[Tuple[Type[Request], RequestChecker, RequestHandler]]:
+        request_checker = self.get_request_checker()
+
+        def delegating_request_handler(mediator, request):
+            tp = self.get_delegated_type(mediator, request)
+            return mediator.delegating_provide(
+                replace(
+                    request,
+                    loc_stack=request.loc_stack.replace_last_type(tp),
+                ),
+            )
+
+        return [
+            (request_cls, request_checker, delegating_request_handler)
+            for request_cls in self.REQUEST_CLASSES
+        ]
+
+    def get_request_checker(self) -> RequestChecker:
+        return AlwaysTrueRequestChecker()
+
+    @abstractmethod
+    def get_delegated_type(self, mediator: Mediator[LocatedRequestT], request: LocatedRequestT) -> TypeHint:
+        ...
diff --git a/src/adaptix/_internal/provider/methods_provider.py b/src/adaptix/_internal/provider/methods_provider.py
new file mode 100644
index 00000000..97654e33
--- /dev/null
+++ b/src/adaptix/_internal/provider/methods_provider.py
@@ -0,0 +1,154 @@
+import inspect
+from typing import Any, Callable, ClassVar, Dict, Iterable, Mapping, Sequence, Tuple, Type, TypeVar, final
+
+from ..type_tools import get_all_type_hints, is_subclass_soft, normalize_type, strip_tags
+from .essential import Mediator, Provider, Request, RequestChecker, RequestHandler
+from .request_checkers import AlwaysTrueRequestChecker
+
+__all__ = ("MethodsProvider", "method_handler")
+
+
+P = TypeVar("P", bound=Any)
+R = TypeVar("R", bound=Request)
+T = TypeVar("T")
+MethodHandler = Callable[[P, Mediator[T], R], T]
+
+_METHOD_HANDLER_REQUEST_CLS = "_method_handler_request_cls"
+
+
+def method_handler(func: MethodHandler[P, T, R], /) -> MethodHandler[P, T, R]:
+    """Marks method as request handler. See :class:`MethodsProvider` for details"""
+    request_cls = _infer_request_cls(func)
+    setattr(func, _METHOD_HANDLER_REQUEST_CLS, request_cls)
+    return func
+
+
+def _infer_request_cls(func) -> Type[Request]:
+    signature = inspect.signature(func)
+
+    params = list(signature.parameters.values())
+
+    if len(params) < 3:  # noqa: PLR2004
+        raise ValueError("Can not infer request class from callable")
+
+    if params[2].annotation == signature.empty:
+        raise ValueError("Can not infer request class from callable")
+
+    type_hints = get_all_type_hints(func)
+    request_tp = strip_tags(normalize_type(type_hints[params[2].name]))
+
+    if is_subclass_soft(request_tp.origin, Request):
+        return request_tp.source
+
+    raise TypeError("Request parameter must be subclass of Request")
+
+
+class MethodsProvider(Provider):
+    _mp_cls_request_to_method_name: ClassVar[Mapping[Type[Request], str]] = {}
+
+    def __init_subclass__(cls, **kwargs):
+        own_spa = _collect_class_own_request_cls_dict(cls)
+
+        parent_request_cls_dicts = [
+            parent._mp_cls_request_to_method_name
+            for parent in cls.__bases__
+            if issubclass(parent, MethodsProvider)
+        ]
+        cls._mp_cls_request_to_method_name = _merge_request_cls_dicts(cls, [*parent_request_cls_dicts, own_spa])
+        for request_cls in cls._mp_cls_request_to_method_name:
+            cls._validate_request_cls(request_cls)
+
+    @classmethod
+    def _validate_request_cls(cls, request_cls: Type[Request]) -> None:
+        pass
+
+    def _get_request_checker(self) -> RequestChecker:
+        return AlwaysTrueRequestChecker()
+
+    @final
+    def get_request_handlers(self) -> Sequence[Tuple[Type[Request], RequestChecker, RequestHandler]]:
+        request_checker = self._get_request_checker()
+        return [
+            (request_cls, request_checker, getattr(self, method_name))
+            for request_cls, method_name in self._mp_cls_request_to_method_name.items()
+        ]
+
+
+def _request_cls_attached_to_several_method_handlers(
+    cls: type,
+    name1: str,
+    name2: str,
+    request_cls: Type[Request],
+):
+    return TypeError(
+        f"The {cls} has several @method_handler"
+        " that attached to the same Request class"
+        f" ({name1!r} and {name2!r} attached to {request_cls})",
+    )
+
+
+def _method_handler_has_different_request_cls(
+    cls: type,
+    name: str,
+    request_cls1: Type[Request],
+    request_cls2: Type[Request],
+):
+    return TypeError(
+        f"The {cls} has @method_handler"
+        " that attached to the different Request class"
+        f" ({name!r} attached to {request_cls1} and {request_cls2})",
+    )
+
+
+_RequestClsToMethodName = Dict[Type[Request], str]
+
+
+def _collect_class_own_request_cls_dict(cls) -> _RequestClsToMethodName:
+    mapping: _RequestClsToMethodName = {}
+
+    for attr_name in vars(cls):
+        try:
+            attr_value = getattr(cls, attr_name)
+        except AttributeError:
+            continue
+        if hasattr(attr_value, _METHOD_HANDLER_REQUEST_CLS):
+            request_cls = getattr(attr_value, _METHOD_HANDLER_REQUEST_CLS)
+            if request_cls in mapping:
+                old_name = mapping[request_cls]
+                raise _request_cls_attached_to_several_method_handlers(
+                    cls,
+                    attr_name,
+                    old_name,
+                    request_cls,
+                )
+
+            mapping[request_cls] = attr_name
+
+    return mapping
+
+
+def _merge_request_cls_dicts(cls: type, dict_iter: Iterable[_RequestClsToMethodName]) -> _RequestClsToMethodName:
+    name_to_request_cls: Dict[str, Type[Request]] = {}
+    request_cls_to_name: _RequestClsToMethodName = {}
+    for dct in dict_iter:
+        for request_cls, name in dct.items():
+            if request_cls in request_cls_to_name:
+                raise _request_cls_attached_to_several_method_handlers(
+                    cls,
+                    request_cls_to_name[request_cls],
+                    name,
+                    request_cls,
+                )
+
+            if name in name_to_request_cls and request_cls != name_to_request_cls[name]:
+                raise _method_handler_has_different_request_cls(
+                    cls,
+                    name,
+                    name_to_request_cls[name],
+                    request_cls,
+                )
+
+            request_cls_to_name[request_cls] = name
+            name_to_request_cls[name] = request_cls
+
+    return request_cls_to_name
diff --git a/src/adaptix/_internal/provider/overlay_schema.py b/src/adaptix/_internal/provider/overlay_schema.py
index b718ed62..3cace3d3 100644
--- a/src/adaptix/_internal/provider/overlay_schema.py
+++ b/src/adaptix/_internal/provider/overlay_schema.py
@@ -5,9 +5,10 @@
 from ..type_tools import strip_alias
 from ..utils import Omitted
 from .essential import CannotProvide, Mediator
+from .loc_stack_filtering import LocStack
+from .located_request import LocatedRequest
+from .methods_provider import MethodsProvider, method_handler
 from .provider_wrapper import Chain
-from .request_cls import LocatedRequest, LocStack
-from .static_provider import StaticProvider, static_provision_action
 
 
 @dataclass(frozen=True)
@@ -106,12 +107,12 @@ def provide_schema(overlay: Type[Overlay[Sc]], mediator: Mediator, loc_stack: Lo
     return stacked_overlay.to_schema()
 
 
-class OverlayProvider(StaticProvider):
+class OverlayProvider(MethodsProvider):
     def __init__(self, overlays: Iterable[Overlay], chain: Optional[Chain]):
         self._chain = chain
         self._overlays = ClassMap(*overlays)
 
-    @static_provision_action
+    @method_handler
     def _provide_overlay(self, mediator: Mediator, request: OverlayRequest):
         try:
             overlay = self._overlays[request.overlay_cls]
diff --git a/src/adaptix/_internal/provider/provider_template.py b/src/adaptix/_internal/provider/provider_template.py
deleted file mode 100644
index 243e54d4..00000000
--- a/src/adaptix/_internal/provider/provider_template.py
+++ /dev/null
@@ -1,45 +0,0 @@
-from typing import Generic, Optional, Type, TypeVar
-
-from .essential import CannotProvide, Mediator, Request
-from .loc_stack_filtering import LocStackChecker, P, Pred, create_loc_stack_checker
-from .provider_wrapper import ProviderWithLSC, RequestClassDeterminedProvider
-from .static_provider import StaticProvider
-
-T = TypeVar("T")
-
-
-class ProviderWithAttachableLSC(StaticProvider, ProviderWithLSC):
-    _loc_stack_checker: LocStackChecker = P.ANY
-
-    def get_loc_stack_checker(self) -> Optional[LocStackChecker]:
-        return self._loc_stack_checker
-
-
-def for_predicate(pred: Pred):
-    def decorator(cls: Type[ProviderWithAttachableLSC]):
-        if not (isinstance(cls, type) and issubclass(cls, ProviderWithAttachableLSC)):
-            raise TypeError(f"Only {ProviderWithAttachableLSC} child is allowed")
-
-        # noinspection PyProtectedMember
-        cls._loc_stack_checker = create_loc_stack_checker(pred)
-        return cls
-
-    return decorator
-
-
-class ValueProvider(RequestClassDeterminedProvider, Generic[T]):
-    def __init__(self, request_cls: Type[Request[T]], value: T):
-        self._request_cls = request_cls
-        self._value = value
-
-    def apply_provider(self, mediator: Mediator, request: Request):
-        if not isinstance(request, self._request_cls):
-            raise CannotProvide
-
-        return self._value
-
-    def __repr__(self):
-        return f"{type(self).__name__}({self._request_cls}, {self._value})"
-
-    def maybe_can_process_request_cls(self, request_cls: Type[Request]) -> bool:
-        return issubclass(request_cls, self._request_cls)
diff --git a/src/adaptix/_internal/provider/provider_wrapper.py b/src/adaptix/_internal/provider/provider_wrapper.py
index e5f5bbfe..b306e73d 100644
--- a/src/adaptix/_internal/provider/provider_wrapper.py
+++ b/src/adaptix/_internal/provider/provider_wrapper.py
@@ -1,111 +1,63 @@
-from abc import ABC, abstractmethod
+import itertools
 from enum import Enum
-from typing import Optional, Type, TypeVar
+from typing import Sequence, Tuple, Type, TypeVar
 
-from .essential import AggregateCannotProvide, CannotProvide, Mediator, Provider, Request
-from .loc_stack_filtering import LocStackChecker
-from .request_cls import LocatedRequest
+from .essential import Mediator, Provider, Request, RequestChecker, RequestHandler
 
 T = TypeVar("T")
 
 
-class RequestClassDeterminedProvider(Provider, ABC):
-    @abstractmethod
-    def maybe_can_process_request_cls(self, request_cls: Type[Request]) -> bool:
-        ...
-
-
-class ProviderWithLSC(Provider, ABC):
-    @abstractmethod
-    def get_loc_stack_checker(self) -> Optional[LocStackChecker]:
-        ...
-
-    def _apply_loc_stack_checker(self, mediator: Mediator, request: Request) -> None:
-        if not isinstance(request, LocatedRequest):
-            raise CannotProvide
-
-        loc_stack_checker = self.get_loc_stack_checker()
-        if loc_stack_checker is None:
-            return
-
-        if not loc_stack_checker.check_loc_stack(mediator, request.loc_stack):
-            raise CannotProvide
-
-
-class BoundingProvider(RequestClassDeterminedProvider, ProviderWithLSC):
-    def __init__(self, loc_stack_checker: LocStackChecker, provider: Provider):
-        self._loc_stack_checker = loc_stack_checker
-        self._provider = provider
-
-    def apply_provider(self, mediator: Mediator, request: Request[T]) -> T:
-        self._apply_loc_stack_checker(mediator, request)
-        return self._provider.apply_provider(mediator, request)
-
-    def __repr__(self):
-        return f"{type(self).__name__}({self._loc_stack_checker}, {self._provider})"
-
-    def maybe_can_process_request_cls(self, request_cls: Type[Request]) -> bool:
-        if isinstance(self._provider, RequestClassDeterminedProvider):
-            return self._provider.maybe_can_process_request_cls(request_cls)
-        return True
-
-    def get_loc_stack_checker(self) -> Optional[LocStackChecker]:
-        return self._loc_stack_checker
-
-
-class ConcatProvider(RequestClassDeterminedProvider):
+class ConcatProvider(Provider):
     def __init__(self, *providers: Provider):
         self._providers = providers
 
-    def apply_provider(self, mediator: Mediator[T], request: Request[T]) -> T:
-        exceptions = []
-
-        for provider in self._providers:
-            try:
-                return provider.apply_provider(mediator, request)
-            except CannotProvide as e:
-                exceptions.append(e)
-
-        raise AggregateCannotProvide.make("", exceptions)
+    def get_request_handlers(self) -> Sequence[Tuple[Type[Request], RequestChecker, RequestHandler]]:
+        return list(
+            itertools.chain.from_iterable(
+                provider.get_request_handlers()
+                for provider in self._providers
+            ),
+        )
 
     def __repr__(self):
         return f"{type(self).__name__}({self._providers})"
 
-    def maybe_can_process_request_cls(self, request_cls: Type[Request]) -> bool:
-        return any(
-            not isinstance(provider, RequestClassDeterminedProvider)
-            or provider.maybe_can_process_request_cls(request_cls)
-            for provider in self._providers
-        )
-
 
 class Chain(Enum):
     FIRST = "FIRST"
     LAST = "LAST"
 
 
-class ChainingProvider(RequestClassDeterminedProvider):
+RequestT = TypeVar("RequestT", bound=Request)
+ResponseT = TypeVar("ResponseT")
+
+
+class ChainingProvider(Provider):
     def __init__(self, chain: Chain, provider: Provider):
         self._chain = chain
         self._provider = provider
 
-    def apply_provider(self, mediator: Mediator[T], request: Request[T]) -> T:
-        current_processor = self._provider.apply_provider(mediator, request)
-        next_processor = mediator.provide_from_next()
-
-        if self._chain == Chain.FIRST:
-            return self._make_chain(current_processor, next_processor)
-        if self._chain == Chain.LAST:
-            return self._make_chain(next_processor, current_processor)
-        raise ValueError
-
     def _make_chain(self, first, second):
         def chain_processor(data):
             return second(first(data))
 
         return chain_processor
 
-    def maybe_can_process_request_cls(self, request_cls: Type[Request]) -> bool:
-        if isinstance(self._provider, RequestClassDeterminedProvider):
-            return self._provider.maybe_can_process_request_cls(request_cls)
-        return True
+    def _wrap_handler(self, handler: RequestHandler[ResponseT, RequestT]) -> RequestHandler[ResponseT, RequestT]:
+        def chaining_handler(mediator: Mediator[ResponseT], request: RequestT) -> ResponseT:
+            current_processor = handler(mediator, request)
+            next_processor = mediator.provide_from_next()
+
+            if self._chain == Chain.FIRST:
+                return self._make_chain(current_processor, next_processor)
+            if self._chain == Chain.LAST:
+                return self._make_chain(next_processor, current_processor)
+            raise ValueError
+
+        return chaining_handler
+
+    def get_request_handlers(self) -> Sequence[Tuple[Type[Request], RequestChecker, RequestHandler]]:
+        return [
+            (request_cls, checker, self._wrap_handler(handler))
+            for request_cls, checker, handler in self._provider.get_request_handlers()
+        ]
diff --git a/src/adaptix/_internal/provider/request_checkers.py b/src/adaptix/_internal/provider/request_checkers.py
new file mode 100644
index 00000000..0e5fa58b
--- /dev/null
+++ b/src/adaptix/_internal/provider/request_checkers.py
@@ -0,0 +1,11 @@
+from adaptix._internal.provider.essential import DirectMediator, Request, RequestChecker
+
+
+class AlwaysTrueRequestChecker(RequestChecker):
+    def check_request(self, mediator: DirectMediator, request: Request, /) -> bool:
+        return True
+
+    def __eq__(self, other):
+        if isinstance(other, type(self)):
+            return True
+        return NotImplemented
diff --git a/src/adaptix/_internal/provider/shape_provider.py b/src/adaptix/_internal/provider/shape_provider.py
index f32a970a..84a5266d 100644
--- a/src/adaptix/_internal/provider/shape_provider.py
+++ b/src/adaptix/_internal/provider/shape_provider.py
@@ -24,10 +24,10 @@
 from ..provider.essential import CannotProvide, Mediator
 from ..provider.loc_stack_filtering import create_loc_stack_checker
 from ..type_tools.generic_resolver import GenericResolver, MembersStorage
-from .provider_template import ProviderWithAttachableLSC
+from .essential import RequestChecker
+from .located_request import LocatedRequest, LocatedRequestChecker
+from .methods_provider import MethodsProvider, method_handler
 from .provider_wrapper import ConcatProvider
-from .request_cls import LocatedRequest, TypeHintLoc
-from .static_provider import StaticProvider, static_provision_action
 
 
 @dataclass(frozen=True)
@@ -40,7 +40,7 @@ class OutputShapeRequest(LocatedRequest[OutputShape]):
     pass
 
 
-class ShapeProvider(StaticProvider):
+class ShapeProvider(MethodsProvider):
     def __init__(self, introspector: ShapeIntrospector):
         self._introspector = introspector
 
@@ -57,18 +57,16 @@ def _get_shape(self, tp) -> Shape:
         except IntrospectionError as e:
             raise CannotProvide from e
 
-    @static_provision_action
+    @method_handler
     def _provide_input_shape(self, mediator: Mediator, request: InputShapeRequest) -> InputShape:
-        loc = request.last_loc.cast_or_raise(TypeHintLoc, CannotProvide)
-        shape = self._get_shape(loc.type)
+        shape = self._get_shape(request.last_loc.type)
         if shape.input is None:
             raise CannotProvide
         return shape.input
 
-    @static_provision_action
+    @method_handler
     def _provide_output_shape(self, mediator: Mediator, request: OutputShapeRequest) -> OutputShape:
-        loc = request.last_loc.cast_or_raise(TypeHintLoc, CannotProvide)
-        shape = self._get_shape(loc.type)
+        shape = self._get_shape(request.last_loc.type)
         if shape.output is None:
             raise CannotProvide
         return shape.output
@@ -86,7 +84,7 @@ def _provide_output_shape(self, mediator: Mediator, request: OutputShapeRequest)
 )
 
 
-class PropertyExtender(StaticProvider):
+class PropertyExtender(MethodsProvider):
     def __init__(
         self,
         output_fields: Iterable[OutputField],
@@ -104,9 +102,9 @@ def __init__(
                 f" all fields must use DescriptorAccessor",
             )
 
-    @static_provision_action
+    @method_handler
     def _provide_output_shape(self, mediator: Mediator[OutputShape], request: OutputShapeRequest) -> OutputShape:
-        tp = request.last_loc.cast_or_raise(TypeHintLoc, CannotProvide).type
+        tp = request.last_loc.type
         shape = mediator.provide_from_next()
 
         additional_fields = tuple(
@@ -187,7 +185,7 @@ def provide_generic_resolved_shape(mediator: Mediator, request: LocatedRequest[S
 T = TypeVar("T")
 
 
-class SimilarShapeProvider(ProviderWithAttachableLSC):
+class SimilarShapeProvider(MethodsProvider):
     def __init__(self, target: TypeHint, prototype: TypeHint, *, for_input: bool = True, for_output: bool = True):
         self._target = target
         self._prototype = prototype
@@ -195,12 +193,14 @@ def __init__(self, target: TypeHint, prototype: TypeHint, *, for_input: bool = T
         self._for_input = for_input
         self._for_output = for_output
 
-    @static_provision_action
+    def _get_request_checker(self) -> RequestChecker:
+        return LocatedRequestChecker(self._loc_stack_checker)
+
+    @method_handler
     def _provide_input_shape(self, mediator: Mediator, request: InputShapeRequest) -> InputShape:
         if not self._for_input:
             raise CannotProvide
 
-        self._apply_loc_stack_checker(mediator, request)
         shape = mediator.delegating_provide(
             replace(
                 request,
@@ -209,12 +209,11 @@ def _provide_input_shape(self, mediator: Mediator, request: InputShapeRequest) -
         )
         return replace(shape, constructor=self._target)
 
-    @static_provision_action
+    @method_handler
     def _provide_output_shape(self, mediator: Mediator, request: OutputShapeRequest) -> OutputShape:
         if not self._for_output:
             raise CannotProvide
 
-        self._apply_loc_stack_checker(mediator, request)
         return mediator.delegating_provide(
             replace(
                 request,
diff --git a/src/adaptix/_internal/provider/static_provider.py b/src/adaptix/_internal/provider/static_provider.py
deleted file mode 100644
index d1e92fac..00000000
--- a/src/adaptix/_internal/provider/static_provider.py
+++ /dev/null
@@ -1,194 +0,0 @@
-import inspect
-from inspect import isfunction
-from typing import Callable, ClassVar, Dict, Iterable, Type, TypeVar, final, overload
-
-from ..datastructures import ClassDispatcher
-from ..type_tools import get_all_type_hints, is_subclass_soft, normalize_type, strip_tags
-from .essential import CannotProvide, Mediator, Provider, Request
-from .provider_wrapper import RequestClassDeterminedProvider
-
-__all__ = ("StaticProvider", "static_provision_action", "RequestDispatcher")
-
-RequestDispatcher = ClassDispatcher[Request, str]
-
-R = TypeVar("R", bound=Request)
-P = TypeVar("P", bound=Provider)
-T = TypeVar("T")
-SPA = Callable[[P, Mediator[T], R], T]
-
-_SPA_RC_STORAGE = "_spa_request_cls"
-
-
-@overload
-def static_provision_action() -> Callable[[SPA[P, T, R]], SPA[P, T, R]]:
-    ...
-
-
-@overload  # type: ignore[overload-overlap]
-def static_provision_action(request_cls: Type[Request], /) -> Callable[[SPA[P, T, R]], SPA[P, T, R]]:
-    ...
-
-
-@overload
-def static_provision_action(func: SPA[P, T, R], /) -> SPA[P, T, R]:
-    ...
-
-
-def static_provision_action(arg=None):
-    """Marks method as ``@static_provision_action``.
-    See :class:`StaticProvider` for details
-    """
-
-    if arg is None:
-        return static_provision_action
-
-    if is_subclass_soft(arg, Request):
-        return _make_spa_decorator(arg)
-
-    if isfunction(arg):
-        return _make_spa_decorator(_infer_rc(arg))(arg)
-
-    if hasattr(arg, "__func__"):
-        return _make_spa_decorator(_infer_rc(arg.__func__))(arg)
-
-    raise TypeError(
-        "static_provision_action must be applied"
-        " as @static_provision_action or @static_provision_action()"
-        " or @static_provision_action(Request)",
-    )
-
-
-def _infer_rc(func) -> Type[Request]:
-    signature = inspect.signature(func)
-
-    params = list(signature.parameters.values())
-
-    if len(params) < 3:  # noqa: PLR2004
-        raise ValueError("Can not infer request class from callable")
-
-    if params[2].annotation == signature.empty:
-        raise ValueError("Can not infer request class from callable")
-
-    type_hints = get_all_type_hints(func)
-    request_tp = strip_tags(normalize_type(type_hints[params[2].name]))
-
-    if is_subclass_soft(request_tp.origin, Request):
-        return request_tp.source
-
-    raise TypeError("Request parameter must be subclass of Request")
-
-
-def _make_spa_decorator(request_cls: Type[R]):
-    def spa_decorator(func: Callable[[P, Mediator, R], T]):
-        if hasattr(func, _SPA_RC_STORAGE):
-            raise ValueError("@static_provision_action decorator cannot be applied twice")
-
-        setattr(func, _SPA_RC_STORAGE, request_cls)
-        return func
-
-    return spa_decorator
-
-
-class StaticProvider(RequestClassDeterminedProvider):
-    """Provider which instances can process same set of Request classes.
-
-    Subclass defines provision actions wrapping method by decorator
-    ``@static_provision_action(request_cls)``. Argument of decorator attaching
-    method to specified Request class.
-    It means that that provision action will be called for specified
-    request, or it's subclass. See :class:`Provider` for details.
-
-    You can omit request_cls parameter and decorator try to infer it introspecting method signature.
-
-    Subclasses cannot have multiple methods attached to the same request.
-
-    During subclassing, ``StaticProvider`` goes through attributes of the class
-    and collects all methods wrapped by :func:`static_provision_action` decorator.
-    Then it merges list of new :func:`static_provision_action`'s with the parent ones.
-    """
-    _sp_cls_request_dispatcher: ClassVar[RequestDispatcher] = RequestDispatcher()
-
-    def __init_subclass__(cls, **kwargs):
-        own_spa = _collect_class_own_rc_dict(cls)
-
-        parent_rd_dicts = [
-            parent._sp_cls_request_dispatcher.to_dict()
-            for parent in cls.__bases__
-            if issubclass(parent, StaticProvider)
-        ]
-
-        result = _merge_rc_dicts(cls, [*parent_rd_dicts, own_spa])
-
-        cls._sp_cls_request_dispatcher = RequestDispatcher(result)
-
-    @final
-    def apply_provider(self, mediator: Mediator, request: Request[T]) -> T:
-        try:
-            attr_name = self._sp_cls_request_dispatcher.dispatch(type(request))
-        except KeyError:
-            raise CannotProvide
-
-        return getattr(self, attr_name)(mediator, request)
-
-    @final
-    def maybe_can_process_request_cls(self, request_cls: Type[Request]) -> bool:
-        try:
-            self._sp_cls_request_dispatcher.dispatch(request_cls)
-        except KeyError:
-            return False
-        return True
-
-
-def _rc_attached_to_several_spa(cls: type, name1: str, name2: str, rc: Type[Request]):
-    return TypeError(
-        f"The {cls} has several @static_provision_action"
-        " that attached to the same Request class"
-        f" ({name1!r} and {name2!r} attached to {rc})",
-    )
-
-
-def _spa_has_different_rc(cls: type, name: str, rc1: Type[Request], rc2: Type[Request]):
-    return TypeError(
-        f"The {cls} has @static_provision_action"
-        " that attached to the different Request class"
-        f" ({name!r} attached to {rc1} and {rc2})",
-    )
-
-
-_RcDict = Dict[Type[Request], str]
-
-
-def _collect_class_own_rc_dict(cls) -> _RcDict:
-    mapping: _RcDict = {}
-
-    for attr_name in vars(cls):
-        try:
-            attr_value = getattr(cls, attr_name)
-        except AttributeError:
-            continue
-        if hasattr(attr_value, _SPA_RC_STORAGE):
-            rc = getattr(attr_value, _SPA_RC_STORAGE)
-            if rc in mapping:
-                old_name = mapping[rc]
-                raise _rc_attached_to_several_spa(cls, attr_name, old_name, rc)
-
-            mapping[rc] = attr_name
-
-    return mapping
-
-
-def _merge_rc_dicts(cls: type, dict_iter: Iterable[_RcDict]) -> _RcDict:
-    name_to_rc: Dict[str, Type[Request]] = {}
-    rc_to_name: _RcDict = {}
-    for dct in dict_iter:
-        for rc, name in dct.items():
-            if rc in rc_to_name:
-                raise _rc_attached_to_several_spa(cls, rc_to_name[rc], name, rc)
-
-            if name in name_to_rc and rc != name_to_rc[name]:
-                raise _spa_has_different_rc(cls, name, name_to_rc[name], rc)
-
-            rc_to_name[rc] = name
-            name_to_rc[name] = rc
-
-    return rc_to_name
diff --git a/src/adaptix/_internal/provider/value_provider.py b/src/adaptix/_internal/provider/value_provider.py
new file mode 100644
index 00000000..8371eccd
--- /dev/null
+++ b/src/adaptix/_internal/provider/value_provider.py
@@ -0,0 +1,20 @@
+from typing import Generic, Sequence, Tuple, Type, TypeVar
+
+from .essential import Provider, Request, RequestChecker, RequestHandler
+from .request_checkers import AlwaysTrueRequestChecker
+
+T = TypeVar("T")
+
+
+class ValueProvider(Provider, Generic[T]):
+    def __init__(self, request_cls: Type[Request[T]], value: T):
+        self._request_cls = request_cls
+        self._value = value
+
+    def get_request_handlers(self) -> Sequence[Tuple[Type[Request], RequestChecker, RequestHandler]]:
+        return [
+            (self._request_cls, AlwaysTrueRequestChecker(), lambda m, r: self._value),
+        ]
+
+    def __repr__(self):
+        return f"{type(self).__name__}({self._request_cls}, {self._value})"
diff --git a/src/adaptix/_internal/retort/base_retort.py b/src/adaptix/_internal/retort/base_retort.py
index 511765e2..a5281bfc 100644
--- a/src/adaptix/_internal/retort/base_retort.py
+++ b/src/adaptix/_internal/retort/base_retort.py
@@ -1,11 +1,9 @@
-from abc import ABC, ABCMeta, abstractmethod
+from abc import ABC, ABCMeta
 from typing import ClassVar, Iterable, Sequence, TypeVar
 
 from ..common import VarTuple
-from ..provider.essential import Mediator, Provider, Request
+from ..provider.essential import Provider
 from ..utils import Cloneable, ForbiddingDescriptor
-from .mediator import BuiltinMediator, ErrorRepresentor, RecursionResolver
-from .routing import IntrospectingRecipeSearcher, RecipeSearcher
 
 
 class RetortMeta(ABCMeta):  # inherits from ABCMeta to be compatible with ABC
@@ -62,33 +60,3 @@ def _calculate_derived(self) -> None:
             + self._get_config_recipe()
             + self._full_class_recipe
         )
-        self._searcher = self._create_searcher(self._full_recipe)
-
-    def _create_searcher(self, full_recipe: Sequence[Provider]) -> RecipeSearcher:
-        return IntrospectingRecipeSearcher(full_recipe)
-
-    @abstractmethod
-    def _create_recursion_resolver(self) -> RecursionResolver:
-        ...
-
-    @abstractmethod
-    def _get_error_representor(self) -> ErrorRepresentor:
-        ...
-
-    def _create_mediator(self) -> Mediator:
-        recursion_resolver = self._create_recursion_resolver()
-        error_representor = self._get_error_representor()
-        return BuiltinMediator(
-            self._searcher,
-            recursion_resolver,
-            error_representor,
-        )
-
-    def _provide_from_recipe(self, request: Request[T]) -> T:
-        """Process request iterating over the result of _get_full_recipe()
-        :param request:
-        :return: request result
-        :raise CannotProvide: request was not processed
-        """
-        mediator = self._create_mediator()
-        return mediator.provide(request)
diff --git a/src/adaptix/_internal/retort/builtin_mediator.py b/src/adaptix/_internal/retort/builtin_mediator.py
new file mode 100644
index 00000000..424b64b9
--- /dev/null
+++ b/src/adaptix/_internal/retort/builtin_mediator.py
@@ -0,0 +1,47 @@
+from abc import ABC, abstractmethod
+from typing import Callable, Generic, Mapping, Type, TypeVar
+
+from ..provider.essential import CannotProvide, Mediator, Request
+
+T = TypeVar("T")
+
+
+RequestT = TypeVar("RequestT", bound=Request)
+ResponseT = TypeVar("ResponseT")
+
+
+class RequestBus(ABC, Generic[RequestT, ResponseT]):
+    @abstractmethod
+    def send(self, request: RequestT) -> ResponseT:
+        pass
+
+    @abstractmethod
+    def send_chaining(self, request: RequestT, search_offset: int) -> ResponseT:
+        pass
+
+
+class BuiltinMediator(Mediator[ResponseT], Generic[ResponseT]):
+    __slots__ = ("_request_buses", "_request", "_search_offset", "_no_request_bus_error_maker")
+
+    def __init__(
+        self,
+        request_buses: Mapping[Type[Request], RequestBus],
+        request: Request,
+        search_offset: int,
+        no_request_bus_error_maker: Callable[[Request], CannotProvide],
+    ):
+        self._request_buses = request_buses
+        self._request = request
+        self._search_offset = search_offset
+        self._no_request_bus_error_maker = no_request_bus_error_maker
+
+    def provide(self, request: Request[T]) -> T:
+        try:
+            request_bus = self._request_buses[type(request)]
+        except KeyError:
+            raise self._no_request_bus_error_maker(request) from None
+
+        return request_bus.send(request)
+
+    def provide_from_next(self) -> ResponseT:
+        return self._request_buses[type(self._request)].send_chaining(self._request, self._search_offset)
diff --git a/src/adaptix/_internal/retort/mediator.py b/src/adaptix/_internal/retort/mediator.py
deleted file mode 100644
index a3706ca0..00000000
--- a/src/adaptix/_internal/retort/mediator.py
+++ /dev/null
@@ -1,100 +0,0 @@
-from abc import ABC, abstractmethod
-from typing import Any, Dict, Generic, Iterable, Optional, TypeVar
-
-from ..provider.essential import AggregateCannotProvide, CannotProvide, Mediator, Request
-from ..utils import add_note
-from .routing import RecipeSearcher
-
-T = TypeVar("T")
-
-
-class RecursionResolver(ABC, Generic[T]):
-    @abstractmethod
-    def track_recursion(self, request: Request[T]) -> Optional[Any]:
-        ...
-
-    @abstractmethod
-    def process_request_result(self, request: Request[T], result: T) -> None:
-        ...
-
-
-E = TypeVar("E", bound=Exception)
-
-
-class ErrorRepresentor(ABC):
-    @abstractmethod
-    def get_no_provider_description(self, request: Request) -> str:
-        ...
-
-    @abstractmethod
-    def get_request_context_notes(self, request: Request) -> Iterable[str]:
-        ...
-
-
-class BuiltinMediator(Mediator):
-    def __init__(
-        self,
-        searcher: RecipeSearcher,
-        recursion_resolver: RecursionResolver,
-        error_representor: ErrorRepresentor,
-    ):
-        self.searcher = searcher
-        self.recursion_resolver = recursion_resolver
-        self.error_representor = error_representor
-
-        self._current_request: Optional[Request] = None
-        self.next_offset = 0
-        self.recursion_stubs: Dict[Request, Any] = {}
-
-    def provide(self, request: Request[T]) -> T:
-        stub = self.recursion_resolver.track_recursion(request)
-        if stub is not None:
-            return stub
-
-        self._current_request = request
-        try:
-            result = self._provide_non_recursive(request, 0)
-        finally:
-            self._current_request = None
-
-        self.recursion_resolver.process_request_result(request, result)
-        return result
-
-    def provide_from_next(self) -> Any:
-        if self._current_request is None:
-            raise ValueError
-        return self._provide_non_recursive(self._current_request, self.next_offset)
-
-    def _provide_non_recursive(self, request: Request[T], search_offset: int) -> T:
-        init_next_offset = self.next_offset
-        exceptions = []
-        for provide_callable, next_offset in self.searcher.search_candidates(
-            search_offset, request,
-        ):
-            self.next_offset = next_offset
-            try:
-                result = provide_callable(self, request)
-            except CannotProvide as e:
-                if e.is_terminal:
-                    self.next_offset = init_next_offset
-                    raise self._attach_request_context_note(e, request)
-                exceptions.append(e)
-                continue
-
-            self.next_offset = init_next_offset
-            return result
-
-        raise self._attach_request_context_note(
-            AggregateCannotProvide.make(
-                self.error_representor.get_no_provider_description(request),
-                exceptions,
-                is_demonstrative=True,
-            ),
-            request,
-        )
-
-    def _attach_request_context_note(self, exc: E, request: Request) -> E:
-        notes = self.error_representor.get_request_context_notes(request)
-        for note in notes:
-            add_note(exc, note)
-        return exc
diff --git a/src/adaptix/_internal/retort/operating_retort.py b/src/adaptix/_internal/retort/operating_retort.py
index 03b647ba..15dd0547 100644
--- a/src/adaptix/_internal/retort/operating_retort.py
+++ b/src/adaptix/_internal/retort/operating_retort.py
@@ -1,15 +1,41 @@
 from abc import ABC
-from typing import Any, Dict, Iterable, List, Mapping, Optional, Type
+from collections import defaultdict
+from typing import (
+    Any,
+    Callable,
+    DefaultDict,
+    Dict,
+    Generic,
+    Iterable,
+    List,
+    Mapping,
+    Optional,
+    Sequence,
+    Tuple,
+    Type,
+    TypeVar,
+)
 
-from ..common import VarTuple
 from ..conversion.request_cls import CoercerRequest, LinkingRequest
 from ..morphing.request_cls import DumperRequest, LoaderRequest
-from ..provider.essential import AggregateCannotProvide, CannotProvide, Mediator, Provider, Request
+from ..provider.essential import (
+    AggregateCannotProvide,
+    CannotProvide,
+    Mediator,
+    Provider,
+    Request,
+    RequestChecker,
+    RequestHandler,
+)
+from ..provider.loc_stack_tools import format_loc_stack
+from ..provider.located_request import LocatedRequest
 from ..provider.location import AnyLoc
-from ..provider.request_cls import LocatedRequest, LocStack, format_loc_stack
+from ..provider.request_checkers import AlwaysTrueRequestChecker
 from ..utils import add_note, copy_exception_dunders, with_module
 from .base_retort import BaseRetort
-from .mediator import ErrorRepresentor, RecursionResolver, T
+from .builtin_mediator import BuiltinMediator, RequestBus, T
+from .request_bus import BasicRequestBus, ErrorRepresentor, RecursionResolver, RecursiveRequestBus, RequestRouter
+from .routers import CheckerAndHandler, SimpleRouter, create_router_for_located_request
 
 
 class FuncWrapper:
@@ -22,16 +48,14 @@ def set_func(self, func):
         self.__call__ = func.__call__
 
 
-class MorphingRecursionResolver(RecursionResolver):
-    REQUEST_CLASSES: VarTuple[Type[LocatedRequest]] = (LoaderRequest, DumperRequest)
+CallableT = TypeVar("CallableT", bound=Callable)
 
+
+class LocatedRequestCallableRecursionResolver(RecursionResolver[LocatedRequest, CallableT], Generic[CallableT]):
     def __init__(self) -> None:
         self._loc_to_stub: Dict[AnyLoc, FuncWrapper] = {}
 
-    def track_recursion(self, request: Request[T]) -> Optional[Any]:
-        if not isinstance(request, self.REQUEST_CLASSES):
-            return None
-
+    def track_recursion(self, request: LocatedRequest) -> Optional[Any]:
         if request.loc_stack.count(request.last_loc) == 1:
             return None
 
@@ -39,66 +63,76 @@ def track_recursion(self, request: Request[T]) -> Optional[Any]:
         self._loc_to_stub[request.last_loc] = stub
         return stub
 
-    def process_request_result(self, request: Request[T], result: T) -> None:
-        if isinstance(request, self.REQUEST_CLASSES) and request.last_loc in self._loc_to_stub:
-            self._loc_to_stub.pop(request.last_loc).set_func(result)
-
+    def track_response(self, request: LocatedRequest, response: CallableT) -> None:
+        if request.last_loc in self._loc_to_stub:
+            self._loc_to_stub.pop(request.last_loc).set_func(response)
 
-@with_module("adaptix")
-class ProviderNotFoundError(Exception):
-    def __init__(self, message: str):
-        self.message = message
 
-    def __str__(self):
-        return self.message
+RequestT = TypeVar("RequestT", bound=Request)
+LocatedRequestT = TypeVar("LocatedRequestT", bound=LocatedRequest)
 
 
-class BuiltinErrorRepresentor(ErrorRepresentor):
-    _NO_PROVIDER_DESCRIPTION_METHOD: Mapping[Type[Request], str] = {
-        LinkingRequest: "_get_linking_request_description",
-    }
-    _NO_PROVIDER_DESCRIPTION_CONST: Mapping[Type[Request], str] = {
-        LoaderRequest: "Cannot find loader",
-        DumperRequest: "Cannot find dumper",
-        CoercerRequest: "Cannot find coercer",
-    }
+class BaseRequestErrorRepresentor(ErrorRepresentor[RequestT], Generic[RequestT]):
+    def __init__(self, not_found_desc: str):
+        self._not_found_desc = not_found_desc
 
-    def _get_linking_request_description(self, request: LinkingRequest) -> str:
-        dst_desc = self._get_loc_stack_desc(request.destination)
-        return f"Cannot find paired field of `{dst_desc}` for linking"
+    def get_request_context_notes(self, request: RequestT) -> Iterable[str]:
+        return ()
 
-    def get_no_provider_description(self, request: Request) -> str:
-        request_cls = type(request)
-        if request_cls in self._NO_PROVIDER_DESCRIPTION_METHOD:
-            return getattr(self, self._NO_PROVIDER_DESCRIPTION_METHOD[request_cls])(request)
-        if request_cls in self._NO_PROVIDER_DESCRIPTION_CONST:
-            return self._NO_PROVIDER_DESCRIPTION_CONST[request_cls]
-        return f"There is no provider that can process {request}"
+    def get_provider_not_found_description(self, request: RequestT) -> str:
+        return self._not_found_desc
 
-    def _get_loc_stack_desc(self, loc_stack: LocStack[AnyLoc]) -> str:
-        return format_loc_stack(loc_stack)
 
-    def _get_located_request_context_notes(self, request: LocatedRequest) -> Iterable[str]:
-        loc_stack_desc = self._get_loc_stack_desc(request.loc_stack)
+class LocatedRequestErrorRepresentor(BaseRequestErrorRepresentor[LocatedRequestT], Generic[LocatedRequestT]):
+    def get_request_context_notes(self, request: LocatedRequestT) -> Iterable[str]:
+        loc_stack_desc = format_loc_stack(request.loc_stack)
         yield f"Location: `{loc_stack_desc}`"
 
-    def _get_coercer_request_context_notes(self, request: CoercerRequest) -> Iterable[str]:
-        src_desc = self._get_loc_stack_desc(request.src)
-        dst_desc = self._get_loc_stack_desc(request.dst)
+
+class LinkingRequestErrorRepresentor(ErrorRepresentor[LinkingRequest]):
+    def get_request_context_notes(self, request: RequestT) -> Iterable[str]:
+        return ()
+
+    def get_provider_not_found_description(self, request: LinkingRequest) -> str:
+        dst_desc = format_loc_stack(request.destination)
+        return f"Cannot find paired field of `{dst_desc}` for linking"
+
+
+class CoercerRequestErrorRepresentor(BaseRequestErrorRepresentor[CoercerRequest]):
+    def get_request_context_notes(self, request: CoercerRequest) -> Iterable[str]:
+        src_desc = format_loc_stack(request.src)
+        dst_desc = format_loc_stack(request.dst)
         yield f"Linking: `{src_desc} => {dst_desc}`"
 
-    def get_request_context_notes(self, request: Request) -> Iterable[str]:
-        if isinstance(request, LocatedRequest):
-            yield from self._get_located_request_context_notes(request)
-        elif isinstance(request, CoercerRequest):
-            yield from self._get_coercer_request_context_notes(request)
+
+@with_module("adaptix")
+class ProviderNotFoundError(Exception):
+    def __init__(self, message: str):
+        self.message = message
+
+    def __str__(self):
+        return self.message
 
 
 class OperatingRetort(BaseRetort, Provider, ABC):
     """A retort that can operate as Retort but have no predefined providers and no high-level user interface"""
 
-    def apply_provider(self, mediator: Mediator, request: Request[T]) -> T:
-        return self._provide_from_recipe(request)
+    def _provide_from_recipe(self, request: Request[T]) -> T:
+        return self._create_mediator(request).provide_from_next()
+
+    def get_request_handlers(self) -> Sequence[Tuple[Type[Request], RequestChecker, RequestHandler]]:
+        def retort_request_handler(mediator, request):
+            return self._provide_from_recipe(request)
+
+        request_classes = {
+            request_cls
+            for provider in self._get_full_recipe()
+            for request_cls, checker, handler in provider.get_request_handlers()
+        }
+        return [
+            (request_class, AlwaysTrueRequestChecker(), retort_request_handler)
+            for request_class in request_classes
+        ]
 
     def _facade_provide(self, request: Request[T], *, error_message: str) -> T:
         try:
@@ -131,8 +165,95 @@ def _extract_demonstrative_exc(self, exc: AggregateCannotProvide) -> Optional[Ca
         copy_exception_dunders(source=exc, target=new_exc)
         return new_exc
 
-    def _create_recursion_resolver(self) -> RecursionResolver:
-        return MorphingRecursionResolver()
-
-    def _get_error_representor(self) -> ErrorRepresentor:
-        return BuiltinErrorRepresentor()
+    def _calculate_derived(self) -> None:
+        super()._calculate_derived()
+        self._request_cls_to_router = self._create_request_cls_to_router(self._full_recipe)
+        self._request_cls_to_error_representor = {
+            request_cls: self._create_error_representor(request_cls)
+            for request_cls in self._request_cls_to_router
+        }
+
+    def _create_request_cls_to_router(self, full_recipe: Sequence[Provider]) -> Mapping[Type[Request], RequestRouter]:
+        request_cls_to_checkers_and_handlers: DefaultDict[Type[Request], List[CheckerAndHandler]] = defaultdict(list)
+        for provider in full_recipe:
+            for request_cls, checker, handler in provider.get_request_handlers():
+                request_cls_to_checkers_and_handlers[request_cls].append((checker, handler))
+
+        return {
+            request_cls: self._create_router(request_cls, checkers_and_handlers)
+            for request_cls, checkers_and_handlers in request_cls_to_checkers_and_handlers.items()
+        }
+
+    def _create_router(
+        self,
+        request_cls: Type[RequestT],
+        checkers_and_handlers: Sequence[CheckerAndHandler],
+    ) -> RequestRouter[RequestT]:
+        if issubclass(request_cls, LocatedRequest):
+            return create_router_for_located_request(checkers_and_handlers)  # type: ignore[return-value]
+        return SimpleRouter(checkers_and_handlers)
+
+    def _create_error_representor(self, request_cls: Type[RequestT]) -> ErrorRepresentor[RequestT]:
+        if issubclass(request_cls, LoaderRequest):
+            return LocatedRequestErrorRepresentor("Cannot find loader")
+        if issubclass(request_cls, DumperRequest):
+            return LocatedRequestErrorRepresentor("Cannot find dumper")
+        if issubclass(request_cls, LocatedRequest):
+            return LocatedRequestErrorRepresentor(f"Can not satisfy {request_cls}")
+
+        if issubclass(request_cls, CoercerRequest):
+            return CoercerRequestErrorRepresentor("Cannot find coercer")  # type: ignore[return-value]
+        if issubclass(request_cls, LinkingRequest):
+            return LinkingRequestErrorRepresentor()  # type: ignore[return-value]
+
+        return BaseRequestErrorRepresentor(f"Can not satisfy {request_cls}")
+
+    def _create_recursion_resolver(self, request_cls: Type[RequestT]) -> Optional[RecursionResolver[RequestT, Any]]:
+        if issubclass(request_cls, (LoaderRequest, DumperRequest)):
+            return LocatedRequestCallableRecursionResolver()  # type: ignore[return-value]
+        return None
+
+    def _create_request_bus(
+        self,
+        request_cls: Type[RequestT],
+        router: RequestRouter[RequestT],
+        mediator_factory: Callable[[Request, int], Mediator],
+    ) -> RequestBus:
+        error_representor = self._request_cls_to_error_representor[request_cls]
+        recursion_resolver = self._create_recursion_resolver(request_cls)
+        if recursion_resolver is not None:
+            return RecursiveRequestBus(
+                router=router,
+                error_representor=error_representor,
+                mediator_factory=mediator_factory,
+                recursion_resolver=recursion_resolver,
+            )
+        return BasicRequestBus(
+            router=router,
+            error_representor=error_representor,
+            mediator_factory=mediator_factory,
+        )
+
+    def _create_no_request_bus_error_maker(self) -> Callable[[Request], CannotProvide]:
+        def no_request_bus_error_maker(request: Request) -> CannotProvide:
+            return CannotProvide(f"Can not satisfy {type(request)}")
+
+        return no_request_bus_error_maker
+
+    def _create_mediator(self, init_request: Request[T]) -> Mediator[T]:
+        request_buses: Mapping[Type[Request], RequestBus]
+        no_request_bus_error_maker = self._create_no_request_bus_error_maker()
+
+        def mediator_factory(request, search_offset):
+            return BuiltinMediator(
+                request_buses=request_buses,
+                request=request,
+                search_offset=search_offset,
+                no_request_bus_error_maker=no_request_bus_error_maker,
+            )
+
+        request_buses = {
+            request_cls: self._create_request_bus(request_cls, router, mediator_factory)
+            for request_cls, router in self._request_cls_to_router.items()
+        }
+        return mediator_factory(init_request, 0)
diff --git a/src/adaptix/_internal/retort/request_bus.py b/src/adaptix/_internal/retort/request_bus.py
new file mode 100644
index 00000000..a629cffb
--- /dev/null
+++ b/src/adaptix/_internal/retort/request_bus.py
@@ -0,0 +1,137 @@
+from abc import ABC, abstractmethod
+from typing import Any, Callable, Generic, Iterable, List, Optional, Tuple, TypeVar
+
+from ..provider.essential import (
+    AggregateCannotProvide,
+    CannotProvide,
+    DirectMediator,
+    Mediator,
+    Request,
+    RequestHandler,
+)
+from ..utils import add_note
+from .builtin_mediator import RequestBus
+
+RequestT = TypeVar("RequestT", bound=Request)
+ResponseT = TypeVar("ResponseT")
+
+
+class ErrorRepresentor(ABC, Generic[RequestT]):
+    @abstractmethod
+    def get_provider_not_found_description(self, request: RequestT) -> str:
+        ...
+
+    @abstractmethod
+    def get_request_context_notes(self, request: RequestT) -> Iterable[str]:
+        ...
+
+
+class RequestRouter(ABC, Generic[RequestT]):
+    """An offset of each element must belong to [0; max_offset)"""
+
+    @abstractmethod
+    def route_handler(
+        self,
+        mediator: DirectMediator,
+        request: RequestT,
+        search_offset: int,
+    ) -> Tuple[RequestHandler, int]:
+        """
+        :raises: StopIteration
+        """
+
+    @abstractmethod
+    def get_max_offset(self) -> int:
+        ...
+
+
+E = TypeVar("E", bound=Exception)
+
+
+class BasicRequestBus(RequestBus[RequestT, ResponseT], Generic[RequestT, ResponseT]):
+    __slots__ = ("_router", "_error_representor", "_mediator_factory")
+
+    def __init__(
+        self,
+        router: RequestRouter[RequestT],
+        error_representor: ErrorRepresentor[RequestT],
+        mediator_factory: Callable[[Request, int], Mediator],
+    ):
+        self._router = router
+        self._error_representor = error_representor
+        self._mediator_factory = mediator_factory
+
+    def send(self, request: RequestT) -> Any:
+        return self._send_inner(request, 0)
+
+    def send_chaining(self, request: RequestT, search_offset: int) -> Any:
+        return self._send_inner(request, search_offset)
+
+    def _send_inner(self, request: RequestT, search_offset: int) -> Any:
+        exceptions: List[CannotProvide] = []
+        next_offset = search_offset
+        mediator = self._mediator_factory(request, next_offset)
+        while True:
+            try:
+                handler, next_offset = self._router.route_handler(mediator, request, next_offset)
+            except StopIteration:
+                raise self._attach_request_context_notes(
+                    AggregateCannotProvide.make(
+                        self._error_representor.get_provider_not_found_description(request),
+                        exceptions,
+                        is_demonstrative=True,
+                    ),
+                    request,
+                ) from None
+            except CannotProvide:
+                raise RuntimeError("RequestChecker raises CannotProvide")
+
+            mediator = self._mediator_factory(request, next_offset)
+            try:
+                response = handler(mediator, request)
+            except CannotProvide as e:
+                if e.is_terminal:
+                    raise self._attach_request_context_notes(e, request)
+                exceptions.append(e)
+                continue
+
+            return response
+
+    def _attach_request_context_notes(self, exc: E, request: RequestT) -> E:
+        notes = self._error_representor.get_request_context_notes(request)
+        for note in notes:
+            add_note(exc, note)
+        return exc
+
+
+class RecursionResolver(ABC, Generic[RequestT, ResponseT]):
+    @abstractmethod
+    def track_recursion(self, request: RequestT) -> Optional[ResponseT]:
+        ...
+
+    @abstractmethod
+    def track_response(self, request: RequestT, response: ResponseT) -> None:
+        ...
+
+
+class RecursiveRequestBus(BasicRequestBus[RequestT, ResponseT], Generic[RequestT, ResponseT]):
+    __slots__ = (*BasicRequestBus.__slots__, "_recursion_resolver")
+
+    def __init__(
+        self,
+        router: RequestRouter[RequestT],
+        recursion_resolver: RecursionResolver[RequestT, ResponseT],
+        error_representor: ErrorRepresentor[RequestT],
+        mediator_factory: Callable[[Request, int], Mediator],
+    ):
+        super().__init__(router, error_representor, mediator_factory)
+        self._recursion_resolver = recursion_resolver
+
+    def send(self, request: RequestT) -> Any:
+        stub = self._recursion_resolver.track_recursion(request)
+        if stub is not None:
+            return stub
+
+        result = self._send_inner(request, 0)
+        self._recursion_resolver.track_response(request, result)
+        return result
diff --git a/src/adaptix/_internal/retort/routers.py b/src/adaptix/_internal/retort/routers.py
new file mode 100644
index 00000000..c4d3e9ce
--- /dev/null
+++ b/src/adaptix/_internal/retort/routers.py
@@ -0,0 +1,120 @@
+from itertools import islice
+from typing import Dict, List, Optional, Sequence, Tuple, TypeVar, Union
+
+from ..common import TypeHint
+from ..provider.essential import DirectMediator, Request, RequestChecker, RequestHandler
+from ..provider.loc_stack_filtering import ExactOriginLSC
+from ..provider.located_request import LocatedRequest, LocatedRequestChecker
+from ..type_tools import normalize_type
+from .request_bus import RequestRouter
+
+RequestT = TypeVar("RequestT", bound=Request)
+CheckerAndHandler = Tuple[RequestChecker, RequestHandler]
+
+
+class SimpleRouter(RequestRouter[RequestT]):
+    __slots__ = ("_checkers_and_handlers", )
+
+    def __init__(self, checkers_and_handlers: Sequence[CheckerAndHandler]):
+        self._checkers_and_handlers = checkers_and_handlers
+
+    def route_handler(
+        self,
+        mediator: DirectMediator,
+        request: RequestT,
+        search_offset: int,
+    ) -> Tuple[RequestHandler, int]:
+        for i, (checker, handler) in enumerate(
+            islice(self._checkers_and_handlers, search_offset, None),
+            start=search_offset,
+        ):
+            if checker.check_request(mediator, request):
+                return handler, i + 1
+        raise StopIteration
+
+    def get_max_offset(self) -> int:
+        return len(self._checkers_and_handlers)
+
+
+OriginToHandler = Dict[TypeHint, RequestHandler]
+LRRoutingItem = Union[CheckerAndHandler, OriginToHandler]
+
+
+class LocatedRequestRouter(RequestRouter[LocatedRequest]):
+    __slots__ = ("_items", )
+
+    def __init__(self, items: Sequence[Union[CheckerAndHandler, OriginToHandler]]):
+        self._items = items
+
+    def route_handler(
+        self,
+        mediator: DirectMediator,
+        request: LocatedRequest,
+        search_offset: int,
+    ) -> Tuple[RequestHandler, int]:
+        try:
+            origin = normalize_type(request.last_loc.type).origin
+        except ValueError:
+            origin = object()
+
+        for i, routing_item in enumerate(
+            islice(self._items, search_offset, None),
+            start=search_offset,
+        ):
+            if type(routing_item) is tuple:
+                if routing_item[0].check_request(mediator, request):
+                    return routing_item[1], i + 1
+            else:
+                handler = routing_item.get(origin)  # type: ignore[union-attr]
+                if handler is not None:
+                    return handler, i + 1
+        raise StopIteration
+
+    def get_max_offset(self) -> int:
+        return len(self._items)
+
+
+class ExactOriginCombiner:
+    def __init__(self) -> None:
+        self._combo: OriginToHandler = {}
+
+    def _stop_combo(self, checker_and_handler: Optional[CheckerAndHandler]) -> Sequence[LRRoutingItem]:
+        result: List[LRRoutingItem] = []
+        if self._combo:
+            if len(self._combo) == 1:
+                [(origin, handler)] = self._combo.items()
+                result.append((LocatedRequestChecker(ExactOriginLSC(origin)), handler))
+            else:
+                result.append(self._combo)
+                self._combo = {}
+
+        if checker_and_handler is not None:
+            result.append(checker_and_handler)
+        return result
+
+    def register_item(self, checker_and_handler: CheckerAndHandler) -> Sequence[LRRoutingItem]:
+        checker, handler = checker_and_handler
+        if isinstance(checker, LocatedRequestChecker) and isinstance(checker.loc_stack_checker, ExactOriginLSC):
+            origin = checker.loc_stack_checker.origin
+            if origin in self._combo:
+                return self._stop_combo(checker_and_handler)
+            self._combo[origin] = handler
+            return []
+
+        return self._stop_combo(checker_and_handler)
+
+    def finalize(self) -> Sequence[LRRoutingItem]:
+        return self._stop_combo(None)
+
+
+def create_router_for_located_request(
+    checkers_and_handlers: Sequence[CheckerAndHandler],
+) -> RequestRouter[LocatedRequest]:
+    items: List[Union[CheckerAndHandler, OriginToHandler]] = []
+
+    combiner = ExactOriginCombiner()
+    for checkers_and_handler in checkers_and_handlers:
+        items.extend(combiner.register_item(checkers_and_handler))
+    items.extend(combiner.finalize())
+
+    return LocatedRequestRouter(items)
diff --git a/src/adaptix/_internal/retort/routing.py b/src/adaptix/_internal/retort/routing.py
deleted file mode 100644
index cbeebd32..00000000
--- a/src/adaptix/_internal/retort/routing.py
+++ /dev/null
@@ -1,177 +0,0 @@
-from abc import ABC, abstractmethod
-from itertools import islice
-from typing import Callable, Dict, Iterable, List, Sequence, Set, Tuple, Type, TypeVar
-
-from ..common import TypeHint
-from ..provider.essential import CannotProvide, Mediator, Provider, Request
-from ..provider.loc_stack_filtering import ExactOriginLSC
-from ..provider.provider_wrapper import ProviderWithLSC, RequestClassDeterminedProvider
-from ..provider.request_cls import LocatedRequest, TypeHintLoc, try_normalize_type
-
-T = TypeVar("T")
-ProvideCallable = Callable[[Mediator, Request[T]], T]
-SearchResult = Tuple[ProvideCallable[T], int]
-
-
-class RecipeSearcher(ABC):
-    """An object that implements iterating over recipe list.
-
-    An offset of each element must belong to [0; max_offset)
-    """
-
-    @abstractmethod
-    def search_candidates(self, search_offset: int, request: Request[T]) -> Iterable[SearchResult[T]]:
-        ...
-
-    @abstractmethod
-    def get_max_offset(self) -> int:
-        ...
-
-    @abstractmethod
-    def clear_cache(self):
-        ...
-
-
-class RawRecipeSearcher(RecipeSearcher):
-    def __init__(self, recipe: Sequence[Provider]):
-        self.recipe = recipe
-
-    def search_candidates(self, search_offset: int, request: Request) -> Iterable[SearchResult]:
-        for i, provider in enumerate(
-            islice(self.recipe, search_offset, None),
-            start=search_offset,
-        ):
-            yield provider.apply_provider, i + 1
-
-    def clear_cache(self):
-        pass
-
-    def get_max_offset(self) -> int:
-        return len(self.recipe)
-
-
-class Combiner(ABC):
-    @abstractmethod
-    def add_element(self, provider: Provider) -> bool:
-        ...
-
-    @abstractmethod
-    def combine_elements(self) -> Sequence[Provider]:
-        ...
-
-    @abstractmethod
-    def has_elements(self) -> bool:
-        ...
-
-
-class ExactOriginCombiner(Combiner):
-    def __init__(self) -> None:
-        self._combo: List[Tuple[ExactOriginLSC, Provider]] = []
-        self._origins: Set[TypeHint] = set()
-
-    def add_element(self, provider: Provider) -> bool:
-        if not isinstance(provider, ProviderWithLSC):
-            return False
-        loc_stack_checker = provider.get_loc_stack_checker()
-        if not isinstance(loc_stack_checker, ExactOriginLSC):
-            return False
-        if loc_stack_checker.origin in self._origins:
-            return False
-
-        self._combo.append((loc_stack_checker, provider))
-        self._origins.add(loc_stack_checker.origin)
-        return True
-
-    def combine_elements(self) -> Sequence[Provider]:
-        if len(self._combo) == 1:
-            element = self._combo[0][1]
-            self._combo.clear()
-            self._origins.clear()
-            return [element]
-
-        merged_provider = ExactOriginMergedProvider(self._combo)
-        self._combo.clear()
-        self._origins.clear()
-        return [merged_provider]
-
-    def has_elements(self) -> bool:
-        return bool(self._combo)
-
-
-class ExactOriginMergedProvider(Provider):
-    def __init__(self, origins_to_providers: Sequence[Tuple[ExactOriginLSC, Provider]]):
-        self.origin_to_provider = {
-            loc_stack_checker.origin: provider
-            for loc_stack_checker, provider in reversed(origins_to_providers)
-        }
-
-    def apply_provider(self, mediator: Mediator[T], request: Request[T]) -> T:
-        if not isinstance(request, LocatedRequest):
-            raise CannotProvide(f"Request must be instance of {LocatedRequest}")
-
-        loc = request.last_loc.cast_or_raise(
-            TypeHintLoc,
-            lambda: CannotProvide(f"Request location must be instance of {TypeHintLoc}"),
-        )
-        norm = try_normalize_type(loc.type)
-        try:
-            provider = self.origin_to_provider[norm.origin]
-        except KeyError:
-            raise CannotProvide from None
-
-        return provider.apply_provider(mediator, request)
-
-
-class IntrospectingRecipeSearcher(RecipeSearcher):
-    def __init__(self, recipe: Sequence[Provider]):
-        self._recipe = recipe
-        self._cls_to_recipe: Dict[Type[Request], Sequence[Provider]] = {}
-
-    def search_candidates(self, search_offset: int, request: Request) -> Iterable[SearchResult]:
-        request_cls = type(request)
-        try:
-            sub_recipe = self._cls_to_recipe[request_cls]
-        except KeyError:
-            sub_recipe = self._collect_candidates(request_cls, self._recipe)
-            self._cls_to_recipe[request_cls] = sub_recipe
-
-        for i, provider in enumerate(
-            islice(sub_recipe, search_offset, None),
-            start=search_offset,
-        ):
-            yield provider.apply_provider, i + 1
-
-    def _create_combiner(self) -> Combiner:
-        return ExactOriginCombiner()
-
-    def _merge_providers(self, recipe: Sequence[Provider]) -> Sequence[Provider]:
-        combiner = self._create_combiner()
-
-        result: List[Provider] = []
-        for provider in recipe:
-            is_added = combiner.add_element(provider)
-            if not is_added:
-                if combiner.has_elements():
-                    result.extend(combiner.combine_elements())
-                result.append(provider)
-
-        if combiner.has_elements():
-            result.extend(combiner.combine_elements())
-        return result
-
-    def _collect_candidates(self, request_cls: Type[Request], recipe: Sequence[Provider]) -> Sequence[Provider]:
-        candidates = [
-            provider
-            for provider in recipe
-            if (
-                not isinstance(provider, RequestClassDeterminedProvider)
-                or provider.maybe_can_process_request_cls(request_cls)
-            )
-        ]
-        return self._merge_providers(candidates)
-
-    def clear_cache(self):
-        self._cls_to_recipe = {}
-
-    def get_max_offset(self) -> int:
-        return len(self._recipe)
diff --git a/src/adaptix/_internal/type_tools/generic_resolver.py b/src/adaptix/_internal/type_tools/generic_resolver.py
index 90233a31..466fc095 100644
--- a/src/adaptix/_internal/type_tools/generic_resolver.py
+++ b/src/adaptix/_internal/type_tools/generic_resolver.py
@@ -5,8 +5,8 @@
 
 from ..common import TypeHint
 from ..feature_requirement import HAS_TV_TUPLE, HAS_UNPACK
-from . import get_generic_args
 from .basic_utils import get_type_vars, get_type_vars_of_parametrized, is_generic, is_parametrized, strip_alias
+from .fundamentals import get_generic_args
 from .implicit_params import fill_implicit_params
 from .normalize_type import normalize_type
 


Test patch:
diff --git a/.github/workflows/lint_and_test.yml b/.github/workflows/lint_and_test.yml
index 4d0e414d..8bf96224 100644
--- a/.github/workflows/lint_and_test.yml
+++ b/.github/workflows/lint_and_test.yml
@@ -56,11 +56,12 @@ jobs:
           - { setup: '3.9',      tox: 'py39',    cov: true  }
           - { setup: '3.10',     tox: 'py310',   cov: true  }
           - { setup: '3.11',     tox: 'py311',   cov: true  }
-          - { setup: '3.12',     tox: 'py312',   cov: true  }
+          - { setup: '3.12.3',   tox: 'py312',   cov: true  }  # (1)
           - { setup: 'pypy3.8',  tox: 'pypy38',  cov: false }
           - { setup: 'pypy3.9',  tox: 'pypy39',  cov: false }
           - { setup: 'pypy3.10', tox: 'pypy310', cov: false }
 
+        # (1): 3.12.4 contains backwards-incompatible changes of ForwardRef._evaluate()
         os: ['ubuntu-latest']
 
     steps:
diff --git a/tests/tests_helpers/tests_helpers/misc.py b/tests/tests_helpers/tests_helpers/misc.py
index 96d9a9b0..64b2b00a 100644
--- a/tests/tests_helpers/tests_helpers/misc.py
+++ b/tests/tests_helpers/tests_helpers/misc.py
@@ -7,15 +7,17 @@
 from dataclasses import dataclass, is_dataclass
 from pathlib import Path
 from types import ModuleType, SimpleNamespace
-from typing import Any, Callable, Dict, Generator, List, Optional, Reversible, Type, TypeVar, Union
+from typing import Any, Callable, Dict, Generator, List, Optional, Reversible, Sequence, Tuple, Type, TypeVar, Union
 from uuid import uuid4
 
 import pytest
 
-from adaptix import CannotProvide, DebugTrail, Mediator, Provider, ProviderNotFoundError, Request
+from adaptix import CannotProvide, DebugTrail, Provider, ProviderNotFoundError, Request, Retort
 from adaptix._internal.compat import CompatExceptionGroup
 from adaptix._internal.feature_requirement import DistributionVersionRequirement, Requirement
 from adaptix._internal.morphing.model.basic_gen import CodeGenAccumulator
+from adaptix._internal.provider.essential import Mediator, RequestChecker, RequestHandler
+from adaptix._internal.retort.operating_retort import OperatingRetort
 from adaptix._internal.struct_trail import TrailElement, extend_trail, render_trail_as_note
 from adaptix._internal.type_tools import is_parametrized
 from adaptix._internal.utils import add_note
@@ -129,8 +131,8 @@ def source_namespace(self):
 class PlaceholderProvider(Provider):
     value: int
 
-    def apply_provider(self, mediator: Mediator, request: Request[T]) -> T:
-        raise CannotProvide
+    def get_request_handlers(self) -> Sequence[Tuple[Type[Request], RequestChecker, RequestHandler]]:
+        return []
 
 
 def full_match(string_to_match: str) -> str:
@@ -231,3 +233,14 @@ def _evaluate(self) -> bool:
     @property
     def fail_reason(self) -> str:
         return self._fail_reason
+
+
+class StubRequest(Request):
+    pass
+
+
+stub_retort = Retort()
+
+
+def create_mediator(retort: OperatingRetort = stub_retort) -> Mediator:
+    return retort._create_mediator(StubRequest())
diff --git a/tests/unit/morphing/model/test_dumper_provider.py b/tests/unit/morphing/model/test_dumper_provider.py
index 7b1d6023..0936f991 100644
--- a/tests/unit/morphing/model/test_dumper_provider.py
+++ b/tests/unit/morphing/model/test_dumper_provider.py
@@ -32,8 +32,8 @@
 )
 from adaptix._internal.morphing.model.dumper_provider import ModelDumperProvider
 from adaptix._internal.morphing.request_cls import DumperRequest
-from adaptix._internal.provider.provider_template import ValueProvider
 from adaptix._internal.provider.shape_provider import OutputShapeRequest
+from adaptix._internal.provider.value_provider import ValueProvider
 from adaptix._internal.struct_trail import Attr, TrailElement, TrailElementMarker
 from adaptix._internal.utils import SingletonMeta
 
diff --git a/tests/unit/morphing/model/test_loader_provider.py b/tests/unit/morphing/model/test_loader_provider.py
index e9a19624..e55a954a 100644
--- a/tests/unit/morphing/model/test_loader_provider.py
+++ b/tests/unit/morphing/model/test_loader_provider.py
@@ -34,8 +34,8 @@
     InputNameLayoutRequest,
 )
 from adaptix._internal.morphing.request_cls import LoaderRequest
-from adaptix._internal.provider.provider_template import ValueProvider
 from adaptix._internal.provider.shape_provider import InputShapeRequest
+from adaptix._internal.provider.value_provider import ValueProvider
 from adaptix.load_error import (
     ExtraFieldsLoadError,
     ExtraItemsLoadError,
diff --git a/tests/unit/morphing/name_layout/test_provider.py b/tests/unit/morphing/name_layout/test_provider.py
index 69ff1a99..2fdf31e4 100644
--- a/tests/unit/morphing/name_layout/test_provider.py
+++ b/tests/unit/morphing/name_layout/test_provider.py
@@ -54,10 +54,10 @@
     OutputNameLayoutRequest,
 )
 from adaptix._internal.morphing.request_cls import DumperRequest, LoaderRequest
-from adaptix._internal.provider.loc_stack_filtering import P
-from adaptix._internal.provider.provider_template import ValueProvider
-from adaptix._internal.provider.request_cls import LocStack, TypeHintLoc
+from adaptix._internal.provider.loc_stack_filtering import LocStack, P
+from adaptix._internal.provider.location import TypeHintLoc
 from adaptix._internal.provider.shape_provider import InputShapeRequest, OutputShapeRequest
+from adaptix._internal.provider.value_provider import ValueProvider
 
 
 @dataclass
diff --git a/tests/unit/provider/shape_provider/local_helpers.py b/tests/unit/provider/shape_provider/local_helpers.py
index 7a68c768..35f843c0 100644
--- a/tests/unit/provider/shape_provider/local_helpers.py
+++ b/tests/unit/provider/shape_provider/local_helpers.py
@@ -1,7 +1,10 @@
 from typing import Mapping, Optional
 
-from adaptix import Retort, TypeHint
-from adaptix._internal.provider.request_cls import LocStack, TypeHintLoc
+from tests_helpers.misc import create_mediator
+
+from adaptix import TypeHint
+from adaptix._internal.provider.loc_stack_filtering import LocStack
+from adaptix._internal.provider.location import TypeHintLoc
 from adaptix._internal.provider.shape_provider import (
     InputShapeRequest,
     OutputShapeRequest,
@@ -16,21 +19,17 @@ def assert_distinct_fields_types(
     input: Mapping[str, TypeHint],  # noqa: A002
     output: Mapping[str, TypeHint],
 ) -> None:
-    retort = Retort()
-    mediator = retort._create_mediator()
-
     input_shape = provide_generic_resolved_shape(
-        mediator,
+        create_mediator(),
         InputShapeRequest(loc_stack=LocStack(TypeHintLoc(type=tp))),
     )
-    input_field_types = {field.id: field.type for field in input_shape.fields}
-    assert input_field_types == input
-
     output_shape = provide_generic_resolved_shape(
-        mediator,
+        create_mediator(),
         OutputShapeRequest(loc_stack=LocStack(TypeHintLoc(type=tp))),
     )
+    input_field_types = {field.id: field.type for field in input_shape.fields}
     output_field_types = {field.id: field.type for field in output_shape.fields}
+    assert input_field_types == input
     assert output_field_types == output
 
 
diff --git a/tests/unit/provider/shape_provider/test_generic_resolving.py b/tests/unit/provider/shape_provider/test_generic_resolving.py
index 92f9c120..78811055 100644
--- a/tests/unit/provider/shape_provider/test_generic_resolving.py
+++ b/tests/unit/provider/shape_provider/test_generic_resolving.py
@@ -3,9 +3,10 @@
 
 import pytest
 from tests_helpers import ModelSpec, cond_list, exclude_model_spec, load_namespace_keeping_module, requires
+from tests_helpers.misc import create_mediator
 from tests_helpers.model_spec import only_generic_models, with_model_spec_requirement
 
-from adaptix import CannotProvide, Retort
+from adaptix import CannotProvide
 from adaptix._internal.feature_requirement import (
     HAS_PY_39,
     HAS_PY_310,
@@ -17,7 +18,8 @@
     IS_PYPY,
     DistributionVersionRequirement,
 )
-from adaptix._internal.provider.request_cls import LocStack, TypeHintLoc
+from adaptix._internal.provider.loc_stack_filtering import LocStack
+from adaptix._internal.provider.location import TypeHintLoc
 from adaptix._internal.provider.shape_provider import (
     InputShapeRequest,
     OutputShapeRequest,
@@ -256,40 +258,33 @@ class Child(Parent1[int, bool], Parent2[str, bytes], Generic[T7]):
     )
 
 
-# TODO: fix it  # noqa: TD003
-skip_if_pypy_39_or_310 = pytest.mark.skipif(
-    IS_PYPY and (HAS_PY_39 or HAS_PY_310),
-    reason="At this python version and implementation list has __init__ that allow to generate Shape",
-)
-
-
 @pytest.mark.parametrize(
     "tp",
     [
         int,
-        pytest.param(list, id="list", marks=skip_if_pypy_39_or_310),
-        pytest.param(List, id="List", marks=skip_if_pypy_39_or_310),
-        pytest.param(List[T], id="List[T]", marks=skip_if_pypy_39_or_310),
-        pytest.param(List[int], id="List[int]", marks=skip_if_pypy_39_or_310),
+        list,
+        List,
+        List[T],
+        List[int],
         *cond_list(
             HAS_STD_CLASSES_GENERICS,
-            lambda: [pytest.param(list[T], id="list[T]", marks=skip_if_pypy_39_or_310)],
+            lambda: [list[T]],
         ),
     ],
 )
 def test_not_a_model(tp):
-    retort = Retort()
-    mediator = retort._create_mediator()
-
-    with pytest.raises(CannotProvide):
-        provide_generic_resolved_shape(
-            mediator,
-            InputShapeRequest(loc_stack=LocStack(TypeHintLoc(type=tp))),
-        )
+    # TODO: fix it  # noqa: TD003
+    # At this python versions and implementation list has __init__ that allow to generate Shape
+    if not (IS_PYPY and (HAS_PY_39 or HAS_PY_310)):
+        with pytest.raises(CannotProvide):
+            provide_generic_resolved_shape(
+                create_mediator(),
+                InputShapeRequest(loc_stack=LocStack(TypeHintLoc(type=tp))),
+            )
 
     with pytest.raises(CannotProvide):
         provide_generic_resolved_shape(
-            mediator,
+            create_mediator(),
             OutputShapeRequest(loc_stack=LocStack(TypeHintLoc(type=tp))),
         )
 
diff --git a/tests/unit/provider/test_loc_stack_filtering.py b/tests/unit/provider/test_loc_stack_filtering.py
index 67c60cdf..4cb201ca 100644
--- a/tests/unit/provider/test_loc_stack_filtering.py
+++ b/tests/unit/provider/test_loc_stack_filtering.py
@@ -7,6 +7,7 @@
 
 import pytest
 from tests_helpers import cond_list, full_match
+from tests_helpers.misc import create_mediator
 
 from adaptix import Chain, P, Retort, loader
 from adaptix._internal.common import TypeHint
@@ -15,12 +16,12 @@
 from adaptix._internal.provider.loc_stack_filtering import (
     ExactOriginLSC,
     ExactTypeLSC,
+    LocStack,
     LocStackEndChecker,
     OriginSubclassLSC,
     create_loc_stack_checker,
 )
 from adaptix._internal.provider.location import FieldLoc, GenericParamLoc, TypeHintLoc
-from adaptix._internal.provider.request_cls import LocStack
 from adaptix._internal.type_tools import normalize_type
 
 
@@ -56,10 +57,6 @@ def param_result(*values, result=None, raises=None, exact_match=None, match=None
     return pytest.param(*values, result, context, id=id)
 
 
-def create_mediator():
-    return Retort()._create_mediator()
-
-
 @pytest.mark.parametrize(
     ["loc_stack", "result", "context"],
     [
diff --git a/tests/unit/provider/test_methods_provider.py b/tests/unit/provider/test_methods_provider.py
new file mode 100644
index 00000000..5cbb3ca0
--- /dev/null
+++ b/tests/unit/provider/test_methods_provider.py
@@ -0,0 +1,115 @@
+from abc import ABC, abstractmethod
+
+import pytest
+
+from adaptix import Mediator, Request
+from adaptix._internal.provider.methods_provider import MethodsProvider, method_handler
+from adaptix._internal.provider.request_checkers import AlwaysTrueRequestChecker
+
+
+class SampleRequest(Request):
+    pass
+
+
+def test_simple():
+    class TestSimple1(MethodsProvider):
+        @method_handler
+        def provide_sample(self, mediator: Mediator, request: SampleRequest):
+            pass
+
+    instance = TestSimple1()
+    assert (
+        instance.get_request_handlers() == [
+            (SampleRequest, AlwaysTrueRequestChecker(), instance.provide_sample),
+        ]
+    )
+
+
+def test_abstract_method():
+    class Base(MethodsProvider, ABC):
+        @abstractmethod
+        @method_handler
+        def provide_sample(self, mediator: Mediator, request: SampleRequest):
+            pass
+
+    class Child(Base):
+        def provide_sample(self, mediator: Mediator, request: SampleRequest):
+            pass
+
+    instance = Child()
+    assert (
+        instance.get_request_handlers() == [
+            (SampleRequest, AlwaysTrueRequestChecker(), instance.provide_sample),
+        ]
+    )
+
+
+def test_error_raising_with_one_class():
+    with pytest.raises(TypeError):
+        class BadDecoratorArg(MethodsProvider):
+            @method_handler
+            def provide(self, mediator: Mediator, request: int):
+                pass
+
+    with pytest.raises(TypeError):
+        class SeveralSPA(MethodsProvider):
+            @method_handler
+            def provide_one(self, mediator: Mediator, request: Request):
+                pass
+
+            @method_handler
+            def provide_two(self, mediator: Mediator, request: Request):
+                pass
+
+
+class Base1(MethodsProvider):
+    @method_handler
+    def provide_one(self, mediator: Mediator, request: Request):
+        pass
+
+
+class Base2(MethodsProvider):
+    @method_handler
+    def provide_two(self, mediator: Mediator, request: Request):
+        pass
+
+
+def test_inheritance_redefine_spa():
+    with pytest.raises(TypeError):
+        class RedefineSPAChild(Base1):
+            @method_handler
+            def provide_one(self, mediator: Mediator, request: Request):
+                pass
+
+
+def test_inheritance_several_spa():
+    with pytest.raises(TypeError):
+        class SeveralSPAChild(Base1):
+            @method_handler
+            def provide_two(self, mediator: Mediator, request: Request):
+                pass
+
+    with pytest.raises(TypeError):
+        class Child12(Base1, Base2):
+            pass
+
+    with pytest.raises(TypeError):
+        class Child21(Base2, Base1):
+            pass
+
+
+def test_inheritance_several_rc():
+    class Base3(MethodsProvider):
+        @method_handler
+        def provide_one(self, mediator: Mediator, request: SampleRequest):
+            pass
+
+    with pytest.raises(TypeError):
+        class Child13(Base1, Base3):
+            pass
+
+    with pytest.raises(TypeError):
+        class Child1(Base1):
+            @method_handler
+            def provide_one(self, mediator: Mediator, request: SampleRequest):
+                pass
diff --git a/tests/unit/provider/test_overlay_schema.py b/tests/unit/provider/test_overlay_schema.py
index 17a919fa..8dde5829 100644
--- a/tests/unit/provider/test_overlay_schema.py
+++ b/tests/unit/provider/test_overlay_schema.py
@@ -6,9 +6,10 @@
 
 from adaptix import AdornedRetort, Chain, Mediator, Omittable, Omitted, Provider, Request, bound
 from adaptix._internal.common import VarTuple
+from adaptix._internal.provider.loc_stack_filtering import LocStack
+from adaptix._internal.provider.location import TypeHintLoc
+from adaptix._internal.provider.methods_provider import MethodsProvider, method_handler
 from adaptix._internal.provider.overlay_schema import Overlay, OverlayProvider, Schema, provide_schema
-from adaptix._internal.provider.request_cls import LocStack, TypeHintLoc
-from adaptix._internal.provider.static_provider import StaticProvider, static_provision_action
 
 
 @dataclass(frozen=True)
@@ -31,11 +32,11 @@ class SampleRequest(Request):
     pass
 
 
-class SampleRequestProvider(StaticProvider):
+class SampleRequestProvider(MethodsProvider):
     def __init__(self, provide_action: Callable[[Mediator], MySchema]):
         self.provide_action = provide_action
 
-    @static_provision_action
+    @method_handler
     def _provide_overlay(self, mediator: Mediator, request: SampleRequest):
         return self.provide_action(mediator)
 
diff --git a/tests/unit/provider/test_static_provider.py b/tests/unit/provider/test_static_provider.py
deleted file mode 100644
index a57a7920..00000000
--- a/tests/unit/provider/test_static_provider.py
+++ /dev/null
@@ -1,170 +0,0 @@
-from abc import ABC, abstractmethod
-
-import pytest
-from tests_helpers import full_match
-
-from adaptix import Mediator, Request
-from adaptix._internal.provider.static_provider import RequestDispatcher, StaticProvider, static_provision_action
-
-
-class SampleRequest(Request):
-    pass
-
-
-def test_simple():
-    class TestSimple1(StaticProvider):
-        @static_provision_action(SampleRequest)
-        def _provide_sample(self, mediator: Mediator, request: SampleRequest):
-            pass
-
-    assert (
-        TestSimple1._sp_cls_request_dispatcher
-        ==
-        RequestDispatcher({SampleRequest: "_provide_sample"})
-    )
-
-    class TestSimple2(StaticProvider):
-        @static_provision_action()
-        def _provide_sample(self, mediator: Mediator, request: SampleRequest):
-            pass
-
-    assert (
-        TestSimple2._sp_cls_request_dispatcher
-        ==
-        RequestDispatcher({SampleRequest: "_provide_sample"})
-    )
-
-    class TestSimple3(StaticProvider):
-        @static_provision_action
-        def _provide_sample(self, mediator: Mediator, request: SampleRequest):
-            pass
-
-    assert (
-        TestSimple3._sp_cls_request_dispatcher
-        ==
-        RequestDispatcher({SampleRequest: "_provide_sample"})
-    )
-
-    class TestSimple4(StaticProvider):
-        @static_provision_action(SampleRequest)
-        def _provide_sample(self, mediator: Mediator, request):
-            pass
-
-    assert (
-        TestSimple4._sp_cls_request_dispatcher
-        ==
-        RequestDispatcher({SampleRequest: "_provide_sample"})
-    )
-
-    class NotASampleRequest(Request):
-        pass
-
-    class TestSimple5(StaticProvider):
-        @static_provision_action(SampleRequest)
-        def _provide_sample(self, mediator: Mediator, request: NotASampleRequest):
-            pass
-
-    assert (
-        TestSimple5._sp_cls_request_dispatcher
-        ==
-        RequestDispatcher({SampleRequest: "_provide_sample"})
-    )
-
-
-def test_abstract_method():
-    class Base(StaticProvider, ABC):
-        @abstractmethod
-        @static_provision_action
-        def _provide_sample(self, mediator: Mediator, request: SampleRequest):
-            pass
-
-    class Child(Base):
-        def _provide_sample(self, mediator: Mediator, request: SampleRequest):
-            pass
-
-    assert (
-        Child._sp_cls_request_dispatcher
-        ==
-        RequestDispatcher({SampleRequest: "_provide_sample"})
-    )
-
-
-def test_error_raising_with_one_class():
-    with pytest.raises(TypeError):
-        class BadDecoratorArg(StaticProvider):
-            @static_provision_action
-            def _provide(self, mediator: Mediator, request: int):
-                pass
-
-    with pytest.raises(
-        ValueError,
-        match=full_match("@static_provision_action decorator cannot be applied twice"),
-    ):
-        class DoubleDecoration(StaticProvider):
-            @static_provision_action
-            @static_provision_action
-            def _provide(self, mediator: Mediator, request: Request):
-                pass
-
-    with pytest.raises(TypeError):
-        class SeveralSPA(StaticProvider):
-            @static_provision_action
-            def _provide_one(self, mediator: Mediator, request: Request):
-                pass
-
-            @static_provision_action
-            def _provide_two(self, mediator: Mediator, request: Request):
-                pass
-
-
-class Base1(StaticProvider):
-    @static_provision_action
-    def _provide_one(self, mediator: Mediator, request: Request):
-        pass
-
-
-class Base2(StaticProvider):
-    @static_provision_action
-    def _provide_two(self, mediator: Mediator, request: Request):
-        pass
-
-
-def test_inheritance_redefine_spa():
-    with pytest.raises(TypeError):
-        class RedefineSPAChild(Base1):
-            @static_provision_action
-            def _provide_one(self, mediator: Mediator, request: Request):
-                pass
-
-
-def test_inheritance_several_spa():
-    with pytest.raises(TypeError):
-        class SeveralSPAChild(Base1):
-            @static_provision_action
-            def _provide_two(self, mediator: Mediator, request: Request):
-                pass
-
-    with pytest.raises(TypeError):
-        class Child12(Base1, Base2):
-            pass
-
-    with pytest.raises(TypeError):
-        class Child21(Base2, Base1):
-            pass
-
-
-def test_inheritance_several_rc():
-    class Base3(StaticProvider):
-        @static_provision_action
-        def _provide_one(self, mediator: Mediator, request: SampleRequest):
-            pass
-
-    with pytest.raises(TypeError):
-        class Child13(Base1, Base3):
-            pass
-
-    with pytest.raises(TypeError):
-        class Child1(Base1):
-            @static_provision_action
-            def _provide_one(self, mediator: Mediator, request: SampleRequest):
-                pass