If you work through it you’ll see that the two are equivalent. Splitting the output of a Dense into num_heads chunks, or creating num_heads Dense layers  is the same (except maybe for the initialization). Draw the weight matrices to see.
The code will be clearer when we can switch this to use tf.keras.layers.experimental.EinsumDense  |  TensorFlow Core v2.6.0 2 (and maybe einops 1), but we try to avoid using experimental symbols in the tutorials.