Skip to content

MVP Backend Architecture

Key Architectural Decisions

1. Project Organization

backend/
├── app/
│   ├── api/                    # API layer
│   │   ├── v1/                # API version
│   │   └── deps.py            # Dependency injection
│   ├── core/                  # Core functionality
│   │   ├── config.py          # Configuration
│   │   ├── security.py        # Security utilities
│   │   └── errors.py          # Error definitions
│   ├── models/                # Database models
│   │   ├── base.py           
│   │   └── mixins.py
│   ├── schemas/               # Pydantic schemas
│   ├── services/              # Business logic
│   ├── repositories/          # Data access
│   └── utils/                 # Shared utilities
├── tests/                     # Test suite
└── alembic/                   # Database migrations

2. Layer Separation

graph TD
    A[API Routes] --> B[Services]
    B --> C[Repositories]
    C --> D[Database]
    E[Schemas] --> A
    F[Models] --> C

API Layer

  • Route definitions only
  • Request/response handling
  • Input validation
  • Dependency injection
  • No business logic

Service Layer

  • Business logic implementation
  • Transaction management
  • Cross-cutting concerns
  • No direct database access

Repository Layer

  • Database operations
  • Query construction
  • Data mapping
  • Reusable data access

3. Design Patterns

Dependency Injection

# Example dependency structure
def get_repository():
    return Repository()

def get_service(repo: Repository = Depends(get_repository)):
    return Service(repo)

@router.get("/")
def endpoint(service: Service = Depends(get_service)):
    return service.operation()

Repository Pattern

# Example repository interface
class Repository(Protocol):
    def get(self, id: int) -> Model:
        ...
    def create(self, data: dict) -> Model:
        ...

Service Layer Pattern

# Example service structure
class Service:
    def __init__(self, repository: Repository):
        self.repository = repository

    def operation(self, data: dict) -> Any:
        # Business logic here
        pass

4. Testing Strategy

Test Organization

tests/
├── conftest.py               # Shared fixtures
├── unit/                    # Unit tests
│   ├── services/
│   └── repositories/
├── integration/             # Integration tests
│   └── api/
└── fixtures/               # Test data

Testing Principles

  • Unit tests for business logic
  • Integration tests for APIs
  • Fixtures over mocks when possible
  • One assert per test
  • Descriptive test names
  • Isolated test database

Core Infrastructure Components

Database Access

  • SQLModel for models
  • Repository pattern for queries
  • Explicit transaction management
  • Migration tracking with Alembic

Error Handling

  • Custom exception classes
  • Global exception handler
  • Structured error responses
  • Detailed logging

Authentication

  • JWT token-based
  • Role-based access control
  • Simple permission model
  • Session management

Configuration

  • Environment-based config
  • Pydantic settings model
  • Type-safe configuration
  • Secrets management

Development Guidelines

Code Organization

  • Single responsibility principle
  • Dependency injection
  • Interface segregation
  • Explicit dependencies

Coding Standards

  • Type hints everywhere
  • Docstring for public APIs
  • Consistent naming conventions
  • Black for formatting
  • Ruff for linting

Documentation

  • OpenAPI/Swagger docs
  • README.md in each module
  • API endpoint documentation
  • Type hint documentation

Testing Guidelines

Unit Testing

  • Test business logic in isolation
  • Mock external dependencies
  • Focus on edge cases
  • Fast execution

Integration Testing

  • Test API endpoints
  • Use test database
  • Test happy paths
  • Verify error responses

Test Coverage

  • Minimum 80% coverage
  • Critical paths 100%
  • Integration test core flows
  • Report coverage metrics