Ну это допустимо там, где нужно использовать именно такую структуру. Например, я в таком классе хранил сообщения-команды: в строковом поле - название команды, в мапе - её аргументы. Имена аргументов, их тип и количество для всех команд могут быть разными (и вложенными на несколько уровней), поэтому просто поля не подходят. При этом, вся эта штука легко (де)сериализуется. Да, если нужен нестандартный маппинг в/из JSON (или любой другой формат), придётся потрудится, но у меня такой проблемы не возникало.
Так что просто используй там, где это реально нужно.
PS ещё можно использовать груви и, например, Expando. Плюсы в том, что там будут настоящие поля, с доступом через точку и т.д.
def foo = new Expando(a: 1, b: "2", c: [3, 4])
foo.d = "value"
foo.e = "another value"
println foo
Expando bar = [f: 5, g: "spot"]
bar.e = foo
println bar
{a=1, b=2, c=[3, 4], d=value, e=another value}
{f=5, g=spot, e={a=1, b=2, c=[3, 4], d=value, e=another value}}