Chain Java decorators deliberately, because wrapper order changes behavior, visibility, performance, and failure semantics.
Decorator becomes especially useful when multiple wrappers can be stacked. It also becomes easier to misuse.
flowchart LR
A["Client"] --> B["Caching decorator"]
B --> C["Metrics decorator"]
C --> D["Authorization decorator"]
D --> E["Base service"]
The order of decorators is not cosmetic.
If authorization wraps metrics, denied requests may not be counted the same way. If caching wraps authorization, cached results may be reachable through the wrong path. If retry wraps logging, the logs may show attempts differently than expected.
That means the chain is part of the design, not an incidental assembly detail.
Each decorator should answer one question clearly:
If those answers are fuzzy, chain behavior becomes hard to predict.
Unit tests for individual decorators are useful, but chain-level tests matter too because order is often where the bugs appear.