3

I have a layered web-application driven by spring-jpa-hibernate and I'm now trying to integrate elasticsearch (search engine).

What I Want to do is to capture all postInsert/postUpdate events and send those entities to elasticsearch so that it will reindex them.

The problem I'm facing is that my "dal-entities" project will have a runtime dependency on the "search-indexer" and the "search-indexer" will have a compile dependency on "dal-entities" since it needs to do different things for different entities.

I thought about having the "search-indexer" as part of the DAL (since it can be argued it does operations on the data) but even still it should be as part of the DAO section.

I think my question can be rephrased as: How can I have logic in a hibernate event listener which cannot be encapsulated solely in an entities project (since it's not its responsibility).

Update
The reason the dal-entities project is dependant on the indexer is that I need to configure the listener in the spring configuration file which is responsible for the jpa context (which obviousely resides in the dal-entities).
The dependency is not a compile time scope but a runtime scope (since at runtime the hibernate context will need that listener).

Ittai
  • 5,625
  • 14
  • 60
  • 97

2 Answers2

1

The answer is Interfaces.

Rather than depend on the various classes directly (in either direction), you can instead depend on Interfaces that surface the capabilities you need. This way, you are not directly dependent on the classes but instead depend on the interface, and you can have the interfaces required by the "dal-entities", for example, live in the same package as the dal-entities and the indexer simply implements that interface.

This doesn't fully remove the dependency, but it does give you a much less tight of a coupling and makes your application a bit more flexible.

If you are still worried about things being too tightly coupled or if you really don't want the two pieces to be circularly dependent at all, then I would suggest you re-think your application design. Asking another question here on SO with more details about some of your code and how it could be better structured would be likely to get some good advice on how to improve the design.

cdeszaq
  • 30,869
  • 25
  • 117
  • 173
  • Do you think this constitues also for simple pojos? Which are the problem here since the dal-entities already depends only on an interface of the indexer but the indexer depends on the concrete model-pojos. I thought about using interfaces also on the pojos however I wasn't sure if that's the way. – Ittai Jan 24 '12 at 13:37
  • I'm curious to know _how_ (or _why_) the domain pojos depend on the indexer. It makes sense for the indexer to depend on them, but not the other way around. Could you post a sample of the domain classes (the pojos)? – cdeszaq Jan 24 '12 at 13:45
  • Also, more to the question you asked, yes, if you need to remove the dependency, an interface can be used even for an amazingly simple class. Look at the Runable interface, after all...only a single method! – cdeszaq Jan 24 '12 at 13:46
  • I've added the explanation. If you think the actual code sample of the spring configuration file would be more helpfull please say so and I'll post it (just thought it was less helpfull) – Ittai Jan 24 '12 at 13:57
  • You _should_ be able to have Spring provide the same context to the indexer, since it has to provide the context to JPA. Using Spring to inject that dependency (the context) into where it's needed, rather than the classes sharing it across layers themselves, is a _much_ better design. Having classes share is "smelly", especially if you are already using the amazingly awesome DI power that is Spring. – cdeszaq Jan 24 '12 at 14:05
  • You mean to try to programatically add the listener to the jpa context before it's loaded by the indexer? I don't know if it's possible but it actually sounds interesting. Since it's an augmentation of the jpa context which (if possible) becomes optional and a derivative of the runtime dependencies. – Ittai Jan 24 '12 at 14:33
0

Hibernate supports PostUpdateEventListener and PostInsertEventListener.

Here is a good example that might suite your case

The main concept is being able to locate when your entity was changed and act after it as shown here.

public class ElasticSearchListener implements PostUpdateEventListener {

@Override
public void onPostUpdate(PostUpdateEvent event) {
    if (event.getEntity() instanceof ElasticSearchEntity ) {
        callSearchIndexerService(event.getEntity());
        Or 
        InjectedClass.act(event.getEntity());
        Or 
        callWebService(InjectedClassUtility.modifyData(event.getEntity()));

      ........
}
}

Edit

You might consider Injecting the class that you want to isolate from the project (that holds the logic) using spring.

Another option might be calling an outside web service that is not dependent on your code. passing to it either the your original project object or one that is modified by a utility, to fit elasticsearch.

Aba Dov
  • 962
  • 4
  • 12
  • 20
  • Thanks, I know about these listeners and I'm using them currently. The question is more towards how cdeszaq percieved it, i.e. how to design the logical coupling between the entities-listener-indexer. – Ittai Jan 24 '12 at 13:39
  • @Ittai, thank you for your comment, changed my answer, hope it helps. – Aba Dov Jan 24 '12 at 13:55