`PATCH /expenses/{id}/approve` has two rules: only users with role `MANAGER` may call it, and a manager may not approve their own expense. Why can the first rule be enforced with a method-level annotation before the method body runs, while the second structurally cannot — and what does that force about where the second check lives in the code?
The role check is answerable from the caller's identity alone — it's already fully known the instant the request is authenticated, before any argument or any row has been touched, so a proxy sitting in front of the method (@PreAuthorize("hasRole('MANAGER')")) can decide it and reject the call before the real body ever executes. The self-approval rule needs a fact that doesn't exist yet at that point: who submitted this specific expense — which is only knowable once that row has been loaded. There's no proxy that can intercept the call and answer that without doing the fetch the method itself was going to do, so the check has no choice but to live inline in the service, right beside the query that has both the caller and the loaded resource in scope at the same time.