This project started with a simple question: what is the right way to represent connections between people from a technical standpoint?
The answer was to use two databases at the same time. MongoDB handles user data, and Neo4j handles the connections between them. Each technology doing what it was built to do, without forcing anything.
The result is an API where you can create users, connect them as friends, and get recommendations of new connections based on mutual friends. That "people you may know" feature you see on every social network, implemented the way large platforms actually do it under the hood.
Spring Boot 3.5 with Java 21. MongoDB to store user documents. Neo4j to store and traverse the relationship graph. Docker Compose spins everything up automatically, so you don't need to install anything other than Docker.
With Docker running on your machine, just execute:
./mvnw spring-boot:runSpring Boot takes care of starting MongoDB and Neo4j through Docker Compose. The application will be available at http://localhost:8080.
Create a user
POST /users
Body: { "username": "john" }
List all users
GET /users
Add a friend
POST /users/{id}/friends
Body: { "friendId": "abc123" }
Get a user's friends
GET /users/{id}/friends
Get friend recommendations
GET /users/{id}/recommendations
MongoDB is great for fetching documents by ID, which makes it perfect for user profiles. But once you start representing relationships in it, you end up nesting arrays inside arrays and it gets messy fast.
Neo4j was designed exactly for this. Relationships are first-class citizens, and the Cypher query language lets you traverse the graph in a clean and efficient way.
This is the heart of the project, the recommendation query:
MATCH (u:User {id: $userId})-[:FRIENDS]->(f1:User)-[:FRIENDS]->(f2:User)
WHERE NOT (u)-[:FRIENDS]->(f2) AND u.id <> f2.id
RETURN DISTINCT f2
LIMIT 10Three lines that walk the graph and find friends of friends you haven't connected with yet. Replicating this in a relational or document database would be far more verbose and less performant.
UserController
│
UserService
│
├── UserDocumentRepository → MongoDB (user data)
└── UserGraphRepository → Neo4j (friendship graph)
The service is the meeting point between the two worlds. It fetches the IDs from the graph and then hydrates the full documents from MongoDB, keeping each database responsible for what it does best.