Key files
What each file in a feature does, with real code from the codebase.
Table of contents
__init__.py
The entry point. Defines the blueprint, registers services, provides framework hooks:
# splent_feature_auth/__init__.py
from splent_framework.blueprints.base_blueprint import create_blueprint
from splent_framework.services.service_locator import register_service
from splent_io.splent_feature_auth.services import AuthenticationService
auth_bp = create_blueprint(__name__)
def init_feature(app):
register_service(app, "AuthenticationService", AuthenticationService)
def inject_context_vars(app):
return {}
config.py
Injects environment variables into app.config. This is how the framework tracks env vars and how refinement features (like mailhog overriding mail’s SMTP) work:
import os
def inject_config(app):
app.config.update({
"MAIL_SERVER": os.getenv("MAIL_SERVER", "smtp.example.com"),
"MAIL_PORT": int(os.getenv("MAIL_PORT", "587")),
})
Without
config.py, env vars used viaos.getenv()are invisible to the framework. Usefeature:inject-configto auto-generate it.
models.py
SQLAlchemy models. Each model is self-contained — it must not import models from other features:
from splent_framework.db import db
class User(db.Model):
id = db.Column(db.Integer, primary_key=True)
email = db.Column(db.String(256), unique=True)
password = db.Column(db.String(256), nullable=False)
active = db.Column(db.Boolean, default=True)
If
profile => authin UVL, then profile may import from auth, but auth must never import from profile.
services.py
Business logic. Extends BaseService. Can emit signals for cross-feature communication:
from splent_framework.services.BaseService import BaseService
class AuthenticationService(BaseService):
def __init__(self):
super().__init__(UserRepository())
def login(self, email, password, remember=True):
user = self.repository.get_by_email(email)
if user and user.check_password(password):
login_user(user, remember=remember)
return True
return False
See Services for the full pattern.
repositories.py
Data access layer. One repository per model:
from splent_framework.repositories.BaseRepository import BaseRepository
from splent_io.splent_feature_auth.models import User
class UserRepository(BaseRepository):
def __init__(self):
super().__init__(User)
def get_by_email(self, email, active=True):
return self.model.query.filter_by(email=email, active=active).first()
See Repositories for the full pattern.
routes.py
Flask routes. Handle HTTP only — delegate logic to services:
from splent_io.splent_feature_auth import auth_bp
from splent_framework.services.service_locator import service_proxy
auth_service = service_proxy("AuthenticationService")
@auth_bp.route("/login", methods=["GET", "POST"])
def login():
...
hooks.py
Registers template hooks — named slots where this feature injects HTML:
from splent_framework.hooks.template_hooks import register_template_hook
from flask import render_template
def sidebar_items():
return render_template("hooks/sidebar_items.html")
register_template_hook("layout.sidebar", sidebar_items)
See Template hooks.
commands.py
CLI commands exposed as splent feature:<name> <command>:
import click
@click.command("test")
@click.option("--to", required=True, metavar="EMAIL")
def mail_test(to):
"""Send a test email."""
...
cli_commands = [mail_test]
signals.py
Listens to signals from upstream features:
from splent_io.splent_feature_auth.signals import user_registered
@user_registered.connect
def on_user_registered(sender, **kwargs):
user = kwargs["user"]
# Create default profile
Must be imported in __init__.py to register handlers.
decorators.py
Feature-specific decorators that other features import:
from flask_login import login_required # noqa: F401
# Other features: from splent_io.splent_feature_auth.decorators import login_required
Makes the dependency explicit for product:validate.
seeders.py
Database seeder for development/test data:
from splent_framework.seeders.BaseSeeder import BaseSeeder
from splent_io.splent_feature_auth.models import User
class AuthSeeder(BaseSeeder):
def run(self):
self.seed([User(email="admin@example.com", password="1234")])
See Seeders.
migrations/
Per-feature Alembic migrations. Each feature has its own alembic_<feature> version table. Generated with splent db:migrate, applied with splent db:upgrade.
See also
- Directory structure — the full file tree
- Architecture layers — how services, repositories, and seeders work
- Extensibility — hooks, refinement, service locator