A production-ready Todo List REST API built with Rust, demonstrating Clean Architecture principles. This project showcases best practices for structuring scalable, maintainable, and testable Rust applications.
This project strictly follows Clean Architecture (also known as Hexagonal/Onion Architecture), ensuring:
- Independence from frameworks – Business logic doesn't depend on web frameworks
- Testability – Core logic can be tested without UI, database, or external services
- Independence from UI – The UI can change without affecting business rules
- Independence from database – Business rules aren't bound to a specific database
- Independence from external agencies – Business rules don't know anything about the outside world
┌─────────────────────────────────────────────────────────────────────────────┐
│ Bootstrap Layer │
│ (Composition Root & Entry Point) │
├─────────────────────────────────────────────────────────────────────────────┤
│ Interfaces Layer │
│ (HTTP Handlers, DTOs, Routes, Middleware) │
├──────────────────────────────┬──────────────────────────────────────────────┤
│ Infrastructure Layer │ Application Layer │
│ (Repos, DB, External APIs) │ (Use Cases, Ports/Interfaces) │
├──────────────────────────────┴──────────────────────────────────────────────┤
│ Domain Layer │
│ (Entities, Value Objects, Domain Errors) │
└─────────────────────────────────────────────────────────────────────────────┘
| Layer | Crate | Responsibility |
|---|---|---|
| Domain | crates/domain |
Core business entities, value objects, IDs, and domain errors. Zero external dependencies (except utilities). |
| Application | crates/application |
Use cases (business logic orchestration), ports (trait definitions for repositories/services). Depends only on Domain. |
| Infrastructure | crates/infrastructure |
Concrete implementations of ports: PostgreSQL repositories, JWT service, Argon2 password hashing, etc. |
| Interfaces | crates/interfaces |
HTTP layer: Actix-web handlers, DTOs, routes, middleware, OpenAPI documentation. |
| Bootstrap | crates/bootstrap |
Application composition root. Wires all dependencies and starts the server. |
Bootstrap → Interfaces → Application → Domain
↓ ↑
Infrastructure ──────┘
- Domain has no dependencies on other layers
- Application depends only on Domain
- Infrastructure implements Application's ports (interfaces)
- Interfaces uses Application's use cases
- Bootstrap composes everything together
-
User Authentication
- Registration with email/password
- Login with JWT access & refresh tokens
- Logout (session revocation)
- Token refresh
- Get current user profile
-
Todo Management
- Create, list, update, delete todos
- Toggle completion status
- User-scoped todos (isolation)
-
Security
- Argon2 password hashing
- JWT-based authentication
- SHA-256 refresh token hashing
- Session management with revocation support
-
API Documentation
- OpenAPI/Swagger UI at
/swagger-ui/
- OpenAPI/Swagger UI at
| Category | Technology |
|---|---|
| Language | Rust 1.92+ (Edition 2024) |
| Web Framework | Actix-web 4.x |
| Async Runtime | Tokio |
| Database | PostgreSQL with SQLx |
| Authentication | JWT (jsonwebtoken) |
| Password Hashing | Argon2 |
| API Docs | utoipa + Swagger UI |
| Error Handling | thiserror |
- Rust 1.92.0 or later
- PostgreSQL 14+
- SQLx CLI (for migrations)
# Install SQLx CLI
cargo install sqlx-cli --no-default-features --features postgres- Clone the repository
git clone https://github.com/hungdv98/clean_architecture_todolist.git
cd clean_architecture_todolist- Configure environment variables
Create a .env file in the project root:
# Server
BIND_ADDR=127.0.0.1:8080
# Database
DATABASE_URL=postgres://user:password@localhost:5432/todolist
# JWT Configuration
JWT_SECRET=your-super-secret-key-at-least-32-characters
JWT_ISSUER=todoapp
# Token TTL
ACCESS_TTL_MINUTES=15
REFRESH_TTL_DAYS=7- Create database and run migrations
# Create database
sqlx database create
# Run migrations
sqlx migrate run- Build and run
# Development
cargo run
# Production
cargo build --release
./target/release/todoappThe server will start at http://127.0.0.1:8080.
| Method | Endpoint | Description |
|---|---|---|
| POST | /v1/auth/register |
Register a new user |
| POST | /v1/auth/login |
Login and receive tokens |
| POST | /v1/auth/logout |
Logout and revoke session |
| POST | /v1/auth/refresh |
Refresh access token |
| GET | /v1/auth/me |
Get current user info |
| Method | Endpoint | Description |
|---|---|---|
| POST | /v1/todos |
Create a new todo |
| GET | /v1/todos |
List all user's todos |
| PATCH | /v1/todos/{id} |
Update a todo |
| PATCH | /v1/todos/{id}/done |
Toggle completion status |
| DELETE | /v1/todos/{id} |
Delete a todo |
- Swagger UI:
http://localhost:8080/swagger-ui/ - OpenAPI JSON:
http://localhost:8080/api-docs/openapi.json
clean_architecture_todolist/
├── Cargo.toml # Workspace configuration
├── .env # Environment variables (not in repo)
├── migrations/ # SQLx database migrations
│ └── 20260118043913_init.sql
└── crates/
├── domain/ # Domain Layer
│ └── src/
│ ├── entities/ # Todo, User
│ ├── ids/ # TodoId, UserId (typed IDs)
│ ├── value_objects/ # Email, PasswordHash, TodoTitle
│ └── error.rs # Domain errors
│
├── application/ # Application Layer
│ └── src/
│ ├── ports/ # Repository & service traits
│ ├── usecases/
│ │ ├── auth/ # Register, Login, Logout, etc.
│ │ └── todos/ # CRUD operations
│ └── error.rs # Application errors
│
├── infrastructure/ # Infrastructure Layer
│ └── src/
│ ├── db/ # Database connection pool
│ ├── repos/ # SQLx repository implementations
│ ├── security/ # JWT, Argon2, SHA256 implementations
│ └── clock/ # System clock implementation
│
├── interfaces/ # Interfaces Layer
│ └── src/http/
│ ├── handlers/ # HTTP request handlers
│ ├── dto/ # Request/Response DTOs
│ ├── middleware/ # Auth middleware
│ ├── routes.rs # Route configuration
│ ├── state.rs # Application state
│ └── openapi.rs # Swagger documentation
│
└── bootstrap/ # Bootstrap Layer
└── src/
├── main.rs # Entry point
├── config.rs # Configuration loading
└── wiring.rs # Dependency injection
The Clean Architecture enables comprehensive testing at each layer:
# Run all tests
cargo test
# Run tests for a specific crate
cargo test -p domain
cargo test -p application| Layer | Test Type | What to Test |
|---|---|---|
| Domain | Unit | Entity behavior, value object validation |
| Application | Unit/Integration | Use case logic with mocked ports |
| Infrastructure | Integration | Repository implementations against test DB |
| Interfaces | Integration | HTTP handlers, request/response serialization |
# Format code
cargo fmt
# Lint
cargo clippy
# Check without building
cargo check
# Build documentation
cargo doc --open