# StarSpring Framework

[![Python Version](https://img.shields.io/badge/python-3.10%2B-blue)](https://www.python.org/downloads/)
[![License](https://img.shields.io/badge/license-MIT-green)](LICENSE)
[![Status](https://img.shields.io/badge/status-alpha-orange)](https://pypi.org/project/starspring/)

**StarSpring** is a production-grade, asynchronous web framework for Python that brings the robust architectural patterns of **Spring Boot** to the modern Python ecosystem. Built on top of **Starlette** and **SQLAlchemy**, it combines enterprise structure with Pythonic elegance.

---

## 🌟 Features

*   **Dependency Injection (IoC)**: Fully typed, automatic constructor injection. No more global state or manual wiring.
*   **Declarative Routing**: Use `@GetMapping`, `@PostMapping` decorators for clean, readable controllers.
*   **Enterprise ORM**: Built on **SQLAlchemy** with **Imperative Mapping**. Define simple Python classes, and they become powerful database entities automatically.
*   **Magic Repositories**: Define interfaces, and StarSpring implements the queries for you (e.g., `find_by_email_and_active(email, True)`).
*   **Robust Transaction Management**: `@Transactional` decorators with support for robust nested transactions (`SAVEPOINT`s).
*   **Production Ready**: Built-in support for CORS, Exception Handling, Logging, and Configuration Management (`application.yaml`).

---

## 📦 Installation

StarSpring requires Python 3.10+.

### Using pip
```bash
pip install starspring
```

### Using uv (Recommended for speed)
```bash
uv pip install starspring
```

### Database Drivers
StarSpring uses SQLAlchemy. Install the driver for your database:
```bash
# SQLite (Standard)
pip install starspring

# PostgreSQL
pip install psycopg2-binary
# or
pip install asyncpg
```

---

## 🚀 Building Your First Application

Here is a complete walkthrough of building a User Management API.

### 1. Project Structure
We recommend a standard layered architecture:
```text
my_app/
├── __init__.py
├── main.py              # Entry point
├── application.yaml     # Configuration
├── entities.py          # Database Models
├── repositories.py      # Data Access
├── services.py          # Business Logic
└── controllers.py       # REST Endpoints
```

### 2. Configuration (`application.yaml`)
Configure your server and database connection.

```yaml
server:
  port: 8000
  host: 0.0.0.0

database:
  url: "sqlite:///app.db"  # or postgresql://user:pass@localhost/db
  ddl-auto: "create-if-not-exists" # Auto-creates tables from entities
```

### 3. Entities (`entities.py`)
Define your database models using standard Python classes. StarSpring maps them automatically.

```python
from starspring import Entity, BaseEntity, Column
from datetime import datetime

@Entity(table_name="users")
class User(BaseEntity):
    # BaseEntity automatically adds 'id', 'created_at', 'updated_at'
    
    username: str = Column(unique=True, length=50, nullable=False)
    email: str = Column(unique=True, nullable=False)
    is_active: bool = Column(default=True)
    role: str = Column(default="USER")
```

### 4. Repositories (`repositories.py`)
Create an interface for data access. Inherit from `StarRepository`.
Note: Identify the Entity type in the generic (e.g., `[User]`).

```python
from starspring import Repository, StarRepository
from my_app.entities import User

@Repository
class UserRepository(StarRepository[User]):
    # StarSpring automatically implements standard CRUD (save, find_by_id, delete, etc.)
    
    # Define custom finders just by naming them!
    async def find_by_username(self, username: str) -> User | None:
        ...
        
    async def find_by_email_and_is_active(self, email: str, is_active: bool) -> User | None:
        ...
```

### 5. Services (`services.py`)
Encapsulate your business logic here. Use `@Transactional` to ensure data integrity.

```python
from starspring import Service, Transactional
from my_app.repositories import UserRepository
from my_app.entities import User

@Service
class UserService:
    # Dependency Injection: Just mention the type in the constructor!
    def __init__(self, user_repo: UserRepository):
        self.user_repo = user_repo

    @Transactional
    async def register_user(self, username: str, email: str) -> User:
        # Check if exists
        existing = await self.user_repo.find_by_email_and_is_active(email, True)
        if existing:
            raise ValueError("User already exists")
            
        # Create new user
        new_user = User(username=username, email=email)
        return await self.user_repo.save(new_user)
```

### 6. Controllers (`controllers.py`)
Expose your logic as a REST API.

```python
# Note: Use @Controller for REST endpoints too. 
# StarSpring automatically serializes dict/list responses to JSON.
from starspring import Controller, GetMapping, PostMapping, ResponseEntity
from my_app.services import UserService

@Controller("/api/users")
class UserController:
    
    def __init__(self, user_service: UserService):
        self.user_service = user_service

    @GetMapping("/{username}")
    async def get_user(self, username: str) -> dict:
        # You can return dicts, lists, or Entities directly
        user = await self.user_service.user_repo.find_by_username(username)
        # Assuming BaseEntity has .to_dict() or Pydantic serialization
        return user.to_dict() if user else ResponseEntity.not_found()

    @PostMapping("/register")
    async def register(self, username: str, email: str) -> dict:
        # Arguments are automatically extracted from JSON body or Query params
        try:
            user = await self.user_service.register_user(username, email)
            return {"status": "success", "user_id": user.id}
        except ValueError as e:
            return ResponseEntity.bad_request(str(e))
```

### 7. Main Entry Point (`main.py`)
Bootstrap the application.

```python
from starspring import StarSpringApplication

# Initialize App
app = StarSpringApplication(
    title="My User API",
    config_path="application.yaml"
)

# Scan for all your components (Controllers, Services, Repositories)
app.scan_components("my_app")

if __name__ == "__main__":
    # Runs the server (default: localhost:8000)
    app.run() 
```

---

## 📚 Core Concepts

### Dependency Injection (DI)
StarSpring manages the lifecycle of your objects. When you ask for a `UserRepository` in your `UserService` constructor, the framework:
1.  Finds the `UserRepository` class.
2.  Creates an instance of it (Singleton by default).
3.  Passes it to your `UserService`.

This makes testing easier (you can mock repositories) and code cleaner.

### Database & ORM
We use a **Code-First** approach.
1.  Define Python classes (`@Entity`).
2.  StarSpring tells SQLAlchemy to map these classes to tables.
3.  If `database.ddl-auto` is set to `create`, the framework creates the tables for you on startup.

### Transaction Management
Use the `@Transactional` decorator on any method (usually in Services).
*   **Success**: The transaction commits automatically.
*   **Reference Counter**: If you nest `@Transactional` methods, the inner one joins the outer transaction.
*   **Error**: If an exception occurs, the entire transaction rolls back.

---

## 🛠 Advanced Configuration

You can tune every part of the framework via `application.yaml`.

```yaml
server:
  port: 8080
  cors:
    allowed-origins: ["*"]
    allowed-methods: ["GET", "POST", "PUT", "DELETE"]

logging:
  level: INFO
  format: "%(asctime)s - %(name)s - %(levelname)s - %(message)s"
```

---

## 🤝 Contributing

We welcome contributions!
1.  Fork the repository.
2.  Create a feature branch.
3.  Submit a Pull Request.

Please ensure all tests pass before submitting.

## 📄 License

This project is licensed under the MIT License - see the LICENSE file for details.
