A container block can specify what it starts with and what may be added, which is the difference between a layout and a suggestion.
const TEMPLATE = [
['core/heading', { level: 3, placeholder: 'Title' }],
['core/paragraph', { placeholder: 'Description' }],
]
<InnerBlocks
template={TEMPLATE}
templateLock="insert"
allowedBlocks={['core/heading', 'core/paragraph', 'core/button']}
/>
allowedBlocks restricts the inserter and templateLock restricts the structure, and they solve different halves of the same problem — a container can allow three block types and still permit reordering, or lock the order and allow any content. Placeholders in the template are what make an empty block usable, because an editor faced with an empty container does not know what belongs there. Neither restriction survives pasting, which is worth knowing.