Services and Dependency Injection
Harry
· 13 Sep 2026
· 2 views
Why Services
Services are the business logic layer of a Grails application. The golden rule:
Controllers should be thin. Services should be rich.
Creating a Service
class UserService {
def createUser(Map params) {
new User(params).save(failOnError: true)
}
}Services live in grails-app/services, are singleton Spring beans, and their public methods are transactional by default.
Transactional Behavior
All public methods run inside a transaction. A runtime exception rolls everything back:
class UserService {
def registerUser(Map params) {
def user = new User(params)
user.save(failOnError: true)
assignRole(user, Role.findByAuthority("ROLE_USER"))
user
}
}Disable transactions when not needed:
static transactional = falseDependency Injection
Spring wires everything; no annotations or XML required:
class UserController {
UserService userService
}You can inject services, domain classes, configuration values, and other Spring beans.
Read-Only Transactions
Marking read-only operations helps the database optimize:
@Transactional(readOnly = true)
def listUsers() {
User.list()
}Propagation and Isolation
Advanced flows like audit logs may need independent transactions:
@Transactional(propagation = Propagation.REQUIRES_NEW)
def logAudit() { ... }Error Handling
Throw exceptions for business failures; they trigger automatic rollback:
if (!user.save()) {
throw new IllegalStateException("User creation failed")
}Service Design Rules
- Keep methods small and focused on one responsibility.
- Avoid circular dependencies between services.
- Keep services stateless; never store request data in fields.
- Service-to-service calls are fine and common.
Testing Services
Write Spock specs for business logic:
class UserServiceSpec extends Specification {
def userService
void "test user creation"() {
when:
userService.createUser([username: "test"])
then:
User.count() == 1
}
}Common Mistakes
- Business logic leaking into controllers.
- Long transactional methods holding locks too long.
- Circular dependencies.
- Stateful services that break under concurrency.
Key Points
- Services are transactional, singleton Spring beans.
- DI is automatic - just declare the property.
- Throw exceptions for failures and let them roll back.
- Stateless, focused services scale and test best.