SDN4 - Entity lifecycle event handlers compatible with GraphRepository - spring

I am using Spring Data Neo4j 4.0.0.RELEASE and would like to take advantage of the built-in data manipulation events to insert some audit information on the fly (e.g. timestamps). The documentation seems to suggest that this is only available to me if I am directly using Neo4jTemplate.
Are there any similar hooks available for the GraphRepository abstraction? That is, is there an out of box way for me to hook into graph repository operations (a la Spring DataJPA?) I've written some tests and can confirm that the documented events don't fire when I'm just using the GraphRepository.

AbstractGraphRepository is from the 3.x codebase, so is not directly relevant here.
As noted, SDN 4 does not yet provide automatic support for Spring's RepositoryEventListener interfaces. Implementing event listeners correctly in SDN 4.0 is complicated because of the nature of the underlying save mechanism, which persists an entire tree of "dirty" objects rather than just a single top-level entity. If the object you want to intercept is not the top-level entity being saved, the event listener for it won't fire.
The SDN development team is currently considering the best way to enable event handlers to fire for objects that may be persisted at any depth in the save tree.
In the meantime, the solution suggested by simonl should work.

Related

MongoDB Panache Best Practices for Multi-Document Transactions

Regarding the quote below in the MongoDB Panache documentation [https://quarkus.io/guides/mongodb-panache]
MongoDB offers ACID transactions since version 4.0. MongoDB with Panache doesn’t provide support for them.
As such, is there a recommended approach or a best practice on handling Multi-Document transactions to ensure atomicity?
Consider the example:
public void buyCarTest() {
carRepository.increaseStock(1);
cashRepository.decreaseCash(10000);
}
If we were to do it manually, it would be:
check if the write operation into 2nd repository failed, and
if so, revert changes made in carRepository
This approach seems tenuous at best especially if there are more than 2 repositories I'm writing into.
Thanks.
What you propose is what is called a compensation and it is tricky to implements.
I'd rather use an event based mechanism for this: you send the two events and asynchronously they are processed so a failure of one of the tenant (the stock manager) will not impact the second one.
You can also use MongoDB transaction but for this you will need to use the MongoDB API instead of Panache (so get the collection from your entity and use it).
Transaction support for MongoDB is a work in progress (see https://github.com/quarkusio/quarkus/pull/7222) you can watch this issue to be notified when it'll be implemented.

What is the typical use case of Spring Application Events?

There is an amazing mechanism is spring: Spring Application Events, I see it helps to build loosely coupled application, implement observer pattern, reactor pattern.
My question is, what is the trigger in Spring application architecture when Spring Application Events is absolutely inevitable? Actually any application classes relationship can be build using events only as well as using class associations and hierarchy only (i'm talking about monolith service now).
May be it's more architectural question, but what is the threshold when it's need to consider events between objects inside a spring application?
Could it be definitely seen the cases when Spring Application Events absolutely needed?
Spring Security pushes events on certain event occurrences. See list of events
In some cases, it's much better to push message instead invoke a method in a classical way. Eg if you need to invoke the same function I multiple places in the code, just to notify another object.
It allows us to build a system which utilizes event-driven architecture. Read more
Helps in solving a producer-consumer problem
Send emails by pushing event object to ApplicationEventPublisher See Spring Higher-Order Components and #EnableEmailSending

Database event listener using spring boot

I need to attach a listener to a table in db
which should call a spring boot method, once CRUD operation is performed in the table(pre listeners and post listeners)
the entry can be made from any source
how can i do that in spring boot?
If the entity can be created from any source - e.g. manual insert - this is something which is outside of the scope and context of your running application.
What you're describing is known as the CDC (change data capture) pattern.
To implement CDC in this case you need to use the instrumentation of the underlying database - for example triggers.
As I see this is tagged with MongoDb - triggers are not an option as mongodb doesn't have support for triggers.
If you are using MongoDb v3.6+ you can leverage the new Change Streams feature. This is the official example with Java.
Change streams allow applications to access real-time data changes
without the complexity and risk of tailing the oplog. Applications can
use change streams to subscribe to all data changes on a single
collection, a database, or an entire deployment, and immediately react
to them. Because change streams use the aggregation framework,
applications can also filter for specific changes or transform the
notifications at will.
If you are using earlier versions of MongoDb you can monitor the oplog or use tailable cursors with capped collections.
Another approach would be to look into a 3rd party solution that turns everything happening in the DB as event streams - like for example debezium.
This article explains how to call any program from DB-Trigger.
Therefore, you can just create a Spring Boot java app and make the sys call to your app.
Similar mechanism is also available in Oracle and other DB.

Spring - JPA - observe events

I would like to observe when an entity is saved or deleted so that I may perform additional activities pertaining to that entity. I thought I did this in the past WITHOUT using the #EntityListeners annotation on the entity class itself because my entity listeners would be implemented in the service layer and NOT in the model / data layer.
The only other way I can think of to do it is inside the persistence.xml and specify my listeners there.
Are there any alternate approaches?
I did this a long time ago in a galaxy far, far away, and the answer was in my question.
The solution (that I wanted, but disliked because of using XML) was to:
write a generic entity listener using annotations, and get a programmatic instance of the bean manager (CDI), or equivalent in spring, then fire an event which I can process either via CDI or spring
where I want that listener to work, place a persistence.xml file and manually specify the entity-listeners there
if I want to do anything special, I simply observe the event that I'm interested in and I get the information that I want.

Centralising logging using Spring AOP

I was hoping that you guys will be able to help me with a conundrum that I am currently facing.I am currently working on an existing web application project where one of the requirements is that we have to centralise logging. The application is a layered application consisting of the client layer (i.e. the views), service layer, business layer and DAO layer.
Currently, logging in the application is handled by controller methods where each controller method that needs to have some information logged, manually logs the data by calling a logging function. Requests handled by these controller methods come from many different client sources including mobile devices (such as phones), web browsers, web services etc. Currently, all the data that needs to be logged is captured in a general purpose object which is passed to a logging method to persist these properties to a DB table.
The problem is that this general purpose object is exactly that, a general purpose object. Its used for many other tasks including logging, searching and many other tasks. When this general purpose object is used for logging, with the exception of a couple of attributes, most of the the attributes which are used to populate the general purpose object (in the case of logging) come from the request i.e. (a HttpServletRequest object). As a result of the versatility of this object, there is a potential for this general purpose object to get misused. Hence, we want to get rid of this general purpose object and create specialised objects for specialised tasks.In the case of logging, we have decided to create a logging object that we will use persist the data we need to have logged. We will be using Spring AOP effect the logging
The conundrum is this
1)Should we be using the controller to set the properties on the new specialised logging object that we want to log and then using an AOP advice, retrieve the log object for persistence once a controller method has finished executing
OR
2)should we set the properties on the new log object in an AOP advice using attributes that we have placed in a request object (i.e. HttpServletRequest object)?
My issue with option 1 is that the controller becomes aware of the logging and also, according to good design principles, a controller is only supposed to delegate tasks to business and services layers to perform such tasks. Option 1 will mean that the controller is doing more than just delegating tasks i.e. it will be building log objects
My issue with option 2 is that it couples my logging object closely with the request object (i.e. HttpServletRequest object) and hence I am wondering whether there are any potential with that approach.
Any sort of suggestions, advice and critique will be welcome. Also, if anyone has had to deal with a similar situation, I want to hear how they went about addressing the issue.
Thank you all in advance.
I'd add logging to the service layer, expressed as interfaces, using aspects.
You can use HTTP filters or aspects to log from the controller layer.
You can apply AOP in multiple layers as needed.

Resources