Keep things as simple as possible
Most smart contracts rely on keeping things as simple as possible.
In that spirit, instantiating new contracts for each client is recommended as it reduces the required code / logic in the contract, as well as limits an attacker's surface to said contract.
And you also don't keep all your eggs ( ETH / Tokens / Value ) in one basket, which as a result will slow down contract drains if they do happen.
When talking about "crowd funding" you need different contracts meant to store "erc20 tokens" called "token trackers", so you're already doing it.
You also need to keep in mind that the logic that fits in one contract has to fit in the 8 mil gas limit, which for such a contract can be quite small.
Real world example:
Maybe we're paranoid but the Crowd Funding DAO, we've been developing does take this to the extreme by creating Personal Funding Vaults for each contributing address to each separate crowd fund.
It's a smart contract that hardcodes 2 output and 2 controller addresses:
See this github link for sourcecode
- One - The Vault Owner ( the wallet address that sent the eth )
- Two - The Team Multi signature address
- One - The Funding Manager Smart Contract, that can request funds which then get sent to the "team" address, ( can happen only if all logic in the contract works as expected each and every time )
- Two - The Vault Owner - Can request their funds back in case they do a "cash back"
Something like this would not really be reliable or even safe if you would stack multiple projects / addresses into one instance.
These vaults do cost a lot, but insure that no one else but the team or the investor can ever get the "value" out of it.
Hope this helps.