A modern link management system built with Java and hexagonal architecture principles.
LinkLift is a RESTful web service for managing web links with comprehensive CRUD operations. It features a clean, maintainable architecture that separates business logic from infrastructure concerns, making it highly testable and adaptable to changing requirements.
- 🔗 Link Management: Create and list web links with metadata
- 🏗️ Clean Architecture: Hexagonal architecture with strict layer separation
- 📊 Pagination & Sorting: Efficient data retrieval with flexible sorting options
- ⚡ Event-Driven: Domain events for loose coupling and extensibility
- 🧪 Comprehensive Testing: Unit, integration, and acceptance tests
- 🛡️ Error Handling: Centralized exception handling with meaningful error codes
- 🔄 Database Agnostic: Repository pattern enables flexible data storage
- Java 17+ - Download here
- Maven 3.8+ - Installation guide
- Docker & Docker Compose - Get Docker
-
Clone the repository
git clone <repository-url> cd linklift
-
Start with Docker Compose (Recommended)
docker-compose up -d
-
Verify installation
curl http://localhost:7070/up # Expected: OK curl http://localhost:7070/api/v1/links # Expected: {"data": {"content": [], ...}, "message": "Links retrieved successfully"}
# 1. Start ArcadeDB
docker run -d --name arcadedb \
-p 2480:2480 -p 2424:2424 \
-e JAVA_OPTS="-Darcadedb.server.rootPassword=playwithdata" \
arcadedata/arcadedb:25.7.1
# 2. Build and run application
mvn clean package
java -jar target/linklift-1.0-SNAPSHOT.jarcurl -X PUT http://localhost:7070/api/v1/link \
-H "Content-Type: application/json" \
-d '{
"url": "https://github.com",
"title": "GitHub",
"description": "The world'\''s leading software development platform"
}'Response:
{
"link": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"url": "https://github.com",
"title": "GitHub",
"description": "The world's leading software development platform",
"extractedAt": "2025-08-15T18:12:39",
"contentType": "text/html"
},
"status": "Link received"
}# Basic listing
curl http://localhost:7070/api/v1/links
# With pagination and sorting
curl "http://localhost:7070/api/v1/links?page=0&size=10&sortBy=title&sortDirection=ASC"Response:
{
"data": {
"content": [...],
"page": 0,
"size": 10,
"totalElements": 25,
"totalPages": 3,
"hasNext": true,
"hasPrevious": false
},
"message": "Links retrieved successfully"
}LinkLift follows Hexagonal Architecture (Ports and Adapters) with clear separation between:
- Domain Layer: Pure business logic and rules
- Application Layer: Use cases and service coordination
- Infrastructure Layer: External adapters (web, database, events)
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ Web Layer │ │ Application │ │ Persistence │
│ (Javalin) │◄───┤ Services ├───►│ (ArcadeDB) │
│ │ │ │ │ │
└─────────────────┘ └─────────────────┘ └─────────────────┘
│
┌─────────────────┐
│ Domain Model │
│ (Entities & │
│ Events) │
└─────────────────┘
- ☕ Java 17: Modern language features and performance
- 🚀 Javalin: Lightweight, fast web framework
- 🗄️ ArcadeDB: Multi-model database (Graph, Document, Key-Value)
- 🔨 Maven: Build automation and dependency management
- 🧪 JUnit 5: Modern testing framework with comprehensive assertions
- 🐳 Docker: Containerization for consistent environments
After starting the application with Docker Compose, you can access the web UI at:
http://localhost:80
Or simply:
http://localhost
The web interface allows you to view and add new links through a user-friendly React interface.
The web UI is built with React. If you want to develop the frontend separately:
# Navigate to the webapp directory
cd webapp
# Install dependencies
npm install
# Start development server
npm start
# Run tests
npm test
# Build for production
npm run buildThe React app includes:
- Component tests with React Testing Library
- API mocking for isolated testing
- Material UI for component styling
| Method | Endpoint | Description | Status |
|---|---|---|---|
| GET | /up |
Health check | ✅ |
| PUT | /api/v1/link |
Create new link | ✅ |
| GET | /api/v1/links |
List links (paginated) | ✅ |
| Parameter | Type | Default | Description |
|---|---|---|---|
page |
Integer | 0 | Page number (0-based) |
size |
Integer | 20 | Items per page (max: 100) |
sortBy |
String | "extractedAt" | Sort field: id, url, title, description, extractedAt, contentType |
sortDirection |
String | "DESC" | Sort direction: ASC, DESC |
All errors return a consistent JSON format:
{
"status": 400,
"code": 1001,
"message": "Validation error",
"fieldErrors": {
"url": "URL cannot be empty"
},
"path": "/api/v1/link",
"timestamp": "2025-08-15T18:12:39"
}Common HTTP Status Codes:
200- Success201- Created400- Bad Request (validation errors)409- Conflict (duplicate URL)500- Internal Server Error