Reputation: 26796
I'm trying to find out the best way to handle transactions at object level (not database level). Short example: 4 objects A, B, C and D. A starts a transaction and calls methods in B and C. Whithin this transaction C is also calling D. The methods being called aren't supposed to always participate in this transaction, but can be called also on their own. Are there any patterns for managing transactions at object level?
I didn't really find something, so I came up with this: Use a TransactionContext where one can register TransactionListeners. If a transaction is started using the TransactionContext, then it will inject the running transaction into each of the registered listeners, which in turn will use a running transaction or elsewise will start one on their own if needed. This way I'm pretty free to decide wether I want an object participating in my transaction or not.
The problem comes when having object calling chains like above. When starting the transaction I just know that B and C must participate in the transaction so I add them to the TransactionContext. But what about D? I don't really want to pass the TransactionContext around to B and C.
I would appreciate some input on my approach as well as some pointers to proven patterns (even better).
Upvotes: 5
Views: 2773
Reputation: 18661
Highly recommend this paper by Andrei Alexandrescu and Petru Marginean, which presents ScopeGuard patten. This is an elegant and a very robust solution for managing transactions specifically in presence of exceptions.
Applicable to the problem that you are trying to solve ScopeGuard will allow you to define the scope of your transaction, and easily (meaning automatically) manage that for you whether transaction involves methods A,B,C,D or calling individual methods.
Upvotes: 0
Reputation: 3501
I'd suggest: Prevayler
Prevayler is an object persistence library for Java. It is an implementation of the
System Prevalence architectural style, in which data is kept hot in Memory with
changes journaled for system recovery.
Prevayler ' s architecture is illustrated in the diagram shown there. Prevayler [1]
serves as a transactional barrier for the business objects [2] of your application,
held in Memory. You encapsulate all modifications of your business objects into
instances of the Transaction interface [3], much like a " command " pattern
(though different from a command in some details of the pattern). Whenever
you ask Prevayler to execute a transaction on your business objects [4], Prevayler
first writes the transaction object to a journal [5] so that data is not lost if your
system crashes. Prevayler can also write a snapshot of your entire business object
graph [6] as often as you wish. Prevayler uses the latest snapshot together with
the journals to automatically recover your business objects from disk [7] on
application startup by restoring the snapshot and then re-executing every
transaction that was originally executed after that snapshot was taken.
Of course, you can disable the persistent store and use only the In-memory store.
Upvotes: 0
Reputation: 391854
"I don't really want to pass the TransactionContext around to B and C."
Why not? They participate and they delegate to yet other objects.
Either
Everyone needs to register. Which means you have to delegate registration. A
knows that it hands off to B
and C
. Each of which may (or may not) have further delegatees to register. This is relatively simple to implement with a "RegisterYourselfAndYourDelegatees" method.
Eschew the Listener design pattern. Create a transaction context and pass it around. This replaces the registration and injection with a slightly simpler design. However, you'll need to have two Context subclasses -- the real Context and a stub Context that does nothing and is used outside a transaction context.
This makes your function definitions slightly more complex. For Java, you can use an overloaded naming to have two method functions with different signatures.
For Python, this is a non-issue; the context is an optional argument.
Upvotes: 2
Reputation: 625077
The Spring framework (initially for Java but there's a .Net version too now) can do this. Methods are marked as:
This is typically done with annotations. Sounds exactly like what you're describing.
Check out Spring's transaction management.
Upvotes: 1