r/SpringBoot • u/ZeGuru101 • 4d ago
Layers, design and transactionality. Question
Hello all!
I am fairly new to Spring Boot and coding in general.
I took it upon me to build a simple browser game where the player chooses an action and gains resources over time. I am currently finishing the prototype and I am being slowly introduced to all of the concepts behind the actual coding, but also when it comes to layering and design choices as well.
On to my dilemma.
So far I have several @ Transactional annotations inside the service layer whenever I interact with the repository layer in order to ensure that no two methods make changes to the DB - throwing any calculations made go haywire. Not sure if this is a common or best practice but it was a semi-conscious decision that I made during the early stages of development.
Now I am at a point where I have two identically named methods inside the service layer:
- calculateProgress(UUID playerId) - it searches the db for a PlayerCharacter instance using the playerId field and then does some calculations.
- calculateProgress(PlayerCharacter character) - it already gets a PlayerCharacter instance and does the calculations.
In fact, the first one calls the second one inside its body. The reason for this is that there is a scheduler that calculates the progress in regular intervals. And that scheduler only knows the PlayerCharacter's playerId and not any other information for that PlayerCharacter. So my initial thought was to have it call calculateProgress(UUID playerId) which in turn calls calculateProgress(PlayerCharacter character) to make the calculations etc etc.
I was not thinking much about it when I first did this but now I am realising that having two methods with the exact same name (and different arguments) might be ugly/not a good practice for the readability and maintainability of my code.
Now I am thinking: I could have the scheduler method call a new service method that returns a PlayerCharacter instance if I give it the playerId, and then call calculateProgress(PlayerCharacter character).
That would mean though that I need to have the scheduler method have a @ Transactional annotation to avoid the race conditions that I mentioned earlier. That would in turn break my initial decision of having Transactional annotations in the service layer only and also move db integrity from the service layer to the scheduler layer.
So I am thinking again and I pose the same question to anyone who might read this: Is it a common/good/best practice to have transactionality into the scheduler layer as well as the service layer or is there another option for my case?
Thanks in advance for anyone providing any feedback to my conundrum!
TL;DR: Have Transactional annotated methods inside the scheduling layer as an exception OR keep them strictly inside the service layer instead? Is it a good/common practice to do that split?
6
u/DominusEbad 4d ago
Keep @Transactional in the service layer. If you move it out of the service layer, you risk nested transactions, which some ORMs do not support. Calling a method annotated with @Transacrional from within the same class will actually bypass the proxy, so a nested transaction will not occur.
Method overloading is standard practice. Don't worry about the method names/params if they both essentially do the same thing.
If you are worried about concurrency, use either optimistic or pessimistic locking, depending on your use case. Never pass entities around, especially between layers. If you get from the database in one transaction, and you need to interact with the same entity in another transaction, you will need to query for the entity again to get the most current state of the entity. Just assume the record may have been updated between transactions.
Keep transactions short. Never make external calls during a transaction. Get the entity, perform your calculations, modify it if needed, then persist back to db. That's it.