Хорошо, одна вещь, которая не представлена в вашем примере выше, это то, что вы не дали имя своему экземпляру приложения, даже если это обязательное поле (поля grails могут иметь значение null: по умолчанию false, если вы не указали это явно иначе). Могу поспорить, если вы проверите
app.hasErrors()
После вашего app.save () вы увидите, что в нем есть ошибки, которые вы можете перечислить в коллекции app.errors.
В противном случае, у вас все в порядке, и этот код отлично работал для меня с объектами домена у вас выше:
AppCategory appCat = new AppCategory(name: "blah")
appCat.save()
assert !appCat.hasErrors()
App app = new App(name: "testApp")
app.addToCategories(appCat)
app.save()
assert !app.hasErrors()