Context
Hello
I'm trying to decide what would be the best path for implementing an upgradable Dapp. My constraints are;
- I'm developing for a permissioned network where gas is no issue
- I will be dealing with HUGE amounts of records in my smart contract's storage
- I will be using complex structs that can change over time
- I want to be able to release quick bug fixes
Resources I read Before Posting
- Mutable storage structs and mappings by mappings to strings or byte arrays?
- https://blog.colony.io/writing-upgradeable-contracts-in-solidity-6743f0eecc88/
- Proxy Patterns - Implementation storage solutions for non-conflicting upgrades, is there a best?
- https://blog.openzeppelin.com/the-state-of-smart-contract-upgrades#eternal-storage
- https://blog.openzeppelin.com/smart-contract-upgradeability-using-eternal-storage
- https://docs.openzeppelin.com/upgrades-plugins/proxies#unstructured-storage-proxies
- https://scsfg.io/developers/system-design/#data-separation-patterns
Question
I'm aware of Proxies and unstructured storage patterns. However, keeping the storage layout in the back of my head all the time when I'm developing my dapp seems something that I don't want to do. And it really looks weird to have storage contracts like in order to avoid collisions between implementation contracts;
contract V1Storage {}
contract V2Storage extends V1Storage {}
contract V3Storage extends V2Storage {}
....
So what seems like a good idea is that (given I'm building for a permissioned network) to have a separate storage contract that is similar to the one in https://blog.colony.io/writing-upgradeable-contracts-in-solidity-6743f0eecc88/ but with additional fields like below to handle my structs:
mapping(bytes32 => mapping(bytes32 => bytes))
This way the structs are stored as byte arrays and the implementation contract could use its own logic to decode the byte array given the version of the respective struct. Thoughts?