A scalable and reusable shared ledger system designed for tracking user credits across multiple applications in a monorepo.
- Features
- Architecture Documentation
- System Architecture
- Technical Stack
- Project Structure
- Getting Started
- Development
- API Reference
- Database Management
- Troubleshooting
- Shared core ledger functionality for credit management
- Type-safe operation handling with Pydantic models
- Extensible architecture for application-specific operations
- Asynchronous database operations with SQLAlchemy
- Comprehensive API endpoints with FastAPI
- Database migration support using Alembic
- Docker for development environment
- Unit tests with pytest
For a detailed view of the system architecture, including class diagrams, sequence diagrams, and component diagrams, please see our Architecture Documentation.
classDiagram
class LedgerEntry {
+int id
+str operation
+str owner_id
+int amount
+str nonce
+datetime created_at
+datetime updated_at
}
class LedgerOperationType {
+DAILY_REWARD
+SIGNUP_CREDIT
+CREDIT_SPEND
+CREDIT_ADD
}
class BaseLedgerOperations {
+Dict BASE_CONFIG
+get_operation_config()
+validate_operations()
}
class ExampleAppOperations {
+Dict APP_CONFIG
+get_operation_config()
}
BaseLedgerOperations <|-- ExampleAppOperations
LedgerEntry --> LedgerOperationType : uses
%%{init: {'theme': 'base', 'themeVariables': {
'primaryColor': '#BB2528',
'primaryTextColor': '#fff',
'primaryBorderColor': '#7C0000',
'lineColor': '#F8B229',
'secondaryColor': '#006100',
'tertiaryColor': '#fff'
}}}%%
sequenceDiagram
participant Client
participant FastAPI
participant LedgerRouter
participant OperationProcessor
participant Database
Client->>FastAPI: POST /ledger/entry
FastAPI->>LedgerRouter: process_request()
LedgerRouter->>OperationProcessor: validate_operation()
OperationProcessor->>Database: check_duplicate_nonce()
Database-->>OperationProcessor: result
OperationProcessor->>Database: get_current_balance()
Database-->>OperationProcessor: balance
OperationProcessor->>Database: save_entry()
Database-->>OperationProcessor: success
OperationProcessor-->>LedgerRouter: operation_result
LedgerRouter-->>FastAPI: response
FastAPI-->>Client: 200 OK
graph TB
subgraph "Example App"
A[FastAPI App]
B[App Router]
C[App Operations]
end
subgraph "Core Ledger System"
D[Ledger Router]
E[Operation Processor]
F[Models & Schemas]
end
subgraph "Database"
G[PostgreSQL]
H[Alembic Migrations]
end
A --> B
B --> C
B --> D
D --> E
E --> F
F --> G
H --> G
stateDiagram-v2
[*] --> Received
Received --> Validating: Check Operation
Validating --> CheckingNonce: Valid Operation
Validating --> Failed: Invalid Operation
CheckingNonce --> CheckingBalance: Unique Nonce
CheckingNonce --> Failed: Duplicate Nonce
CheckingBalance --> Processing: Sufficient Balance
CheckingBalance --> Failed: Insufficient Balance
Processing --> Completed: Save Entry
Processing --> Failed: Database Error
Completed --> [*]
Failed --> [*]
- Python: ≥ 3.10 (Currently using 3.13)
- Web Framework: FastAPI
- ORM: SQLAlchemy 2.0
- Data Validation: Pydantic v2
- Database: PostgreSQL 16
- Migration Tool: Alembic
- Testing: pytest, pytest-asyncio
- Development Tools:
- Docker & Docker Compose (I highly suggest Orbstack)
- pgAdmin 4
- uvicorn (ASGI server)
shared-ledger-system/
├── alembic/ # Database migrations
├── apps/ # Application modules
│ └── example_app/ # Example application using the shared ledger
│ ├── api/ # FastAPI routes and dependencies
│ ├── models/ # App-specific database models
│ └── schemas/ # App-specific Pydantic models
├── core/ # Core shared ledger functionality
│ └── shared_ledger/ # Shared ledger implementation
│ ├── models/ # Core database models
│ ├── schemas/ # Core Pydantic models
│ ├── operations/ # Ledger operations
│ └── utils/ # Shared utilities
├── tests/ # Test suite
├── alembic.ini # Alembic configuration
├── docker-compose.yml # Docker services configuration
└── setup.py # Package configuration
The project uses setup.py for package management and installation. This configuration is crucial for:
- Installing the project as a package
- Managing dependencies
- Ensuring proper module imports
setup.py Configuration
from setuptools import find_namespace_packages, setup
setup(
name="shared-ledger-system",
version="0.1.0",
packages=find_namespace_packages(include=["core.*", "apps.*"]),
install_requires=[
"fastapi>=0.115.6",
"uvicorn>=0.34.0",
"sqlalchemy>=2.0.37",
"pydantic>=2.10.5",
"alembic>=1.14.1",
"asyncpg>=0.30.0",
],
extras_require={
"test": [
"pytest",
"pytest-asyncio",
"httpx",
],
},
)-
Package Name and Version:
- Name: "shared-ledger-system"
- Version: "0.1.0"
-
Package Discovery:
- Uses
find_namespace_packagesto automatically find all packages - Includes both
core.*andapps.*namespaces - This ensures proper module imports throughout the project
- Uses
-
Dependencies:
- Core dependencies in
install_requires:- FastAPI for API framework
- uvicorn for ASGI server
- SQLAlchemy for ORM
- Pydantic for data validation
- Alembic for migrations
- asyncpg for async PostgreSQL support
- Test dependencies in
extras_require:- pytest for testing framework
- pytest-asyncio for async test support
- httpx for async HTTP client
- Core dependencies in
-
Development Installation:
pip install -e .This installs the package in editable mode, allowing you to modify the code without reinstalling.
-
Installing with Test Dependencies:
pip install -e ".[test]"This installs both the package and test dependencies.
-
Why We Need It:
- Enables Python to treat the project as a proper package
- Ensures correct module imports (e.g.,
from core.shared_ledger import ...) - Manages project dependencies consistently
- Supports namespace packages for modular architecture
- Python 3.10 or higher
- Docker and Docker Compose
- Git
- PostgreSQL client (optional, for direct database access)
- Make (optional, for using Makefile commands)
-
Clone the repository:
git clone <repository-url> cd shared-ledger-system
-
Start the database containers:
docker-compose up -d
This will automatically create and start:
- Development database (ledger_db) on port 5432
- Test database (test_ledger_db) on port 5433
- pgAdmin on port 5050
Wait for the databases to be ready. You can check their status with:
docker-compose ps
All containers should show as "running" and healthy before proceeding.
-
Create a virtual environment and activate it:
python -m venv venv source venv/bin/activate # On Windows: .\venv\Scripts\activate
-
Install the package in development mode:
pip install -e . -
Apply migrations:
alembic upgrade head
-
Start the FastAPI server:
uvicorn apps.example_app.main:app --reload
-
Access the API documentation:
- Swagger UI: http://localhost:8000/docs
- ReDoc: http://localhost:8000/redoc
Ensure all containers are running and healthy before running tests:
# Check container status
docker-compose ps
# Run all tests with verbose output
pytest -v
# Run specific test file
pytest tests/test_api_endpoints.py -v
# Run tests with coverage report
pytest --cov=core --cov=appsIf you encounter database errors, wait a few seconds for the databases to be fully initialized and try again.
The example app provides the following endpoints:
GET /health: Check API health status
GET /balance/{owner_id}: Get balance for an ownerPOST /ledger: Create a new ledger entry
POST /content: Create content (requires credits)POST /content/{content_id}/access: Access content (requires credits)
For detailed API documentation, including request/response schemas and examples, visit:
- Swagger UI: http://localhost:8000/docs
- ReDoc: http://localhost:8000/redoc
- Host: ledger_db
- Port: 5432
- Database: ledger_db
- Username: postgres
- Password: postgres
- Host: ledger_test_db
- Port: 5433
- Database: test_ledger_db
- Username: postgres
- Password: postgres
- URL: http://localhost:5050
- Email: admin@admin.com
- Password: admin
-
Add New Server:
- Name: Ledger DB (or any name you prefer)
- Host: localhost
- Port: 5432 (development) or 5433 (test)
- Database: ledger_db or test_ledger_db
- Username: postgres
- Password: postgres
-
Important Notes:
- Use 'localhost' when connecting from your local machine
- Use 'postgres' when connecting from inside Docker containers
-
Common Connection Errors:
- "connection refused": Ensure Docker containers are running (
docker-compose ps) - "password authentication failed": Check the password (default is 'postgres')
- If databases aren't ready, wait a few seconds and try again
- "connection refused": Ensure Docker containers are running (
-
Database Management Commands:
# View container logs docker-compose logs postgres docker-compose logs postgres_test # Reset databases (removes all data) docker-compose down -v docker-compose up -d # Restart specific container docker-compose restart postgres_test
-
Migration Issues:
# Reset migration state alembic stamp head # Create new migration alembic revision --autogenerate -m "description" # Apply migrations alembic upgrade head
-
Database Connection:
- Ensure test database container is running and healthy
- Check if test database exists and is accessible
- Verify database connection settings in test configuration
-
Transaction Errors:
- Ensure no other tests are running
- Try restarting the test database container
- Check for proper cleanup in test fixtures