Spring and load-time weaving of Neo4j-based domain objects

What do you do when your Spring configuration isn’t in charge of creating your objects that needs to be injected with stuff? This became a real problem for me when I tried doing some non-anemic domain object implementations persisted as Neo4j Nodes.

I was playing around with creating a Twitter clone, in my opinion the “Hello World” of graph databases, and I started out with the following simple service.

Spring configurating:

The service:

This doesn’t feel very DDD. I would really want to do the User to be responsible for creating the Tweet. Like this:

The TweetNode and UserNode would look like this:

NodeDelegate, extended by both User- and TweetNode implements the Neo4j interface Node and delegates all the method calls to the underlying node ( for more on this, see my previous post) :

As you see I have a problem here. In the first version my NeoService is injected into NeoBasedTweetService and used when I create the new TweetNode. In the re-made, more DDD-style example I create the new TweetNode in the UserNode, where I don’t have access to my NeoService. How do we solve this?

One alternative that I instinctively dislike is to inject the NeoService into the UserNode. The reason for me disliking this is that my UserNode IS a Node, and it feels very tangled if a Node holds a reference to its container. But I came to think of another solution thanks to a talk I did a while back together with Henrik Reinhold on what was new in Spring 2.5.

In Spring 2.5 load-time weaving (LTW) capabillites was introduced, and with LTW you can manipulate objects in load-time, as opposed to runtime weaving that is proxy based. This, combine it with @Configurable and @Autowired, proves to be very helpful when the Spring context is not in charge of creating the objects in question, as in the example above…

To enable LTW we add one tag in the Spring configuration:

After that we have to inform the Spring container of how we want thing done. To do we edit the TweetNode and NodeDelegate like this:

Now Spring only has to be able to create Nodes. To do that we create a simple NodeFactory, like this:

… and add it to the configuration, like this:

The result of this is that Spring now has a NodeFactory that returns a new node for every request, I have annotated the TweetNode so that Spring knows of it (@Configurable) and what to do with it (@Autowired on setNode) and I’ve enabled load-time weaving in order to get the a new Node woven into a TweetNode every time a new instance of it is created in the code.

In UserNode I really don’t have to make any changes. “setNode(Node)” will get a new Node after the instantiation of a TweetNode, even if the constructor that takes a Node is called. For clarity I still changed the UserNode as follows:

The problem I had when trying to make the UserNode responsible for creating its TweetNode’s was that I needed to inject something that could create Nodes into the UserNodes. By using load-time weaving and instructing Spring of how to wire a TweetNode I don’t have to pass my NeoService around to all the UserNodes created in my code. Instead I can rely on Spring to inject what is needed, when it’s needed.

This Post Has 9 Comments

  1. Good stuff.

    Any chance you could post the final UserNode.createTweet() implementation?

    -EE

  2. I forgot that, Emil. The only change I made was that I used the empty constructor instead of sending in “null” in the one that takes a Node. But on the other hand, “new TweetNode(null)” works equally fine :) The setting of the Node is done after the new TweetNode is instantiated.

  3. I have been playing around with neo4j and so far, have independently done more or less the same thing as you, including the NodeDelegate wrapper. It is good to see someone else thinking the same.

    How do you plan on getting around the fact that pretty much all operations on a node object require a transaction block? It is easy enough inside the service layer with annotation markers but what about when you pass your domain model to your view?

    I thought about introducing the transaction block in the getProperty method in the node delegate base class but that means I have to inject a NeoService reference into every node.

    Your thoughts?

  4. Nice to hear that you’ve solved the problem in a similar way, tk :)

    I see two solutions to your dilemma:
    1. Create simple view-VOs that hold the property values of the Node.
    2. Set the transaction annotation on the controller instead of the service.

    The first alternative is some work. But if you want to pass your domain around, another alternative is to create a DetachedNode which just creates a HashMap of the property values of the Node and that implements the Node interface. This has a whole bunch of other problems (like traversers, relations etc.), but it’s less code to write if you want to pass detached properties out of the TX scope.

    The second alternative can be a bit risky since you can make changes to the DB in your controller, but it’s sure to be fast since you only need one TX per request.

    Personally, I put the TX around the controller that populates the view. I don’t know if it’s correct, but it feels like the lowest hanging fruit :)

    Does anyone else have any thoughts on this?

  5. In my case, the controller does not actually handle the rendering of the view (JSP) so annotating the controller does not help.

    I might just end up plucking property values into request attributes for my JSP to pick up. I really just want to pass in my domain objects but creating a value object from my (node wrapper) domain object just defeats the point doesn’t it?

  6. Jan: that’s very helpful. I like that approach, thanks!

  7. Matias,

    Can you attach project source code?

Leave a Reply

Close Menu