Skip to main content

Overview

Soft-Bee API follows a feature-based structure where each business feature is organized as a self-contained module with its own domain, application, and infrastructure layers.

Root Structure

soft-bee-api/
├── app.py                 # Application factory
├── config.py              # Configuration management
├── requirements.txt       # Python dependencies
├── .env                   # Environment variables
├── src/                   # Source code
│   ├── api/              # API routing
│   ├── core/             # Shared core functionality
│   ├── features/         # Business features
│   └── shared/           # Shared utilities
├── tests/                # Test suite
└── docs/                 # Documentation

Application Entry Point

app.py

The application factory that creates and configures the Flask app:
app.py
from flask import Flask
from flask_cors import CORS
from src.core.database.db import init_app
from src.api.router import register_features
from config import get_config

def create_app(config_name: str = None, testing: bool = False):
    app = Flask(__name__)
    
    # Load configuration
    config_class = get_config()
    app.config.from_object(config_class)
    
    # Enable CORS
    CORS(app, resources={r"/api/*": {"origins": "*"}})
    
    # Initialize database
    init_app(app)
    
    # Register features
    features_to_register = ['auth']  # Add more features here
    registered_features = register_features(app, features_to_register)
    
    return app
The factory pattern makes it easy to create different app instances for testing, development, and production.

config.py

Environment-based configuration:
config.py
import os
from dotenv import load_dotenv

load_dotenv()

class Config:
    """Base configuration"""
    SECRET_KEY = os.getenv('SECRET_KEY', 'dev-secret-key')
    JWT_SECRET_KEY = os.getenv("JWT_KEY", "secret-key-default")
    JWT_ALGORITHM = os.getenv("ALGORITHM", "HS256")
    FRONTEND_URL = os.getenv("FRONTEND_URL", "http://localhost:3000")

class LocalConfig(Config):
    DEBUG = True
    DATABASE_URL = os.getenv("DATABASE_URL", 
        "postgresql://postgres:postgres@localhost:5432/softbee_local")

class ProductionConfig(Config):
    DEBUG = False
    DATABASE_URL = os.getenv("DATABASE_URL")
    
    if not DATABASE_URL:
        raise ValueError("DATABASE_URL is required in production")

config = {
    'local': LocalConfig,
    'production': ProductionConfig,
    'testing': TestingConfig,
    'default': LocalConfig
}

Source Directory Structure

src/api/ - API Routing

Handles feature registration and routing:
src/api/
├── __init__.py
└── router.py              # Feature router
router.py - Dynamic feature registration:
src/api/router.py
from flask import Flask
from typing import List

class FeatureRouter:
    """Router que registra todas las features"""
    
    def __init__(self, app: Flask):
        self.app = app
        self.registered_features: List[str] = []
    
    def register(self, feature_name: str, enabled: bool = True) -> bool:
        """Registrar una feature individual"""
        if not enabled:
            return False
        
        try:
            # Import the feature module
            module = __import__(
                f"src.features.{feature_name}",
                fromlist=['']
            )
            
            # Find and register blueprint
            blueprint = getattr(module, f'{feature_name}_bp', None)
            if blueprint:
                self.app.register_blueprint(blueprint)
                self.registered_features.append(feature_name)
                print(f"✅ Feature '{feature_name}' registrada")
                return True
                
        except ImportError as e:
            print(f"❌ Feature '{feature_name}' no encontrada: {e}")
            return False
Features are registered dynamically, making it easy to enable/disable features without changing code.

src/core/ - Core Functionality

Shared infrastructure that all features use:
src/core/
├── database/
│   └── db.py              # Database connection and session management
└── dependencies/
    └── containers.py       # Dependency injection containers
Database Module:
src/core/database/db.py
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker, scoped_session
from flask import Flask, g

def init_app(app: Flask):
    """Initialize database with Flask app"""
    engine = create_engine(app.config['DATABASE_URL'])
    session_factory = sessionmaker(bind=engine)
    app.db_session = scoped_session(session_factory)

def get_db():
    """Get database session for current request"""
    if 'db' not in g:
        g.db = current_app.db_session()
    return g.db
Dependency Container:
src/core/dependencies/containers.py
from dependency_injector import containers, providers

class AuthContainer(containers.DeclarativeContainer):
    """Dependency container for auth feature"""
    
    config = providers.Configuration()
    db_session = providers.Dependency()
    
    # Repositories
    user_repository = providers.Factory(
        UserRepositoryImpl,
        db_session=db_session
    )
    
    # Services
    jwt_service = providers.Singleton(
        JWTService,
        secret_key=config.auth.jwt_secret_key
    )
    
    # Use cases
    login_use_case = providers.Factory(
        LoginUserUseCase,
        user_repository=user_repository,
        token_service=jwt_service
    )

class MainContainer(containers.DeclarativeContainer):
    """Main application container"""
    
    config = providers.Configuration()
    db_session = providers.Dependency()
    
    # Feature containers
    auth = providers.Container(
        AuthContainer,
        db_session=db_session,
        config=config.auth
    )

src/features/ - Business Features

Each feature follows the same structure:
src/features/
└── auth/                  # Authentication feature
    ├── __init__.py        # Exports auth_bp blueprint
    ├── domain/            # Business logic
    │   ├── entities/
    │   │   └── user.py
    │   ├── value_objects/
    │   │   ├── email.py
    │   │   └── password.py
    │   ├── events/
    │   │   └── auth_events.py
    │   ├── exceptions/
    │   │   └── auth_exceptions.py
    │   └── services/
    ├── application/       # Use cases and DTOs
    │   ├── use_cases/
    │   │   ├── login_user.py
    │   │   └── register_user.py
    │   ├── dto/
    │   │   ├── auth_dto.py
    │   │   └── user_dto.py
    │   ├── interfaces/
    │   │   ├── repositories/
    │   │   │   └── user_repository.py
    │   │   └── services/
    │   │       └── token_service.py
    │   └── mappers/
    │       └── user_mapper.py
    ├── infrastructure/     # Technical implementation
    │   ├── repositories/
    │   │   └── user_repository_impl.py
    │   ├── models/
    │   │   └── user_model.py
    │   └── services/
    │       └── security/
    │           ├── jwt_handler.py
    │           └── password_hasher.py
    └── presentation/      # API layer
        └── api/v1/
            ├── endpoints/
            │   └── auth.py
            └── schemas/
                └── auth_schemas.py
Pure business logic with no framework dependencies:
  • entities/: Business objects with behavior (User, Apiary, Hive)
  • value_objects/: Immutable objects defined by attributes (Email, Password)
  • events/: Domain events (UserRegistered, HiveInspected)
  • exceptions/: Business rule violations
  • services/: Domain services for operations involving multiple entities
Application workflows that orchestrate domain logic:
  • use_cases/: Business workflows (LoginUser, RegisterUser)
  • dto/: Data Transfer Objects for crossing boundaries
  • interfaces/: Contracts that infrastructure must implement
  • mappers/: Convert between entities, DTOs, and models
Concrete implementations of technical concerns:
  • repositories/: Database implementations
  • models/: SQLAlchemy or other ORM models
  • services/: External services (email, SMS, payments)
HTTP interface for the feature:
  • endpoints/: Flask blueprints with routes
  • schemas/: Request/response validation (Marshmallow)
  • dependencies/: Route-level dependency injection

Feature init.py

Each feature exports its blueprint:
src/features/auth/__init__.py
"""Authentication Feature"""

from .presentation.api.v1.endpoints import auth_bp

__all__ = ['auth_bp']
This makes it easy for the router to discover and register features automatically.

src/shared/ - Shared Utilities

Utilities used across multiple features:
src/shared/
└── utils/
    ├── file_handler.py    # File upload/download
    ├── validators.py      # Common validation logic
    └── formatters.py      # Data formatting

Adding a New Feature

To add a new feature (e.g., “hives”):
1

Create feature directory

mkdir -p src/features/hives/{domain,application,infrastructure,presentation}
2

Implement the layers

Create entities, use cases, repositories, and endpoints following the structure above
3

Export the blueprint

src/features/hives/__init__.py
from .presentation.api.v1.endpoints import hives_bp
__all__ = ['hives_bp']
4

Register in app.py

features_to_register = ['auth', 'hives']  # Add 'hives'
The modular structure makes it easy to add, remove, or modify features without affecting others.

Key Files Reference

app.py

Application factory and initialization

config.py

Environment-based configuration

router.py

Dynamic feature registration

containers.py

Dependency injection setup

Clean Architecture

Understand the three layers in detail

Dependency Injection

Learn how dependencies are wired

Build docs developers (and LLMs) love