bitbeet.dev
← back to projects
// 03 · enterprise

Time-Tracking Platform

Multi-tenant time-tracking and scheduling backend.


STACK
Python 3.12 · FastAPI · SQLAlchemy · Alembic · PostgreSQL · Fernet · Google Calendar API · docxtpl · Jinja2 · WebSockets
STATUS
private

Enterprise backend for the kind of time-tracking and scheduling that labour inspectors actually look at. Every domain is a module, every module is a layer cake — api → domain → infrastructure — and the HTTP surface is just routing.

Clean architecture

Clean architecture layers: HTTP routers in the API layer, seven domain modules in the domain layer, and persistence plus external integrations in the infrastructure layer API LAYER · HTTP FastAPI routers · request validation · permission deps · response schemas DOMAIN LAYER · BUSINESS LOGIC auth companies users schedules calendars integrations shared each module = services + schemas + repositories RBAC permissions resolved dynamically · tenant scope enforced on every repo INFRASTRUCTURE · ADAPTERS SQLAlchemy + Alembic · Fernet token store · Google Calendar API · docxtpl renderer · WebSocket hub
clean architecture — layers talk downward · infrastructure implements domain interfaces

The codebase is organised by domain, not by technical role. Each top-level module (auth, companies, schedules, calendars, users, integrations) ships with its own services, schemas and repositories.

app/
├── api/v1/          # HTTP routers and endpoints
├── domain/          # business logic
│   ├── auth/        # RBAC service, permissions, sessions
│   ├── companies/   # multi-tenant scope
│   ├── schedules/   # work calendars, shifts
│   ├── calendars/   # Google Calendar bridge
│   └── users/       # user lifecycle
├── infrastructure/  # SQLAlchemy sessions, external APIs
│   ├── database/
│   └── integrations/
└── shared/          # exceptions, utils, RBAC primitives

Boundaries are enforced: infrastructure never imports from api, api never writes SQL directly. Testing is straightforward because domain services take repositories as dependencies.

RBAC with dynamic permissions

Permissions aren’t hardcoded decorators — they’re a table. The API layer resolves them at request time through a shared dependency.

# app/api/deps.py
def require_permission(code: str):
    def _dep(
        user: User = Depends(get_current_user),
        rbac: RBACService = Depends(get_rbac_service),
    ):
        if not rbac.user_has(user, code):
            raise HTTPException(403, "forbidden")
        return user
    return _dep

# usage
@router.post("/schedules")
def create_schedule(
    payload: ScheduleCreate,
    user = Depends(require_permission("schedules.create")),
):
    ...

What makes it interesting

  • Google Calendar integration — two-way sync of schedules with employee calendars via the Google API. OAuth tokens encrypted at rest with Fernet.
  • Offline-friendly data model — timestamps normalised to UTC, conflict resolution when a shift is edited while a sync was in flight.
  • DOCX report generationdocxtpl templates render signed attendance reports from the same domain objects — no secondary export pipeline.
  • Audit, always — every mutation writes to a dedicated log table; a labour inspector walking in can be given a PDF of the month in seconds.
  • Real-time — WebSocket channel for presence (clock-ins/clock-outs) streamed to the admin dashboard.

7
domain modules
3
architecture layers
multi
tenant companies