As indicated in my answer to this question, I'm a big believer in total data discovery. If you have that property, then you're at least half way to being able to migrate that data to a new structure. But it's not always possible.
You're not going to get away without some logic. Even in modular storage contracts, you're still needing permission and setter logic. Getter logic too if you're dealing with complex types.
So first strategy is simple data types to begin with and simple permissions such as a contract owner.
The simplest extensible database could be a mapping of bytes
.
mapping (bytes32 => bytes) public simpleDB;
function set(bytes32 key, bytes data)
onlyOwner
{
simpleDB[key] = data;
}
Bytes is chosen here because any data structure can be rendered into and interpreted from an array of bytes.
Byte streams to and from contracts themselves use RLP encoding to render an object into a byte array. I haven't seen Solidity code to implement this, but if there's none out there, I'll give it a go when I have the chance.
The problem with this is that if other contracts want to interact with with such a store, there is no way of reading values without unacceptable overheads. This is not so much a problem for a front end but it does weaken the ability of inter-contract applications. Also, if the data is only being used by the front-end, then perhaps it's better stored off chain in the first place in something like IPFS or Swarm.
So with that said, I don't really have an answer but I think it's a good question deserving some research.