Mitsuki Framework Overview
Mitsuki brings Spring Boot's programming model to Python, combining:
- Structured, opinionated architecture - Layered components, dependency injection and inversion of control
- Python productivity - Fast development with clean, expressive syntax
- Modern async - Built on ASGI for high-performance, scalable applications
Core Concepts
Inversion of Control (IoC)
IoC is a design principle where the framework controls the flow of the application, rather than the application controlling the framework. Instead of manually creating and managing objects, you declare what you need and let Mitsuki handle the lifecycle.
Traditional approach (no IoC):
class UserService:
def __init__(self):
self.repo = UserRepository() # Tight coupling
self.email = EmailService()
class UserController:
def __init__(self):
self.service = UserService() # Manual creationMitsuki approach (with IoC):
@Service
class UserService:
def __init__(self, repo: UserRepository, email: EmailService):
# Mitsuki injects dependencies automatically
self.repo = repo
self.email = email
@RestController("/users")
class UserController:
def __init__(self, service: UserService):
# Mitsuki creates and injects UserService
self.service = serviceDependency Injection (DI)
DI is the mechanism that implements IoC. Mitsuki automatically:
- Discovers components via decorators (
@Service,@Repository, etc.) - Analyzes constructor parameters and type hints
- Resolves and injects dependencies
- Manages component lifecycle (singleton vs prototype)
Benefits:
- Loose coupling - Components don’t construct their own dependencies, and can rely on abstractions when desired
- Testability - Easy to mock dependencies in unit tests
- Maintainability - Clear separation of concerns
The Container
The DI Container is the heart of Mitsuki. It:
- Stores component metadata (type, name, scope, dependencies)
- Resolves dependency graphs
- Creates and caches instances (for singletons)
- Detects circular dependencies
- Provides providers to the application
Component Registration:
Components are registered when decorators are applied (import-time):
from mitsuki import Service, Repository, RestController
@Repository() # Registered immediately
class UserRepository:
pass
@Service() # Registered immediately
class UserService:
def __init__(self, repo: UserRepository):
self.repo = repo
@RestController("/api") # Registered immediately
class ApiController:
def __init__(self, service: UserService):
self.service = serviceDependency Resolution:
When Mitsuki starts, it:
- Scans all registered components
- For each component, resolves its dependencies
- Creates instances in the correct order (dependencies first)
Container Resolution Order:
1. UserRepository (no dependencies) → create instance
2. UserService (depends on UserRepository) → inject UserRepository → create instance
3. ApiController (depends on UserService) → inject UserService → create instanceArchitecture Layers
Mitsuki encourages a layered architecture, where each layer has clearly defined scope and responsibilities, where lower layers (i.e. closer to data layer) don't depend on layers above:
┌─────────────────────────────────┐
│ Presentation Layer │ @RestController, @Controller
│ (Controllers, Web) │ Handle HTTP requests/responses
└─────────────────────────────────┘
↓
┌─────────────────────────────────┐
│ Business Logic Layer │ @Service
│ (Services) │ Application logic, orchestration
└─────────────────────────────────┘
↓
┌─────────────────────────────────┐
│ Data Access Layer │ @Repository, @CrudRepository
│ (Repositories) │ Database operations, queries
└─────────────────────────────────┘
↓
┌─────────────────────────────────┐
│ Domain Layer │ @Entity
│ (Entities, Models) │ Business objects, data models
└─────────────────────────────────┘Example flow:
# Domain Layer
@Entity()
@dataclass
class User:
id: int = Id()
name: str = ""
email: str = Column(unique=True, default="")
# Data Access Layer
@CrudRepository(entity=User)
class UserRepository:
async def find_by_email(self, email: str) -> Optional[User]: ...
# Business Logic Layer
@Service()
class UserService:
def __init__(self, repo: UserRepository):
self.repo = repo
async def register_user(self, name: str, email: str) -> User:
# Business logic: validation, creation
user = User(name=name, email=email)
return await self.repo.save(user)
# Presentation Layer
@RestController("/api/users")
class UserController:
def __init__(self, service: UserService):
self.service = service
@PostMapping("/register")
async def register(self, body: dict) -> dict:
user = await self.service.register_user(
name=body["name"],
email=body["email"]
)
return {"id": user.id, "name": user.name}In a Nutshell
Mitsuki is opinionated on how applications should be structured. As such, it can make assumptions on structure, which enables many downstream use cases, such as rich metadata for auto-documentation, auto-generating implementations for your domains as defaults (which you can then override if you want them custom), etc.
Many more downstream use cases are currently planned.
The point is - you should be able to focus on the interesting parts - the business logic. Much of the remainder can be automated with sensible defaults, for most apps.
1. Convention Over Configuration
Mitsuki uses sensible defaults to minimize boilerplate:
- Components auto-named from class names
- URL paths derived from controller paths
- Database tables mapped from entity class names
2. Explicit Over Implicit
Mitsuki favors clarity:
- Constructor injection only - Dependencies are visible and testable
- Type hints required - Clear contracts between components
- No magic strings - Use types and names explicitly
3. Decorator-Based Registration
Components register themselves when imported, and express intent:
@Service()
class MyService:
passAutomatic Component Scanning: Mitsuki automatically scans and imports all Python files in your project directory recursively when the application starts. This means:
- All
@Service,@Repository,@RestControllerclasses are automatically discovered - Works across all subdirectories (e.g.,
app/controllers/,app/services/, etc.) - Automatically excludes:
tests/,test_*.py,migrations/,venv/,__pycache__/
No configuration needed for most projects - just organize your code however you like!
4. Async-First
All framework operations support async:
- Controllers can be
async def - Repository methods are async by default
- Database operations are non-blocking
Application Lifecycle
1. Import Phase - Decorators register components
from mitsuki import Application, Service, RestController
@Service() # Component registered
class MyService:
pass2. Startup Phase - Application.run() called
@Application
class MyApp:
pass
if __name__ == "__main__":
MyApp.run() # Triggers startup3. Initialization Phase
- Load configurations -
defaultconfig,application.ymlconfig,application-{profile}.ymlconfig, env vars. Blends them. - Process
@Configurationclasses - Execute
@Providerfactory methods - Initialize database connections
- Create entity tables
4. Resolution Phase
- Resolve all component dependencies
- Create instances in dependency order
- Inject dependencies via constructors
5. Server Startup
- Collect all
@RestControllerand@Controllerclasses - Build routing table from
@GetMapping,@PostMapping, etc. - Start ASGI server
6. Runtime Phase
- Handle incoming HTTP requests
- Route to appropriate controller methods
- Execute business logic through services
- Return responses
7. Shutdown Phase
- Close database connections
- Clean up resources
- Graceful shutdown
Configuration
Mitsuki supports multiple configuration sources, with a default config file containing sensible defaults:
1. application.yml (primary)
server:
port: 8000
database:
url: postgresql://localhost/mydb2. Environment Variables (override)
MITSUKI_DATABASE_URL=postgresql://prod/db
MITSUKI_SERVER_PORT=90003. Profile-Specific Files
# application-development.yml
# application-production.yml4. @Value Injection
@Configuration
class Config:
port: int = Value("${server.port:8000}")When starting an app, sources are logged:
Configuration sources:
[default configuration]
┌──────────────────────────────────────────────────────────────────────┐
│database.adapter database.echo │
│database.pool.enabled database.pool.max_overflow │
│database.pool.recycle database.pool.size │
│database.pool.timeout logging.format │
│logging.sqlalchemy server.cors.allowed_origins │
│server.max_body_size server.multipart.max_file_size │
│server.multipart.max_request_size server.workers │
└──────────────────────────────────────────────────────────────────────┘
[application.yml]
┌──────────────────────────────────────────────────────────────────────┐
│logging.level logging.log_config_sources │
│server.access_log server.cors.enabled │
│server.ignore_trailing_slash server.type │
└──────────────────────────────────────────────────────────────────────┘
[environment variable (MITSUKI_SERVER_PORT)]
┌──────────────────────────────────────────────────────────────────────┐
│server.port │
└──────────────────────────────────────────────────────────────────────┘Next Steps
- Decorators Reference - Complete guide to all decorators
- Repositories & Data Layer - CRUD operations and query DSL
- Controllers & Routing - Web request handling
- Profiles - Environment-specific configuration
- Configuration - Application configuration and providers
- OpenAPI - Auto-generated API documentation