A highly scalable, secure distributed ledger system built with hexagonal architecture principles. The system implements a robust money transfer mechanism with optimistic locking, double-entry bookkeeping, and comprehensive audit trails.
This project follows Hexagonal Architecture (Ports & Adapters) pattern with clear separation of concerns:
- Domain Layer: Pure business logic, framework-agnostic
- Application Layer: Use case orchestration
- Infrastructure Layer: Framework and external system adapters
- Language: Java 21
- Framework: Spring Boot 3.5.9
- Database: PostgreSQL 16 with optimistic locking
- Cache: Redis 7
- Messaging: Apache Kafka
- Migration: Flyway
- Build: Gradle 8.x
- Testing: JUnit 5, Testcontainers, K6
- Java 21 or higher
- Docker & Docker Compose
-
Create Environment File: Create a
.envfile in the root directory with the following content:SPRING_PROFILES_ACTIVE=dev DB_PORT=5432 DB_NAME=distributed_ledger DB_USER=ledger_user DB_PASSWORD=ledger_pass REDIS_PORT=6379 REDIS_PASSWORD=redis_pass ENCRYPTION_KEY=12345678901234567890123456789012 JWT_SECRET=dev-secret-key-must-be-at-least-256-bits-long-for-security JWT_EXPIRATION_MS=86400000 ADMIN_USER=admin ADMIN_PASSWORD=password LOG_LEVEL=info PGADMIN_EMAIL=admin@ledger.local PGADMIN_PASSWORD=admin
-
Start infrastructure services:
docker-compose up -d- Build the application:
./gradlew build- Run the application:
./gradlew bootRun- Application:
http://localhost:8080 - PostgreSQL:
localhost:5432(user:ledger_user, password:ledger_pass, db:distributed_ledger) - Redis:
localhost:6379 - Kafka:
localhost:9092 - Kafka UI:
http://localhost:8090
Health Check:
curl http://localhost:8080/api/v1/healthSend Money (requires existing accounts in database):
curl -X POST http://localhost:8080/api/v1/transactions/send \
-H "Content-Type: application/json" \
-u admin:password \
-d '{
"fromAccountId": "uuid-of-source-account",
"toAccountId": "uuid-of-target-account",
"amount": 100.00,
"currency": "USD",
"reference": "TXN-001"
}'distributed-ledger/
├── docs/
│ ├── adr/ # Architecture Decision Records
│ │ ├── 001-init-structure.md
│ │ ├── 002-choose-postgres-over-mongo.md
│ │ ├── 003-adoption-of-optimistic-locking.md
│ │ └── 004-implementation-of-mappers.md
│ └── c4-diagrams/ # System architecture diagrams
├── src/main/java/com/distributed/ledger/
│ ├── domain/ # Core business logic (framework-independent)
│ │ ├── model/ # Entities, Value Objects
│ │ │ ├── Account.java # Account aggregate with business logic
│ │ │ ├── AccountId.java # Value object for account identity
│ │ │ ├── AccountStatus.java # Account status enum
│ │ │ ├── Money.java # Money value object with currency
│ │ │ ├── Transaction.java # Transaction aggregate
│ │ │ ├── TransactionType.java
│ │ │ └── TransactionStatus.java
│ │ ├── event/ # Domain Events
│ │ │ └── DomainEvent.java
│ │ ├── exception/ # Domain Exceptions
│ │ │ └── DomainException.java
│ │ └── port/ # Ports (Interfaces)
│ │ ├── in/ # Use Case Interfaces
│ │ │ ├── UseCase.java
│ │ │ ├── SendMoneyUseCase.java
│ │ │ └── SendMoneyCommand.java
│ │ └── out/ # Repository/External Service Interfaces
│ │ ├── LoadAccountPort.java
│ │ ├── SaveAccountPort.java
│ │ └── SaveTransactionPort.java
│ ├── application/ # Use case orchestration
│ │ ├── service/ # Use Case Implementations
│ │ │ └── SendMoneyService.java # Money transfer with retry logic
│ │ └── dto/ # Data Transfer Objects
│ │ └── BaseDTO.java
│ └── infrastructure/ # Adapters (Spring Boot, DB, Kafka)
│ ├── adapter/
│ │ ├── web/ # REST Controllers
│ │ │ ├── HealthController.java
│ │ │ ├── SendMoneyController.java
│ │ │ ├── dto/
│ │ │ │ ├── SendMoneyRequest.java
│ │ │ │ └── ApiErrorResponse.java
│ │ │ └── exception/
│ │ │ └── GlobalExceptionHandler.java
│ │ ├── persistence/ # JPA Repositories, Entity Mappers
│ │ │ ├── entity/
│ │ │ │ ├── AccountEntity.java # JPA entity with @Version
│ │ │ │ └── TransactionEntity.java
│ │ │ ├── mapper/
│ │ │ │ ├── AccountMapper.java
│ │ │ │ └── TransactionMapper.java
│ │ │ ├── repository/
│ │ │ │ ├── SpringDataAccountRepository.java
│ │ │ │ └── SpringDataTransactionRepository.java
│ │ │ ├── AccountPersistenceAdapter.java
│ │ │ └── TransactionPersistenceAdapter.java
│ │ └── messaging/ # Kafka Adapters (placeholder)
│ └── config/ # Spring Configurations
│ ├── RedisConfig.java
│ └── SecurityConfig.java
├── tests/
│ ├── k6/ # Load test scripts
│ └── integration/ # Integration tests (Testcontainers)
│ ├── IntegrationTestBase.java
│ └── MoneyTransferIntegrationTest.java
└── docker-compose.yml
- ✅ Account: Aggregate with deposit/withdraw logic and validation
- ✅ Money: Value object with currency and arithmetic operations
- ✅ Transaction: Transfer, Deposit, Withdrawal support
- ✅ Domain Validation: Business rule enforcement at domain level
- ✅ Send Money: Complete money transfer implementation
- Atomic withdraw + deposit operations
- Transaction state management
- Retry mechanism for optimistic locking failures
- ✅ Optimistic Locking: Version-based concurrency control
- ✅ Double-Entry Bookkeeping: Schema ready (ledger_entries table)
- ✅ Audit Log: Complete audit trail support
- ✅ Mappers: Bidirectional domain ↔ entity mapping
- ✅
POST /api/v1/money/send- Transfer money between accounts - ✅
GET /api/v1/health- Health check endpoint - ✅ Global exception handling with detailed error responses
- ✅ Concurrency: Handled with optimistic locking + @Retryable
- ✅ ACID Compliance: @Transactional boundaries
- ✅ Idempotency: Transaction reference support
- ✅ Testing: Integration tests with Testcontainers
-
Defense in Depth
- Multi-layered security approach
- Input validation at all boundaries
- Output encoding to prevent injection attacks
-
Least Privilege
- Database users have minimal required permissions
- Service accounts with restricted access
- Role-based access control (RBAC)
-
Secure by Default
- All endpoints require authentication by default
- HTTPS enforced in production
- Secure password policies
- HTTP Basic Authentication
- Role-based access control (Admin role)
- Session management with Redis
- OAuth2/OpenID Connect support (Planned)
- Encryption at Rest: PostgreSQL with encryption
- Encryption in Transit: TLS 1.3 for all communications
- Sensitive Data Masking: PII fields masked in logs
- Secure Key Management: External secrets management (Vault/AWS Secrets Manager)
- Comprehensive audit logging
- Transaction traceability
- Anomaly detection
- Real-time security monitoring
- Right to Access: User data export functionality
- Right to Erasure: Secure data deletion with audit trail
- Data Minimization: Only necessary data collected
- Purpose Limitation: Clear data usage policies
- Data Portability: Standard export formats
- Personal data classification
- Retention policies enforced
- Pseudonymization where applicable
- Cross-border transfer safeguards
- ✅ No secrets in source code
- ✅ Dependency vulnerability scanning
- ✅ SAST/DAST in CI/CD pipeline
- ✅ Code review mandatory
- ✅ Security testing in development
- ✅ Regular security patches
- ✅ Automated backup and recovery
- ✅ Incident response plan
- ✅ DDoS protection
- ✅ Rate limiting on APIs
Track and remediate vulnerabilities promptly:
- Check
docs/security/cve-tracking.mdfor current status - Run
./gradlew dependencyCheckAnalyzefor security scan
Please DO NOT file public issues for security vulnerabilities.
Email: security@yourdomain.com
./gradlew test./gradlew integrationTestcd tests/k6
k6 run load-test.js- Metrics: Micrometer + Prometheus
- Logging: SLF4J + Logback
- Tracing: OpenTelemetry
- Health Checks: Spring Boot Actuator
- README.md - This file, project overview
- API_DOCUMENTATION.md - Complete API reference with examples
- IMPLEMENTATION_SUMMARY.md - What's implemented, metrics, status
- CONTRIBUTING.md - How to contribute
- PROJECT_STRUCTURE.md - Directory structure and organization
- SETUP_COMPLETE.md - Setup guide and next steps
- ADR-001 - Hexagonal architecture
- ADR-002 - PostgreSQL choice
- ADR-003 - Optimistic locking strategy
- ADR-004 - Mapper implementation
- ADR-005 - Transactional Outbox Pattern
- Architecture Decisions:
docs/adr/(4 ADRs documenting major decisions) - Domain Models:
domain/model/Account.java- Account aggregatedomain/model/Transaction.java- Transaction aggregatedomain/model/Money.java- Money value object
- Use Case Implementation:
application/service/SendMoneyService.java - REST API:
infrastructure/adapter/web/SendMoneyController.java - Database Schema:
src/main/resources/db/migration/V1__initial_schema.sql - Mappers:
infrastructure/adapter/persistence/mapper/AccountMapper.javainfrastructure/adapter/persistence/mapper/TransactionMapper.java
- Tests:
src/test/java/com/distributed/ledger/integration/MoneyTransferIntegrationTest.java
- Hexagonal Architecture: Domain is pure Java, infrastructure implements ports
- Optimistic Locking: Concurrent updates handled with @Version + @Retryable
- Value Objects: Money and AccountId prevent primitive obsession
- Mappers: Manual bidirectional conversion between domain and entities
- ACID Transactions: @Transactional ensures atomicity
domain/ # Business logic - start here
application/service/ # Use case implementations
infrastructure/
adapter/web/ # REST API endpoints
adapter/persistence/ # Database layer
entity/ # JPA entities
mapper/ # Domain ↔ Entity conversion
repository/ # Spring Data repositories
docs/adr/ # Architecture decisions - read these!