The full picture
Only SnowballToken and FeeRouter talk to each other and to PancakeSwap. SnowballRegistry is deliberately standalone: it's a listing directory, not a financial pipe (more on why in Fee & Burn Mechanics).
Why FeeRouter is a separate contract, not built into the token
Keeping the swap/LP logic out of SnowballToken keeps the token's transfer hook trivial: it either takes a fee and forwards it, or it doesn't. No external calls, no swap logic, no reentrancy surface inside _update. All the complexity, and all the external calls to PancakeSwap, live in FeeRouter, guarded separately by a reentrancy lock and a caller gate. It also means FeeRouter can be configured to burn fees from other registered tokens too, without touching SnowballToken at all. See Fee & Burn Mechanics.
Where it lives
contracts/src/SnowballToken.solcontracts/src/FeeRouter.solcontracts/src/SnowballRegistry.solcontracts/src/interfaces/IPancakeRouter02.sol,IPancakeFactory.solcontracts/script/Deploy.s.sol