jpos / jPOS

jPOS Project
http://jpos.org
GNU Affero General Public License v3.0
599 stars 458 forks source link

master: Add annotation functionality to jPos participant. #576

Open espaillato opened 6 months ago

espaillato commented 6 months ago

Application developers are very familiar with writing APIs and using annotations to wire things together. In addition, IoC/Dependency Injection is an accepted standard across frameworks. JPos, on the other hand, uses a simple 2-phase commit interface to string together units of work called participants. Participants can be considered like filters strung together to perform a transaction in the web world.

While writing participants in jPos is simple, the interface is straightforward. However, it has some drawbacks, mainly due to two factors: the lack of IoC and the lack of a contract. This attempts to introduce IoC and a verifiable contract on the TransactionManager using annotations to enable parameters and return types on the prepare method. This should make JPos more developer-friendly by adding auto-binding of parameters.

For example, We convert the standard participant entry point int doPrepare(long id, Context ctx) to any custom method with parameter binding.

  @Result(CARD)
  protected Card findCard(@ContextKey(PAN) String pan, @ContextKey DB db) throws BLException {

This increases the code's readability and testability by specifying a contract and doing the injection at the transaction manager level. In this case, findCard only accesses PAN, needs access to the CardDAO object and returns a Card object. When testing, you only need to test card present or missing scenarios because you know this method cannot have any other side effects.

The annotations should be more familiar to any developer comfortable with API development using any current framework.

The power of this comes in with the contract. Suppose I wanted to boondoggle extra functionality into the FindCard participant. I can do so by using the Context, which can have side effects throughout the code without changing any test. However, with the annotated participant, the only thing that can be returned by the FindCard participant is the Card object, so no changes to the contract are allowed without a change to the signature.

aVolpe commented 6 months ago

After some testing, some minor observations:

  1. A NameRegistrar entry can be created after the transaction manager's setup, so the registrar arguments should be resolved in the call to prepare and not in the configuration.
  2. To test this API better, this can be put entirely in a separate module since the only interaction point with the rest of jPos is in the transaction manager. This will make this easy to test in current projects.

This is a very cool idea and implementation!

ar commented 6 months ago

... so the registrar arguments should be resolved in the call to prepare and not in the configuration.

Remember the participants use the Flyweight pattern. It's cheaper to do stuff at configuration time than prepare time.

espaillato commented 6 months ago
  1. so the registrar arguments should be resolved in the call to prepare and not in the configuration.

At configuration, we check for the presence of the entry in NameRegistrar to so the name resolution. This is to prevent the transaction manager to be initialized without being fully functional. If you have a dependency on something in NameRegistrar, you should have it initialize before the transaction manager. Having said that, the actual lookup/binding NameRegistrar.get... happens at runtime. This is because an entry in NameRegistrar can be changed, i.e. xml is touched, so we always want to get the latest from the NameRegistrar.