whereas in OpenGSN's version _trustedForwarder isn't immutable:
... and has the function _setTrustedForwarder to update it:
I think it'd be better to remove immutable so that we can have a function to set a new trusted forwarder
Why should a trusted forwarder be hardcoded? Like everything else in life, something or someone that was once trusted may one day no longer be trustworthy, so it should be possible to adapt to such changes.
So without immutable , I'd even make a public setTrustedForwarder function in my contract (and secure it with modifier onlyRole(DEFAULT_ADMIN_ROLE)).
Making _trustedForwarder immutable means the address of the trusted forwarder is hardcoded at the time of the deployment of the ERC20 token contract. After some years and thousands of people holding and using the token, say that forwarder gets hacked or no longer works for whatever reason, then what would be the outcome with the token being permanently stuck with the address of a defunct forwarder?
If the token contact is upgradeable then upgrade it, setting a new valid value for forwarder address.
If it's not upgradeable then maybe meta transactions woud have to be disabled for the token.
Try to switch to OpenGSN's implementation of ERC-2771, as they probably have more real-world experience with meta transactions than OpenZeppelin. I think making addresses of external entities immutable is not suitable for real-world applications, because external entities can change without your control in the real world.
It's funny how the variable is called "trusted" forwarder. In an ideal world, it'd be OK to make the address of a forwarder that is forever-trustworthy immutable.
But in the real world, most things cannot or should not be "forever-trustworthy". So the variable should just be called "forwarder" instead of "trustedForwarder", and the value should be updateable at any time in the future by the contract admin (which would be more trustworthy than an external entity).