A multi-language repository demonstrating Clean Architecture principles with practical implementations. Each language implementation follows the same domain model and architecture patterns, making it easy to compare approaches across different ecosystems.
This project is designed to be a starting point for your own projects.
Use this repository as a template or boilerplate to kickstart your applications with Clean Architecture (also known as Hexagonal Architecture or Ports & Adapters) already in place. Each implementation provides a minimal, production-ready structure that you can extend and customize for your specific needs.
The goal is to help developers:
- Start new projects quickly with a proven architecture pattern
- Understand Clean Architecture through practical, working examples
- Compare implementations across different programming languages
- Learn best practices for structuring maintainable applications
-
- Framework: Express.js
- Database: MongoDB
- Features: Full async/await support with native MongoDB driver
-
- Framework: FastAPI
- Database: MongoDB (PyMongo)
- Features: Async support with sync-to-async decorator pattern (prototyping approach)
-
- Framework: Axum
- Database: MongoDB
- Features: Full async/await support with async-trait for trait objects
All implementations follow the same Clean Architecture structure:
Entity -> Port -> Usecase -> Infrastructure -> Controller
- Domain: Entities and Value Objects with business rules
- Application: Use Cases and Ports (interfaces)
- Infrastructure: Concrete implementations (MongoDB repositories)
- Presentation: Controllers and HTTP handlers
All implementations share the same domain model:
- User: Represents a user in the system
- ID: Sequential integer identifier
- PhoneNumber: 10-digit phone number
- Sex: Male or Female
- Role: Admin, User, or Guest
- Age: Integer from 0 to 120
- CreateUser: Creates a new user with validation
- MongoDB running on
localhost:27017(or update connection strings in config files)
Single command Node.js (using wget):
wget -qO- https://raw.githubusercontent.com/F4RAN/Clean-Code-Schema/main/bootstrap-node.sh | bashSingle command Python (using wget):
wget -qO- https://raw.githubusercontent.com/F4RAN/Clean-Code-Schema/main/bootstrap-python.sh | bashSingle command Rust (using wget):
wget -qO- https://raw.githubusercontent.com/F4RAN/Clean-Code-Schema/main/bootstrap-rust.sh | bashIf you prefer using git (only downloads the selected language directory):
# Node.js
git clone --filter=blob:none --sparse https://github.com/F4RAN/Clean-Code-Schema.git && cd Clean-Code-Schema && git sparse-checkout init --cone && git sparse-checkout set node && cd node && bash install.sh
# Python
git clone --filter=blob:none --sparse https://github.com/F4RAN/Clean-Code-Schema.git && cd Clean-Code-Schema && git sparse-checkout init --cone && git sparse-checkout set python && cd python && bash install.sh
# Rust
git clone --filter=blob:none --sparse https://github.com/F4RAN/Clean-Code-Schema.git && cd Clean-Code-Schema && git sparse-checkout init --cone && git sparse-checkout set rust && cd rust && bash install.shNote: After installation, you can customize the code before running. To start the server:
- Node.js:
npm start(runs on port 3000) - Python:
source .venv/bin/activate && uvicorn main:app --reload(runs on port 8000) - Rust:
cargo run(runs on port 3000)
Each language implementation has its own README with detailed setup instructions:
All implementations expose the same API endpoint and can be tested using the same curl commands. The only difference is the port number:
- Node.js:
http://localhost:3000 - Python:
http://localhost:8000(FastAPI default) - Rust:
http://localhost:3000
# Node.js or Rust (port 3000)
curl -X POST http://localhost:3000/create_user \
-H "Content-Type: application/json" \
-d '{
"phone_number": "1234567890",
"role": "user",
"age": 25,
"sex": "male"
}'
# Python (port 8000)
curl -X POST http://localhost:8000/create_user \
-H "Content-Type: application/json" \
-d '{
"phone_number": "1234567890",
"role": "user",
"age": 25,
"sex": "male"
}'# Node.js or Rust
curl -X POST http://localhost:3000/create_user -H "Content-Type: application/json" -d '{"phone_number":"1234567890","role":"user","age":25,"sex":"male"}'
# Python
curl -X POST http://localhost:8000/create_user -H "Content-Type: application/json" -d '{"phone_number":"1234567890","role":"user","age":25,"sex":"male"}'# Node.js or Rust
curl -X POST http://localhost:3000/create_user \
-H "Content-Type: application/json" \
-d '{"phone_number":"9876543210","role":"admin","age":30,"sex":"female"}'
# Python
curl -X POST http://localhost:8000/create_user \
-H "Content-Type: application/json" \
-d '{"phone_number":"9876543210","role":"admin","age":30,"sex":"female"}'All implementations return 400 Bad Request for validation errors:
# Node.js or Rust
curl -X POST http://localhost:3000/create_user \
-H "Content-Type: application/json" \
-d '{"phone_number":"123","role":"user","age":25,"sex":"male"}'
# Python
curl -X POST http://localhost:8000/create_user \
-H "Content-Type: application/json" \
-d '{"phone_number":"123","role":"user","age":25,"sex":"male"}'# Node.js or Rust
curl -X POST http://localhost:3000/create_user \
-H "Content-Type: application/json" \
-d '{"phone_number":"1234567890","role":"user","age":150,"sex":"male"}'
# Python
curl -X POST http://localhost:8000/create_user \
-H "Content-Type: application/json" \
-d '{"phone_number":"1234567890","role":"user","age":150,"sex":"male"}'# Node.js or Rust
curl -X POST http://localhost:3000/create_user \
-H "Content-Type: application/json" \
-d '{"phone_number":"1234567890","role":"invalid_role","age":25,"sex":"male"}'
# Python
curl -X POST http://localhost:8000/create_user \
-H "Content-Type: application/json" \
-d '{"phone_number":"1234567890","role":"invalid_role","age":25,"sex":"male"}'# Node.js or Rust
curl -X POST http://localhost:3000/create_user \
-H "Content-Type: application/json" \
-d '{"phone_number":"1234567890","role":"user","age":25,"sex":"invalid"}'
# Python
curl -X POST http://localhost:8000/create_user \
-H "Content-Type: application/json" \
-d '{"phone_number":"1234567890","role":"user","age":25,"sex":"invalid"}'If you have jq installed, you can pretty-print the JSON response:
# Node.js or Rust
curl -X POST http://localhost:3000/create_user \
-H "Content-Type: application/json" \
-d '{"phone_number":"1234567890","role":"user","age":25,"sex":"male"}' | jq .
# Python
curl -X POST http://localhost:8000/create_user \
-H "Content-Type: application/json" \
-d '{"phone_number":"1234567890","role":"user","age":25,"sex":"male"}' | jq .We welcome contributions for additional language implementations! All contributions must follow the same story and development sequence as the existing implementations.
Every implementation in this repository follows a strict development sequence called EPUIC:
Entity -> Port -> Usecase -> Infrastructure -> Controller
This sequence ensures consistency across all language implementations and demonstrates Clean Architecture principles.
All implementations solve the same problem:
Build a minimal Clean Architecture setup for creating a user with validation and repository storage.
When contributing a new language implementation, you must follow this exact story:
Start with the domain model and business rules:
Entities:
- User has an ID
- User has a phone number
- User has a sex
- User has a role
- User has an age
Value Objects:
- ID is a sequential integer
- PhoneNumber is a 10-digit field
- Sex is male or female
- Role can be admin/user/guest
- Age is an integer from 0 to 120
Define the interface without implementation:
- Port:
UserRepositoryinterface/abstract class that specifies user model behavior (e.g.,save(user),findById(id)) - UseCase:
CreateUseruse case that usesUserRepositorymethods to add functionality in the domain area
Key Point: Ports (Repositories) are interfaces between UseCases and Entities
Implement the concrete repository:
MongoUserRepositoryimplements theUserRepositorymethodsUserRepositoryspecifies WHAT to do,MongoUserRepositoryspecifies HOW to do it with MongoDB- You can swap MongoDB for PostgreSQL, MySQL, or an in-memory store without changing the application or domain layer
Connect the framework to the use case:
- Controller connects the framework (Express, FastAPI, etc.) to the
CreateUserUseCase - Controllers use UseCases to implement their functionality
Wire everything together:
- Get DB client (e.g.,
MongoClient) - Create infrastructure repo (
MongoUserRepository) - Pass
MongoUserRepositoryinstance toCreateUserUseCase - Pass
CreateUserUseCase to the controller - Controller executes the UseCase
- Fork the repository
- Create a new directory for your language (e.g.,
rust/,go/,java/,csharp/, etc.) - Follow the EPUIC sequence exactly as described above
- Implement the same domain model and business rules (User entity with the same value objects)
- Implement the same use case (
CreateUser) - Use MongoDB as the database (or provide clear instructions for alternatives)
- Create a README following the same structure as existing implementations, including:
- The EPUIC sequence explanation
- The same business rules
- The same problem statement
- Setup instructions
- Submit a pull request
- β Must follow EPUIC sequence: Entity -> Port -> Usecase -> Infrastructure -> Controller
- β Must follow the same domain model: User entity with ID, PhoneNumber, Sex, Role, Age value objects
- β Must implement the same use case: CreateUser with the same business rules
- β Must use MongoDB (or provide clear alternatives)
- β Must include comprehensive README with the same structure
- β Must keep implementation minimal and educational
- β Must follow language-specific best practices and conventions
Important: Review the existing Node.js, Python, and Rust implementations to understand the exact pattern and story structure before contributing.
- Node.js / TypeScript β
- Python β
- Rust β
- Go πΉ
- Java β
- C# π·
- C
- Ruby π
- PHP π
- Kotlin π±
- Swift π
- Elixir π§
- Dart π―
- Scala β‘
To use this repository as a starting point for your project:
- Clone or fork this repository:
git clone https://github.com/F4RAN/Clean-Code-Schema.git - Choose your preferred language (Node.js, Python, or Rust)
- Copy the implementation to your new project
- Customize the domain model - Replace User entity and value objects with your own
- Extend the use cases - Add your business logic following the same patterns
- Swap infrastructure - Change MongoDB to your preferred database if needed
- Add features - Build upon the clean architecture foundation
The structure is designed to scale - start simple and grow your application while maintaining clean boundaries between layers.
This project is open source and available for educational purposes.
This repository is designed to help developers learn Clean Architecture principles through practical, language-agnostic examples and serve as a solid foundation for new projects.
Happy Learning & Building! ππ