Almost there:
julia> transform!(df, :B => (x -> ismissing(x) ? [missing] : string.(split(x, ";")), ) => :C)
4×3 DataFrame
Row │ A B C
│ Int64 String? Vector
─────┼─────────────────────────────
1 │ 1 a; b ["a", " b"]
2 │ 2 missing [missing]
3 │ 3 b ["b"]
4 │ 4 a; c ["a", " c"]
julia> flatten(df, :C)
6×3 DataFrame
Row │ A B C
│ Int64 String? String?
─────┼─────────────────────────
1 │ 1 a; b a
2 │ 1 a; b b
3 │ 2 missing missing
4 │ 3 b b
5 │ 4 a; c a
6 │ 4 a; c c