There is one thing that is very important to understand about how contract storage works in diamonds.
Without understanding this one thing a person won’t really understand how to handle data in a diamond.
The one thing that is very important to understand is that facets read and write to a diamond proxy contract’s storage, not to their own contract storage.
Facets or Solidity libraries define structs and where to read and write those structs in a diamond proxy contract’s storage.
Look at this diagram from the EIP-2535 Diamond standard and see that facets are used for their code only, and that only diamond contract storage is used:
To understand this better let’s go through some basics of diamonds.
A diamond is a proxy contract.
When an external function is called on a diamond proxy contract it does a delegatecall to a facet that has that function. The delegatecall will cause the facet’s function to act on the diamond proxy contract’s storage — not on the facet’s contract storage.
There is a difference between defining a struct and storing data in a struct. A facet (or Solidity libraries) define structs but that does not mean that they store any data defined by those structs within themselves. They do not store any contract storage data in themselves, they define structs and where to store data using those structs in diamond proxy contract storage.
Facets contain no contract storage data, only code and struct definitions, and possibly constants and immutable variables (which are constants set during deployment). Variable data (in structs) can be defined in facets but no variable data is stored in facets. That’s why facet’s are reusable. Code is reusable. The same deployed facet can be used by multiple diamonds.
All data that can change — variables — are stored in the diamond proxy contract storage. Facets can define variables in structs, but store no variable data in themselves. Facet’s do store data, but they store data in diamond proxy contracts, not in themselves.
So of course a constructor function in a facet that tries to write to contract storage is nonsense. Because a constructor function in a facet that writes to storage is writing to a facet’s storage (not a diamond proxy contract’s storage) and facet contract storage is completely ignored by diamonds.
Replacing or removing a facet in a diamond causes no contract storage data loss because diamonds don’t read or write data stored in facet contract storage.
A different but related subject is how to manage variables in diamonds. That is covered by the article here:
Awesome content, After reading this, it made more sense to me to understand workflow of Diamond Storage