Architecture decision records - How not to get lost in the past
Spring Up Your Graph
1. Spice up Your Graph
Gerrit Meier and Michael Simons
introducing the all new Spring Data Neo4j (SDN 6)
SpringOne 2020
Spring
2. Introducing SDN 6 at SpringOne 2020 by @meistermeier and @rotnroll666 2
3. Introducing SDN 6 at SpringOne 2020 by @meistermeier and @rotnroll666 4
Label
Type
4. Introducing SDN 6 at SpringOne 2020 by @meistermeier and @rotnroll666 5
Person
KNOWS
{
name: "Mr. X"
dob: 26.02.1982
}
{
met: "Spring One"
contact: 03.09.2020
}
5. Introducing SDN 6 at SpringOne 2020 by @meistermeier and @rotnroll666 6
Person
KNOWS
Person
Topic
INTERESTED_IN INTERESTED_IN
{
term: "Spring Data"
description: " … "
}
Graph Databases
6. Introducing SDN 6 at SpringOne 2020 by @meistermeier and @rotnroll666 7
Spring Data Neo4j Team
@meistermeier @rotnroll666
7. What would you do if you could create
yet another object mapper from
scratch?
8. What would you do if you could create
yet another object mapper from
scratch?
Why
9. Introducing SDN 6 at SpringOne 2020 by @meistermeier and @rotnroll666 9
Current Spring Data Neoj4 + Neo4j-OGM
• start.spring.io gives you SDN and Neo4j-OGM
• 3 different modes available (Embedded, HTTP and native)
• Makes usage of Cypher harder than necessary
• Custom queries are possible but go all through the Neo4j-OGM
abstraction
• Some building parts stack vertically, some horizontally
➡ Confusing options for beginners
11. Introducing SDN 6 at SpringOne 2020 by @meistermeier and @rotnroll666 11
What do we want?
• A composable stack
• Nicely integrated in Spring Boot
• Access at all abstraction levels
• Raw queries (outside application level transactions)
• Raw queries (inside application level transactions)
• Plus ad-hoc mapping
• Mapping
• Repository abstraction
• Reactive (and imperative) database access
12. Introducing SDN 6 at SpringOne 2020 by @meistermeier and @rotnroll666
• Java Driver
• Direct usage
• Simple Client
• RowMapper-like
• Neo4jTemplate
• Entity-Metadata aware
• Repositories
• DDD way of accessing your data
Multiple, ordered
levels of
Abstraction
13. Introducing SDN 6 at SpringOne 2020 by @meistermeier and @rotnroll666 13
Spring Data Repositories
Neo4j Template
Neo4j Client
Neo4j Java Driver
spring-boot-starter-data-neo4j
spring-boot-autoconfigure
SDN 6
provides
configures
A composable stack
14. Introducing SDN 6 at SpringOne 2020 by @meistermeier and @rotnroll666
• Imperative and reactive on par
• New: Reactive transactions
Reactive support
15. Introducing SDN 6 at SpringOne 2020 by @meistermeier and @rotnroll666
• Bolt protocol
• Driver type system
100%-based on
Neo4j Java Driver
16. Introducing SDN 6 at SpringOne 2020 by @meistermeier and @rotnroll666 18
@Autowired org.neo4j.driver.Driver
@RestController
public class MoviesController {
private final Driver driver;
public MoviesController(Driver driver) {
this.driver = driver;
}
@GetMapping(path = "/movies", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public Flux<String> getMovieTitles() {
/# Using an implicit, driver managed transaction
return Flux.usingWhen(
Mono.fromSupplier(driver:%rxSession),
s -' Flux.from(s.run("MATCH (m:Movie) RETURN m ORDER BY m.name ASC").records()),
RxSession:%close
).map(r -' r.get("m").asNode().get("title").asString());
}
}
Based on open standards: Reactive Streams. Can be used with RxJava2, too.
17. Introducing SDN 6 at SpringOne 2020 by @meistermeier and @rotnroll666
@Service
public class MovieService {
private final ReactiveNeo4jClient client;
public MovieService(ReactiveNeo4jClient client) {
this.client = client;
}
/# Fully integrated with Springs declarative
/# or explicit transactions (via TransactionalOperator or TransactionTemplate)
@Transactional
public Flux<String> getMovieTitles() {
return client.query("MATCH (m:Movie) RETURN m ORDER BY m.name ASC")
.fetchAs(String.class)
.mappedBy((typeSystem, record) -' record.get("m").asNode().get("title").asString())
.all();
}
}
19
@Autowired
org.springframework.data.neo4j.core.ReactiveNeo4jClient
18. Introducing SDN 6 at SpringOne 2020 by @meistermeier and @rotnroll666 20
@Autowired
org.springframework.data.neo4j.core.ReactiveNeo4jTemplate
@Service
public class MovieService {
private final ReactiveNeo4jTemplate template;
public MovieService(ReactiveNeo4jTemplate template) {
this.template = template;
}
@Transactional
public Flux<String> getMovieTitles() {
/# Overload with custom query available
return template.findAll(MovieEntity.class)
.map(MovieEntity:%getTitle);
}
}
@Node("Movie")
class MovieEntity {
private @Id final String title;
private @Property("tagline") final String description;
@Relationship(type = "ACTED_IN", direction = INCOMING)
private final Map<PersonEntity, Roles> actorsAndRoles;
/# Full immutable class, works also with @Data,
/# Kotlin and JDK 14 records
public MovieEntity(String title, String description,
Map<PersonEntity, Roles> actorsAndRoles
) {
this.title = title;
this.description = description;
this.actorsAndRoles = actorsAndRoles;
}
}
19. Introducing SDN 6 at SpringOne 2020 by @meistermeier and @rotnroll666
public interface MovieRepository extends ReactiveNeo4jRepository<MovieEntity, String> {}
@Service
public class MovieService {
private final MovieRepository repository;
public MovieService(MovieRepository repository) {
this.repository = repository;
}
@Transactional
public Flux<String> getMovieTitles() {
/# Custom query via @Query on a method
/# For simple cases, use derived query methods
/# repository.findAllByOrderByTitle
return repository.findAll()
.map(MovieEntity:%getTitle);
/# For the first time with Spring Data and Neo4j, query by Example
/# return repository.findAll(Example.of(new MovieEntity("The Matrix", null)));
}
}
21
@Autowired org.springframework.data.neo4j.core.ReactiveNeo4jRepository
20. Introducing SDN 6 at SpringOne 2020 by @meistermeier and @rotnroll666
• Instances via
• (persistent) constructor
• wither
• setter
• No reflection violating the
contract
Immutable Mapping
21. Introducing SDN 6 at SpringOne 2020 by @meistermeier and @rotnroll666
• Neo4j Driver Java auto-config
• Properties where they belong
• Updated SDN starter
• Getting you up to speed in a
blink
• Health Checks
• Based on the Java Driver
Spring Boot
integration
22. Introducing SDN 6 at SpringOne 2020 by @meistermeier and @rotnroll666
@ConfigurationProperties(prefix = "spring.neo4j")
public class Neo4jProperties {
public static class Authentication {
/** The login of the user connecting to the database.**
private String username;
/** The password of the user connecting to the database. **
private String password;
/** The realm to connect to.**
private String realm;
}
/** The uri this driver should connect to. The driver supports bolt or neo4j as schemes.**
private URI uri;
/** The authentication the driver is supposed to use. Maybe null. **
private Authentication authentication = new Authentication();
}
24
Access to all configuration settings
Consistent across frameworks!
23. Introducing SDN 6 at SpringOne 2020 by @meistermeier and @rotnroll666
Less Surprises
24. Introducing SDN 6 at SpringOne 2020 by @meistermeier and @rotnroll666
Cypher DSL
• Zero dependency library
• Compile-time checked query
construction
• Re-usable instances
25. Introducing SDN 6 at SpringOne 2020 by @meistermeier and @rotnroll666 27
var tom = node("Person").named("tom").properties("name", literalOf("Tom Hanks"));
var coActors = anyNode("coActors");
var cocoActors = anyNode("cocoActors");
var strength = count(asterisk()).as("Strength");
var statement = Cypher
.match(
tom.relationshipTo(anyNode("m"), "ACTED_IN").relationshipFrom(coActors, "ACTED_IN"),
coActors.relationshipTo(anyNode("m2"), "ACTED_IN").relationshipFrom(cocoActors, "ACTED_IN")
)
.where(not(tom.relationshipTo(anyNode(), "ACTED_IN").relationshipFrom(cocoActors, "ACTED_IN")))
.and(tom.isNotEqualTo(cocoActors))
.returning(
cocoActors.property("name").as("Recommended"),
strength
).orderBy(strength.asName().descending())
.build();
assertThat(cypherRenderer.render(statement))
.isEqualTo(""
+ "MATCH "
+ "(tom:`Person` {name: 'Tom Hanks'})-[:`ACTED_IN`]-$(m)%-[:`ACTED_IN`]-(coActors), "
+ "(coActors)-[:`ACTED_IN`]-$(m2)%-[:`ACTED_IN`]-(cocoActors) "
+ "WHERE (NOT (tom)-[:`ACTED_IN`]-$()%-[:`ACTED_IN`]-(cocoActors) AND tom <( cocoActors) "
+ "RETURN cocoActors.name AS Recommended, count(*) AS Strength ORDER BY Strength DESC");
26. Introducing SDN 6 at SpringOne 2020 by @meistermeier and @rotnroll666 28
Current status
• Works with Neo4j 3.4+
• Reactive database access needs 4.0
• Multi-database feature needs Neo4j Enterprise Edition
• Works with Neo4j Aura https://neo4j.com/aura/
• Provides Neo4j support in JHipster https://www.jhipster.tech
27. Introducing SDN 6 at SpringOne 2020 by @meistermeier and @rotnroll666 29
Current status
• Original version released as SDN/RX in April 2020
• Used already by customers
• Will replace SDN+Neo4j-OGM in Spring Boot 2.4 / Spring Data Release Train
Ockham (2020.0.0)
• SDN+Neo4j-OGM in Spring Boot 2.3 and before still supported and
maintained
• Good choice for new projects
• More effort to migrate existing SDN+Neo4j-OGM code
• Different packages
• More opinionated behavior
28. Introducing SDN 6 at SpringOne 2020 by @meistermeier and @rotnroll666 30
Links
• Announcement of SDN 6.0
https://bit.ly/welcome_sdn6
• Spring Data Neo4j
https://spring.io/projects/spring-data-neo4j
• Neo4j Cypher-DSL
https://github.com/neo4j-contrib/cypher-dsl
• Neo4j Downloads
https://neo4j.org/downloads
• Neo4j Aura
https://neo4j.org/aura
• Neo4j Community Site
https://community.neo4j.com/