Transactional: controller vs service
Asked Answered
C

3

16

Consider I have a controller method get() which calls a few service methods working with database.

Is it correct to make the entire controller method transactional or just every service method?

It seems to me that we must make get() transactional because it performs associated operations.

Changeling answered 16/12, 2010 at 16:7 Comment(0)
G
9

I prefer to make only transactional the service methods that need to be transactional and control the transactionality in the service not in the controller. You can create a service method which englobes other service methods and with the spring transaction manage the transaction with propagation in @Transactional annotation.

@Transactional(propagation =...)

Edit

If I had 2 methods for example saveUser() and saveEmail() (because I store the emails in a database to send them later - like a queue) I would create in my service a method saveUserAndSendEmail(User user) which would be transactional. This method would call saveUser and saveEmail() each one in a @Repository component because they deal with the database. So I would put them in the @Repository components the methods to handle with the database and then I control the transactionality in the @Service component. Then the controller will only have to worry about providing the data and calling whenever they are needed. But I make a transaction because I don't want to commit changes in thedatabase until the whole method is executed successfully.

But this is the style I usually use, I'm not saying that this must be the way to go.

Gurule answered 16/12, 2010 at 16:21 Comment(6)
It seems to be preferred design but why? Consider I need to loadMenuItems(), loadUserInfo(), loadDocument() - lots of methods. According to you I have to create a loadMenuItemsAndUserInfoAndDocument() method - is it OK?Changeling
@Andrey I haven't said it is the preferred design. It's the way I usually work because I prefer that the controller don't need to be aware of transaction management. According to the name of your methods starting with load... if they only real from the database why do you need to make a transaction?Gurule
Well, what if you have two methods: createUser(), sendEmail() from different services (user and mail). And you need to call them both in a transactionalal controller. What should you do?Changeling
@Aundrey I've edited the question with two methods that would need to be in a transaction because they both need to persist data in the database.Gurule
#18498615Unclose
If you have two methods from two different services, you need to apply the facade pattern and create a service let´s say UserEmailFacade that call the method process() that interally call the methods of createUser and sendEmail();Deflection
F
9

That's entirely up to you, and how you interpret your own business logic.

Spring doesn't really care where you put the transaction boundaries, and certainly doesn't limit you to putting them on your DAO classes.

So yes, adding @Transactional to your controller methods is perfectly valid.

Fantasia answered 16/12, 2010 at 16:20 Comment(2)
IMHO, @Transactional in a controller has not too much sense from an architectural perspective (MVC). I mean, a controller shouldn't be aware of the persistence layer, and you could have to reuse your business logic in a desktop application where your controller layer no longer exists... I think transactionality should be defined in the @Service layer.Herc
Perfectly valid from language point. But if you consider bounding your controller method to some DB actions that's not a good practice.Woodpecker
G
9

I prefer to make only transactional the service methods that need to be transactional and control the transactionality in the service not in the controller. You can create a service method which englobes other service methods and with the spring transaction manage the transaction with propagation in @Transactional annotation.

@Transactional(propagation =...)

Edit

If I had 2 methods for example saveUser() and saveEmail() (because I store the emails in a database to send them later - like a queue) I would create in my service a method saveUserAndSendEmail(User user) which would be transactional. This method would call saveUser and saveEmail() each one in a @Repository component because they deal with the database. So I would put them in the @Repository components the methods to handle with the database and then I control the transactionality in the @Service component. Then the controller will only have to worry about providing the data and calling whenever they are needed. But I make a transaction because I don't want to commit changes in thedatabase until the whole method is executed successfully.

But this is the style I usually use, I'm not saying that this must be the way to go.

Gurule answered 16/12, 2010 at 16:21 Comment(6)
It seems to be preferred design but why? Consider I need to loadMenuItems(), loadUserInfo(), loadDocument() - lots of methods. According to you I have to create a loadMenuItemsAndUserInfoAndDocument() method - is it OK?Changeling
@Andrey I haven't said it is the preferred design. It's the way I usually work because I prefer that the controller don't need to be aware of transaction management. According to the name of your methods starting with load... if they only real from the database why do you need to make a transaction?Gurule
Well, what if you have two methods: createUser(), sendEmail() from different services (user and mail). And you need to call them both in a transactionalal controller. What should you do?Changeling
@Aundrey I've edited the question with two methods that would need to be in a transaction because they both need to persist data in the database.Gurule
#18498615Unclose
If you have two methods from two different services, you need to apply the facade pattern and create a service let´s say UserEmailFacade that call the method process() that interally call the methods of createUser and sendEmail();Deflection
S
0

You also need to consider that if you add it on the controller level then you may hold connection withing transaction for longer then needed. So it is good practice only wrap in transaction what is needed. I agree service level is more appropriate place.

So lets say you have few DB calls and API call withing controller method - best would be to wrap in the transaction only DB calls in case API will take long time to get response. For small volume application this may not make big difference but with high traffic you may run out of db connections.

@Transactional
public void initialPayment(PaymentRequest request) {
    savePaymentRequest(request); // DB
    callThePaymentProviderApi(request); // API - this should not be in transaction
    updatePaymentState(request); // DB
    saveHistoryForAuditing(request); // DB
}
Scheck answered 2/12, 2022 at 16:2 Comment(1)
As it’s currently written, your answer is unclear. Please edit to add additional details that will help others understand how this addresses the question asked. You can find more information on how to write good answers in the help center.Deglutinate

© 2022 - 2024 — McMap. All rights reserved.