bitbeet.dev
← volver a proyectos
// 03 · empresa

Plataforma de fichajes

Backend multi-tenant de fichajes y turnos.


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

Backend empresarial para el tipo de fichajes y turnos que realmente mira una inspección de trabajo. Cada dominio es un módulo, cada módulo es una pila de capas — api → dominio → infraestructura — y la superficie HTTP es solo 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

El código está organizado por dominio, no por rol técnico. Cada módulo de primer nivel (auth, companies, schedules, calendars, users, integrations) trae sus propios servicios, schemas y repositorios.

app/
├── api/v1/          # routers y endpoints HTTP
├── domain/          # lógica de negocio
│   ├── auth/        # servicio RBAC, permisos, sesiones
│   ├── companies/   # scope multi-tenant
│   ├── schedules/   # calendarios de trabajo, turnos
│   ├── calendars/   # puente con Google Calendar
│   └── users/       # ciclo de vida de usuario
├── infrastructure/  # sesiones SQLAlchemy, APIs externas
│   ├── database/
│   └── integrations/
└── shared/          # excepciones, utils, primitivas RBAC

Las fronteras se respetan: infrastructure nunca importa de api, api nunca escribe SQL directamente. Testear es simple porque los servicios de dominio reciben repositorios como dependencia.

RBAC con permisos dinámicos

Los permisos no son decoradores hardcodeados — son una tabla. La capa API los resuelve en tiempo de request a través de una dependencia compartida.

# 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

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

Qué lo hace interesante

  • Integración Google Calendar — sincronización bidireccional de turnos con calendarios de empleados vía Google API. Tokens OAuth cifrados en reposo con Fernet.
  • Modelo de datos tolerante a offline — timestamps normalizados a UTC, resolución de conflictos cuando se edita un turno mientras hay una sincronización en vuelo.
  • Generación de informes DOCX — plantillas docxtpl renderizan informes firmados de asistencia desde los mismos objetos del dominio — sin pipeline de exportación secundario.
  • Auditoría siempre — cada mutación escribe en una tabla de log dedicada; una inspección que entra por la puerta se lleva el PDF del mes en segundos.
  • Tiempo real — canal WebSocket de presencia (clock-ins/clock-outs) al dashboard admin.

7
módulos de dominio
3
capas arquitectura
multi
empresas por tenant