Question

I'm just wondering how to best handle transactions across multiple service layers. The service layers use an ORM to store and retrieve from the database. Should the transactions be known and handled within the individual service layers? Or should they be handled by another layer?

For example: I have two service layers for users and clients. I would like to:

1) Create and save a new client
2) Create and save a new user
3) Assign that user to the client

All within a single transaction.

A simple example might look like this:

$userManagementService = new UserManagementService;
$newUserData = array(...);
$newUser = $userManagementService->create($newUserData);

$clientManagementService = new ClientManagementService;
$newClientData = array(...);
$newClient = $clientManagementService->create($newClientData);

$userManagementService->assignUserToClient($newUser, $newClient);

Where should transaction logic go?

Was it helpful?

Solution

Do not try to do nested transactions within service layers or within the ORM.

Transactions are global to the DB connection. Unless your RDBMS supports nested transactions natively and your DB API exposes nested transactions, you can run into anomalies.

For details, see my answer to How do detect that transaction has already been started?

Since you're using PHP, the scope of your transactions is at most a single request. So you should just use container-managed transactions, not service-layer transa. That is, start the transaction at the start of handling the request, and commit (or rollback) as you finish handling the request.

If an exception requiring a rollback occurs deep within nested ORM actions, then bubble that up by using an Exception, and let the container (i.e. your PHP action controller) take care of it.

OTHER TIPS

Are you facing an aggregation of transactions? Does this pseudo code match what I think you're saying?

try
    begin application transaction
    begin ORM transaction 1
       create new user
       commit request
    begin ORM transaction 2
       create new client
       commit request
    begin ORM transaction 3
       create user client association
       commit request
    commit application tx
catch()
    abort ORM tx 3
    abort ORM tx 2
    abort ORM tx 1
    abort app tx

At any point a rollback of a nested transaction will likely throw an exception, and those exceptions would logically roll back all the nested transactions in the two-phase commit.

I might not be getting what you're after tho.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top