I think your terminology is a bit off -- a descriptor heap is a huge area of memory where descriptors can be allocated. You can only have a single combined SRV/CBV/UAV-type descriptor heap bound to the device at a time, and changing this binding is expensive, so you're encouraged to only ever have a single one bound. You can create extra ones as staging areas where you can pre-create SRV's which can later be copied into your main/bound heap. Within a heap, you create descriptor-tables, which get bound to the root signature.
The resource binding model in our engine has 8 "resource list" slots, which each contain an array of SRV's.
In D3D11, each "resource list" is mapped to a contiguous range of t# registers in the shader.
e.g. If a shader has ResList#0 with 4 textures and ResList#1 with 2 textures, the binding system is configured to copy ResList#0 into SRV slots [0,3] and ResList#1 into SRV slots [4,5].
In D3D12, each "resource list" is mapped to a root-descriptor-table parameter. Each shader generates a root-signature where param#0 is a table of CBV's, and param #1,2... are the res-list SRV tables. When submitting a draw-call, we determine if any res-list slots have changed since the previous draw-call (or if the previous draw-call used a different root signature). If so, a descriptor table is allocated for each new res-list within a ring-buffer, the SRV's for that root signature are copied into that new allocation from a non-shader-visible (staging) descriptor heap, and these new tables are set as root parameters.
When creating a texture, it's SRV is pre-created in the non-shader-visible descriptor heap, while the shader-visble descriptor heap is just a ring-buffer of these transient tables.