LvlUp - Experience Tracking for All
LvlUp is a gamification platform I've been building that enables creators to design learning missions and players to earn XP and achievements. What makes it unique? All progression is stored on an immutable blockchain ledger - cryptographic proof that your accomplishments are real and verifiable.
This project has been a proving ground for everything I believe about software architecture.
The Origin Story
The idea for LvlUp has been rattling around in my head for over a decade. I've always been fascinated by how games motivate people - the dopamine hit of leveling up, the satisfaction of unlocking an achievement, the friendly competition of leaderboards. What if we could apply that same psychology to learning, professional development, or any goal worth pursuing?
The first iterations never quite got off the ground. But when I picked it back up in 2024 with Elixir and Phoenix LiveView, something clicked. The technology finally matched the vision.
Why Elixir and Phoenix?
After years of working across different tech stacks, Elixir feels like coming home. The functional paradigm, pattern matching, the actor model via OTP - it all aligns with how I think about building resilient systems. Phoenix LiveView is particularly elegant: real-time interactive UIs without the JavaScript complexity that often comes with Web Projects.
But more than the language, this project became an exercise in architecture done right.
Hexagonal Architecture, Event Sourcing, and CQRS
I've coached dozens of teams on clean architecture principles. With LvlUp, I wanted to practice what I preach - no excuses, no shortcuts. The result is a codebase that follows Hexagonal Architecture (Ports & Adapters) with Event Sourcing and CQRS.
Six Bounded Contexts, Zero Direct Coupling:
- Mission Builder - Visual editor with event-sourced state
- User Stats - XP tracking, levels, leaderboards
- User Achievements - Unlock tracking and auto-triggers
- Mission Participation - Enrollment and progress
- Blockchain - Immutable ledger for verification
- Identity - Authentication and user management
Each context defines ports (Elixir behaviours/interfaces) and adapters (implementations). The Mission Builder alone has 11 commands, 4 queries, 16 domain events, and a 707-line aggregate root. Contexts communicate exclusively through domain events via Phoenix PubSub - when a player completes a mission, MissionParticipation publishes a MissionCompleted event. UserStats listens and awards XP. UserAchievements checks unlock conditions. Nobody calls anybody directly.
This isn't theoretical architecture astronautics. It's practical: the same port interface that uses PostgreSQL in production uses an in-memory implementation in tests. That's why 499 tests run in under a second.
Testing Strategy: 499 Tests in 0.8 Seconds
Testing isn't an afterthought - it's the backbone of the development process. The test suite is comprehensive:
Test Categories:
- Unit tests - Pure domain logic, no database
- Integration tests - Commands, queries, and adapters working together
- Performance tests - Blockchain operations under load
- Stress tests - Concurrent block additions, duplicate detection
- E2E tests - Full browser automation with Wallaby
The Secret to Speed: In-Memory Adapters
Every port has two adapters: one for production (PostgreSQL) and one for tests (in-memory). The InMemoryEventStore is 5x faster than hitting Postgres. The InMemoryLedgerAdapter lets blockchain tests run without disk I/O. Test isolation comes from Elixir's lightweight processes, not database transactions.
Sample test output:
Running ExUnit with seed: 877485, max_cases: 32
Chain height after stress test: 6
10 of 10 concurrent block additions succeeded
Finished in 0.8 seconds (0.6s async, 0.2s sync)
499 tests, 0 failuresThis kind of test suite enables fearless refactoring. I recently transformed a 738-line JavaScript monolith into 6 focused modules without breaking anything - because the tests caught every regression immediately.
The Mission Builder
The heart of LvlUp is the Mission Builder - a visual editor where creators design learning paths by dragging nodes onto a canvas and connecting them with bezier curves. Think of it like a flowchart for gamified experiences.
Node Types:
- Level - Stages in the journey with customizable content
- XP - Experience point rewards (triggers blockchain recording)
- Achievement - Unlockable badges with rarity tiers
- Event - Triggers and conditions for branching paths
- Fanfare - Celebration effects (confetti, animations)
True Event Sourcing:
The builder doesn't store "current state." It stores every change as an immutable event: NodeAdded, NodeMoved, NodeResized, ConnectionCreated, NodeDeleted. Loading a mission means replaying its event history. This gives you:
- Complete audit trail - See every change ever made
- Time travel - Reconstruct state at any point in history
- Undo/redo - Trivial to implement (just replay fewer events)
- Conflict resolution - Optimistic locking via version numbers
The frontend architecture mirrors the backend. I refactored it from a monolithic 738-line file into 6 focused modules following Hexagonal patterns:
line_geometry.js- Pure domain logic (no DOM)line_renderer.js- SVG rendering (port)line_state_manager.js- State management (port)endpoint_drag_handler.js- Reconnection behavior (adapter)control_point_handler.js- Curve manipulation (adapter)line_handler.js- Thin orchestrator (230 lines)
Blockchain-Backed Progression
Here's where LvlUp gets genuinely interesting.
Most gamification systems store progress in a database. If someone claims they have 10,000 XP, you have to trust them - or trust the database hasn't been tampered with. With LvlUp, every XP award and achievement unlock is recorded on a blockchain ledger with cryptographic hashes linking each block.
How It Works:
Each block contains:
- Index and timestamp
- Hash of the previous block
- List of transactions (XP awards, achievement unlocks)
- Validator signature
- SHA-256 hash of the block contents
The chain is self-validating. Any tampering breaks the hash chain. External systems can hit the public verification API:
GET /api/v1/verify/:user_id
GET /api/v1/verify/:user_id/xp
GET /api/v1/verify/:user_id/achievementsThese endpoints cryptographically confirm that a user's claimed progress is legitimate and hasn't been altered.
This isn't cryptocurrency. It's using blockchain for what it's genuinely good at: immutable, verifiable records. For professional certifications, educational credentials, or any context where proof of accomplishment matters, this is genuinely useful.
Performance Under Load:
The blockchain implementation handles stress gracefully:
- Chain validation: 0.377ms for 150 transactions
- Transaction retrieval: 0.003ms per user
- Duplicate detection: <1ms even with 20+ blocks
- Concurrent writes: 10/10 success rate with proper locking
Code Quality: The Numbers
- Test count: 499
- Test runtime: 0.8 seconds
- Backend modules: 118 Elixir files
- Frontend modules: 32 JavaScript files
- Domain events: 16 types
- Commands (CQRS): 15
- Queries (CQRS): 4
- REST API endpoints: 7
- Bounded contexts: 6
The codebase follows SOLID principles, DRY without premature abstraction, and idiomatic Elixir patterns throughout. Every public function has typespecs. Every module has documentation. The architecture documentation alone spans several detailed markdown files.
What I Learned (Again)
Building LvlUp reinforced lessons I already knew but needed to feel in my hands again:
Tests unlock fearless refactoring. The test suite is my safety net. I can restructure entire modules without anxiety because regressions surface immediately.
Hexagonal Architecture isn't academic. Swapping PostgreSQL for in-memory storage in tests isn't theoretical - it's why my tests are fast. The ports and adapters aren't ceremony; they're practical.
Event Sourcing is powerful but demanding. Replaying events to reconstruct state is elegant until you need to evolve your event schemas. I'm still learning the nuances of event versioning.
LiveView is a game-changer. Building a real-time drag-and-drop canvas editor that stays in sync with the server, without writing REST endpoints or managing WebSocket state manually? That's the future of web development.
Current State and What's Next
The MVP is complete: visual mission builder, user authentication, achievement system with auto-triggers, REST API, admin dashboard, and blockchain integration. 499 tests pass. The architecture is solid.
What's still to come:
- Mission execution flow - Players actually completing missions step by step with API event triggers.
- Undo/redo - The event sourcing makes this straightforward and will give us a replay factor to learn more about the way missions are completed.
- API token authentication - JWT for third-party integrations like Games, I'm building in a full solution for game developers to incorporate XP and Acheivement systems so they can focus on their gameplay and not have to spend a bunch of time re-building XP or Acheivement systems.
- Mobile-responsive design - Tailwind makes this easier and we want a web version for creatives to be able to see details at a glance.
This is a long-term project. I'm building it the way I want to build it, taking the time to do it right. No deadlines, no compromise on quality. In the end I also think i'm solving a problem that few developers want to talk about. I see a lot of potential use cases for this platform.
The Bigger Picture
LvlUp is more than a side project. It's a reference implementation of the architectural principles I coach teams on. When I tell developers to use ports and adapters, I can show them LvlUp. When I explain event sourcing, I can walk through the Mission aggregate. When I advocate for domain events over direct coupling, the codebase demonstrates why.
There's something clarifying about building software for yourself. No stakeholders to negotiate with, no shortcuts demanded by deadlines. Just the craft itself.
If you're interested in the technical details, the architecture documentation in the repo is extensive. If you want to see it in action, check out the live demo at lvlup.liftlockstudios.com.
The above isn't up yet, but its earmarked ... bookmark it and come back from time to time. If you're intersted in testing and providing feedback, shoot me an email with a subject line of "LvlUp".
Level up! 🎮
