Folder structure and common mistakes
1. app/
This is the main application package.
Purpose:
- Contains all the application code
- Entry point for the application
Should contain:
main.py
: The entry point of the application- Subpackages for different layers of the application
Common mistakes:
- Putting too much logic in
main.py
- Not organizing subpackages properly
2. app/core/
This folder contains core functionality and configurations.
Purpose:
- Store application-wide configurations
- Define core exceptions and utilities
Should contain:
config.py
: Configuration settingsexceptions.py
: Custom exception classes
Common mistakes:
- Hardcoding configuration values instead of using environment variables
- Not centralizing exception handling
3. app/domain/
This folder represents the domain layer of the application.
Purpose:
- Define core business logic and entities
- Implement business rules that are application-independent
Should contain:
models/
: ORM models representing database entitiesschemas/
: Pydantic models for data validation and serialization
Common mistakes:
- Mixing domain logic with infrastructure concerns
- Depending on external frameworks in domain models
4. app/usecases/
This folder contains the application’s use cases or interactors.
Purpose:
- Implement application-specific business rules
- Orchestrate the flow of data to and from entities
Should contain:
- Use case classes that encapsulate business logic
Common mistakes:
- Implementing database operations directly in use cases
- Tight coupling with specific frameworks or libraries
5. app/interfaces/
This folder defines interfaces (abstract base classes) for external agency boundaries.
Purpose:
- Define contracts for repositories and external services
- Enable dependency inversion
Should contain:
repositories/
: Abstract base classes for data access
Common mistakes:
- Implementing concrete classes here instead of just interfaces
- Not using abstract base classes (ABC) for proper interface definition
6. app/infrastructure/
This folder contains implementations of interfaces defined in the interfaces layer.
Purpose:
- Implement data access logic
- Set up external service connections
Should contain:
database.py
: Database connection setuprepositories/
: Concrete implementations of repository interfaces
Common mistakes:
- Mixing business logic with data access code
- Hard-coding database connections instead of using dependency injection
7. app/api/
This folder contains the API layer of the application.
Purpose:
- Define API routes and endpoints
- Handle HTTP requests and responses
Should contain:
dependencies.py
: FastAPI dependenciesv1/
: API version 1 endpoints
Common mistakes:
- Implementing business logic in API routes
- Not versioning the API
- Not using dependency injection for database sessions and repositories
8. tests/
This folder contains all the tests for the application.
Purpose:
- Store unit, integration, and end-to-end tests
- Provide fixtures and utilities for testing
Should contain:
conftest.py
: pytest configurations and fixtures- Subfolders mirroring the
app/
structure for different test types
Common mistakes:
- Not writing tests
- Testing implementation details instead of behavior
- Not using test doubles (mocks, stubs) appropriately
9. alembic/
This folder is for database migrations using Alembic.
Purpose:
- Manage database schema changes
- Version control for database structure
Should contain:
versions/
: Individual migration scriptsenv.py
: Alembic environment configuration
Common mistakes:
- Manually changing the database schema without creating migrations
- Not reviewing migrations before applying them to production
10. Root directory
Purpose:
- Store project-wide configuration files
- Define project dependencies and setup
Should contain:
.env
: Environment variables (not tracked in git).gitignore
: Git ignore filerequirements.txt
: Python dependenciesDockerfile
: Docker configuration for the applicationdocker-compose.yml
: Docker Compose configuration for local development
Common mistakes:
- Committing sensitive information (like API keys) to version control
- Not keeping
requirements.txt
updated - Inconsistent Docker configurations between development and production
General Best Practices:
- Follow the Single Responsibility Principle for each module and class.
- Use dependency injection to make your code more testable and flexible.
- Keep the domain layer independent of external frameworks and libraries.
- Use type hints consistently throughout the codebase.
- Document your code, especially public interfaces and complex logic.
- Use environment variables for configuration that changes between environments.
- Implement proper error handling and logging throughout the application.
- Write tests for all layers of the application, aiming for high test coverage.
This guide provides a comprehensive overview of the folder structure, their purposes, what should be in each folder, and common mistakes to avoid. By following this structure and adhering to clean architecture principles, you can create a FastAPI application that is modular, testable, and maintainable.
Some additional points to consider:
Dependency Management: Consider using
poetry
orpipenv
for more robust dependency management, especially for larger projects.Configuration: Use a library like
pydantic-settings
to manage configuration in a type-safe manner.API Documentation: Leverage FastAPI’s built-in support for OpenAPI (Swagger) and ReDoc for API documentation.
Asynchronous Programming: FastAPI is built for asynchronous programming. Ensure you’re using async/await correctly throughout your application.
Security: Implement proper authentication and authorization. Consider using FastAPI’s built-in security utilities.
Caching: For performance optimization, implement caching where appropriate, using tools like Redis.
Logging: Implement a robust logging system to aid in debugging and monitoring.
Containerization: Ensure your Dockerfile and docker-compose.yml are optimized for both development and production environments.
Remember, while this structure provides a solid foundation, you may need to adapt it based on your specific project requirements. The key is to maintain a clear separation of concerns and keep your code modular and testable.
Would you like me to elaborate on any specific part of this structure or discuss how to implement any particular component?