Is this what you want?
julia> Z(a, b) = Iterators.product([0:a-1 for _ in 1:b]...)
Z (generic function with 1 method)
julia> collect(Iterators.product(Z(3,2), Z(3,2))) |> vec
81-element Vector{Tuple{Tuple{Int64, Int64}, Tuple{Int64, Int64}}}:
((0, 0), (0, 0))
((1, 0), (0, 0))
((2, 0), (0, 0))
((0, 1), (0, 0))
((1, 1), (0, 0))
((2, 1), (0, 0))
((0, 2), (0, 0))
((1, 2), (0, 0))
((2, 2), (0, 0))
((0, 0), (1, 0))
⋮
((0, 0), (2, 2))
((1, 0), (2, 2))
((2, 0), (2, 2))
((0, 1), (2, 2))
((1, 1), (2, 2))
((2, 1), (2, 2))
((0, 2), (2, 2))
((1, 2), (2, 2))
((2, 2), (2, 2))
julia> collect(Iterators.product(Z(4,3), Z(4,3))) |> vec
4096-element Vector{Tuple{Tuple{Int64, Int64, Int64}, Tuple{Int64, Int64, Int64}}}:
((0, 0, 0), (0, 0, 0))
((1, 0, 0), (0, 0, 0))
((2, 0, 0), (0, 0, 0))
((3, 0, 0), (0, 0, 0))
((0, 1, 0), (0, 0, 0))
((1, 1, 0), (0, 0, 0))
((2, 1, 0), (0, 0, 0))
((3, 1, 0), (0, 0, 0))
((0, 2, 0), (0, 0, 0))
((1, 2, 0), (0, 0, 0))
⋮
((3, 1, 3), (3, 3, 3))
((0, 2, 3), (3, 3, 3))
((1, 2, 3), (3, 3, 3))
((2, 2, 3), (3, 3, 3))
((3, 2, 3), (3, 3, 3))
((0, 3, 3), (3, 3, 3))
((1, 3, 3), (3, 3, 3))
((2, 3, 3), (3, 3, 3))
((3, 3, 3), (3, 3, 3))
Note that I collect it to make the result visible. Normally, as the result set can be very large it is better to stay with lazy Iterators.product and just iterate it.