-1

Question is result of me consuming a service exposed by a colleague, the service is a simple update service, which takes an object/entity as input, does some processing and updates persistence. the service doesn't handle transaction within, expects caller to wrap the service with transaction. As the business logic with in the service is atomic, my point is service should handle the transaction. Why?

  1. by this we are keeping the scope/life of the transaction is short. with caller wrapping the transaction, life of the transaction is wider.
  2. business logic is atomic, caller wrapping the transaction can lead to change in atomic scope in the future.

What is the better approach?

1 Answers1

1

The reason this is done the way it is, and not the way you expect it to be, is to give you control about the related transaction.

Right now, you're not interested in this update being part of a larger transaction outside of the service, so having to create the transaction seems like something the service could automate for you. But the service has been written to specifically account for you wanting to pass in your own transaction.

If tomorrow you need to update this thing and perform another data mutation in a single transaction (= both pass or none pass), you are able to do so because you are the one who provides the transaction under which the service works. This means that your colleague doesn't have to revisit their code just because you are adding something to the transaction you manage.

This is a matter of inversion of control and making sure the codebase is change-friendly without incurring more work.


If an analogy helps, your question/idea boils down to:

My local coffeeshop has me bring in my own cup for them to put the coffee in. They should just use plastic/cardboard cups.

While your idea is certainly a viable one, it is not the most consumer-friendly one. Plenty of people want to choose which cup their coffee is made in, because they don't like/want the default cup that is provided.

You are correct, however, that it is nice if the coffeeshop at least still provides a default cup if you don't bring your own. However, that decision is up to the coffeeshop.
For example, they may want you to bring your own cup as a point of principle to avoid needless resource usage. If you really like cardboard cups, you can still just go out and get your own cup and bring that to the coffeeshop.

The same is true of your transaction. You're perfectly able to just create your own transaction and pass it in, and you're not explicitly required to do more with this transaction than a single call to the service.
If it bothers you that much, just write a wrapper service which automatically opens a transaction, calls the service, and commits the transaction.

Flater
  • 44,596
  • 8
  • 88
  • 122
  • thank you for detailed explanation. my view point if the coffeeshop doesn't provide a default cup, will loose opportunity to serve the customer, one who walks in without a cup. We can use transaction prorogation, the service provide the default implementation, if the caller already has/passes transaction use it else create one. – PraveenKumar May 27 '21 at 09:21
  • @PraveenKumar: I generally like default implementations, but they are not _required_, they are a nice-to-have feature. It takes you about the same amount of work to write a wrapper with a default implementation than it does your colleague, so I see no reason why your colleague _must_ invariably be the one providing it. Class libraries tend to cut down on fluff specifically to keep their libraries concise and easy to manage. To further the analogy, it's perfectly fine for the coffeeshop to have a separate cup provider just outside the door, as opposed to storing cups in their own limited space. – Flater May 27 '21 at 09:24