Architecture Overview¶
High-Level Architecture¶
The SaaS-Courier system follows a Modular Monolith architecture with Clean Architecture principles within each module.
┌─────────────────────────────────────────────────────────────┐
│ Presentation │
│ (FastAPI Routes, Schemas, WebSocket, Dependencies) │
└─────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ Application │
│ (DTOs, Use Cases, Application Services, Protocols) │
└─────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ Domain │
│ (Entities, Value Objects, Repository Interfaces, Events) │
└─────────────────────────────────────────────────────────────┘
▲
│
┌─────────────────────────────────────────────────────────────┐
│ Infrastructure │
│ (SQLModel Models, Repository Impl, External Services) │
└─────────────────────────────────────────────────────────────┘
Backend Module Structure¶
Each module follows Clean Architecture:
modules/<module>/
├── domain/ # Pure Python - NO framework dependencies
│ ├── entities.py # Domain entities (frozen dataclasses)
│ ├── repositories.py # Repository interfaces (Protocols)
│ └── exceptions.py # Domain exceptions
│
├── application/ # Use case orchestration
│ ├── dtos.py # Data Transfer Objects
│ ├── interfaces/ # Protocol interfaces (Dependency Rule)
│ └── usecases/ # Use case implementations
│
├── infrastructure/ # External implementations
│ ├── persistence/ # SQLModel models, repository implementations
│ ├── services/ # JWT, password, email service implementations
│ └── adapters/ # Third-party service adapters
│
└── presentation/ # FastAPI layer
├── routes/ # API endpoints
├── schemas/ # Pydantic/SQLModel schemas
└── dependencies.py # FastAPI dependencies (DI)
Frontend Module Structure¶
saas-fronted/
├── core/ # Infrastructure (no business logic)
│ ├── config.py # Environment configuration
│ ├── error/ # Result type, failures
│ ├── di/ # Service locator
│ ├── network/ # ApiClient (httpx async)
│ ├── router/ # AppRouter
│ ├── security/ # Rate limiter, input validation, IP blocklist
│ ├── storage/ # SharedPreferences
│ └── theme/ # Theme system (tokens + builder)
│
├── modules/ # Feature modules
│ └── <module>/
│ ├── domain/ # Entities, repositories (abstract), usecases
│ ├── data/ # Datasources, repository impl
│ └── presentation/ # Controllers, pages, components
│
└── shared/ # Reusable UI components
Key Design Decisions¶
1. SQLModel for ORM and Validation¶
SQLModel unifies SQLAlchemy ORM with Pydantic validation:
class UserModel(SQLModel, table=True):
id: UUID = Field(primary_key=True)
email: str = Field(unique=True, max_length=255)
2. Hybrid Entity Pattern¶
Domain entities use frozen dataclasses with with_* factory methods for state transitions:
@dataclass(frozen=True)
class User:
id: UUID
email: str
@staticmethod
def create(email: str) -> "User":
return User(id=uuid4(), email=email)
def with_email(self, new_email: str) -> "User":
return dataclasses.replace(self, email=new_email)
3. Protocol-Based Dependency Rule¶
Use cases depend on Protocol interfaces instead of concrete implementations:
class JWTServiceProtocol(Protocol):
def create_access_token(self, data: dict) -> str: ...
class LoginUseCase:
def __init__(self, jwt_service: JWTServiceProtocol, ...):
self.jwt_service = jwt_service
4. Multi-Tenancy via RLS¶
PostgreSQL Row Level Security isolates tenant data with context manager.
5. API Versioning¶
URL-based versioning allows different response formats:
Dependency Rule¶
- Domain: Never imports from other layers (pure Python)
- Application: Uses interfaces from Domain (Protocols)
- Infrastructure: Implements Domain interfaces
- Presentation: Orchestrates via Application use cases