Back to Research

ARCHITECTURE & DESIGN DECISIONS

Phase 1 — Why a modular monolith, and how it’s built

February 2026 • Solen Kablo • Living Document

The architecture was not designed upfront — it emerged. The database evolved as each module was built. The framework choices were made for pragmatic reasons: speed of development, component libraries that eliminate GUI work, and the ability to run on a factory floor from a single Mac Mini. This document records what was chosen, why, and what the trade-offs are.

MODULAR
MONOLITH
ARCHITECTURE
FastAPI
+ React
STACK
SQLite
→ Postgres
DATABASE
25+
API Routes
ENDPOINTS

TABLE OF CONTENTS

1. The Architecture: Modular Monolith 2. Technology Stack 3. Design Rules 4. API Design 5. Database Strategy 6. Frontend Architecture 7. AI Integration 8. Network & Deployment 9. The Evolution Path

1. THE ARCHITECTURE: MODULAR MONOLITH

The system is a Modular Monolith — a single application with clearly separated internal modules, sharing a single database. Not microservices. Not a traditional monolith. The sweet spot in between.

Traditional Monolith

Single codebase, no module separation. Everything tangled. Not this.

Modular Monolith ← THIS

Single application, clear module boundaries, shared database. Right-sized for a factory with 10–12 machines and limited concurrent users.

Microservices

Independent services with separate databases. Overkill for this scale. Adds network latency, distributed transaction complexity, and operational overhead.

Why Not Microservices?

The trade-off is known: When the database fails, everything fails. All modules share one SQLite/PostgreSQL instance — there is a single point of failure. For a factory ERP with on-site hardware, this is an acceptable trade-off. The alternative (distributed databases, eventual consistency) adds complexity that would slow development without meaningful benefit at this scale.

Module Boundaries

solen_backend/
├── app/
│   ├── api/                    # REST API layer
│   │   ├── auth/               # Authentication & authorization
│   │   ├── materials/          # Raw material management
│   │   ├── suppliers/          # Supplier management
│   │   ├── siparis/            # Order management
│   │   ├── production/         # Production operations
│   │   ├── teknik/             # Cable design & machines
│   │   ├── stock/              # Stock & projection
│   │   ├── ai/                 # AI chat & search
│   │   └── ...                 # 15+ more route groups
│   ├── models/                 # SQLAlchemy data models
│   │   ├── teknik/             # machines.py, cable_design.py, standards.py
│   │   ├── siparis/            # order.py, customer.py
│   │   ├── production/         # production_session.py, work_card.py
│   │   └── ...                 # material.py, supplier.py, user.py
│   ├── schemas/                # Pydantic validation schemas
│   ├── services/               # Business logic layer
│   │   ├── production_calculator.py
│   │   ├── work_card_generator.py
│   │   └── projeksiyon_service.py
│   ├── core/                   # Config, database, shared utilities
│   └── utils/                  # Production constants, helpers
├── migrations/                 # Database migration scripts
└── main.py                     # FastAPI application entry point

2. TECHNOLOGY STACK

Backend

LayerTechnologyVersionWhy
FrameworkFastAPI≥0.110Async Python, auto-generated OpenAPI docs, dependency injection built-in
ServerUvicorn≥0.27ASGI server, handles async + WebSocket
ORMSQLAlchemy 2.0≥2.0.25Database-agnostic — same code runs on SQLite (dev) and PostgreSQL (prod)
ValidationPydantic 2≥2.6Type-safe request/response validation, automatic JSON serialization
Authpython-jose + passlibJWT tokens (HS256), bcrypt password hashing, 12h access / 7d refresh
Database (dev)SQLiteZero-config, file-based, perfect for single-server deployment
Database (prod)PostgreSQL 1515.xConnection pooling (20 base + 40 overflow), production-grade
QR Codesqrcode + Pillow≥7.4Material tracking — every basket/reel gets a printed QR label
Printingreportlab + socketDirect Epson thermal printer communication over factory network

Frontend

LayerTechnologyVersionWhy
FrameworkReact19.1Component-based UI, massive ecosystem
LanguageTypeScript5.xType safety across the entire frontend
UI LibraryAnt Design Pro 66.0Enterprise-grade components — tables, forms, layouts, charts out of the box
BuildUmiJS Max4.0.7Ant Design Pro’s build system — routing, state management, request handling
ChartsECharts6.0Production dashboards, stock projections, machine utilization
AnimationFramer Motion12.xUI transitions and micro-interactions
Drag & Dropreact-dnd16.xCable design playground, palette arrangement

Why Ant Design Pro? The single most important framework choice. A cable factory ERP is 80% tables, forms, and CRUD screens. Ant Design Pro provides production-ready versions of all of these with built-in Turkish localization, responsive layouts, role-based menus, and a dark mode. Writing these from scratch would have added months. The trade-off: the frontend is tied to the Ant Design ecosystem.

Infrastructure

ComponentTechnologyNotes
Dev ServerMac MiniFactory floor, local network only
DeploymentDirectNo containers — Python + Node running natively on the server
HTTPSSelf-signed / Let’s EncryptFactory network HTTPS for mobile devices
AI ServiceSeparate Python serviceMulti-provider (Gemini, OpenAI, Claude), read-only access, audit-logged

3. DESIGN RULES

These rules were not written on day one — they crystallized over months of building, breaking, and rebuilding.

Rule 1: Turkish-First Interface

Every UI label, every error message, every notification is in Turkish. Factory operators do not speak English. The Ant Design locale is locked to Turkish. AI prompts are Turkish. The only English in the system is in code comments, variable names, and this documentation.

Rule 2: QR Code Everything

Every physical object that moves through the factory gets a QR code printed on entry. Raw copper baskets, tinned copper baskets, reels, finished cables — all scannable. The QR code links to the full history of that object in the system. This enables traceability from raw material to customer shipment.

Rule 3: Three User Roles, No More

RoleAccessInterfaceDevice
Super AdminEverything — including raw database queries, force delete, AI settingsDesktopPC
Lab UserTest standards, test results, material inspection, quality controlDesktopPC
OperatorProduction sessions, material scanning, machine-specific viewsMobile-firstPhone/Tablet

Rule 4: Design Drives Production

The cable design is the single source of truth. When a design is created in the Cable Playground, it encodes the complete production flow — every machine step, every material requirement, every test. Orders reference designs. Work cards are generated from designs. Material calculations walk through the design’s production flow. Nothing is manually configured per-order.

Rule 5: No Premature Optimization

SQLite in development. PostgreSQL when needed. No Redis cache until there’s a measured performance problem. No message queue until synchronous processing is proven insufficient. Build the simplest thing that works, measure, then optimize.

4. API DESIGN

The backend exposes 25+ router groups, all mounted on a single FastAPI application running on port 8000. Every endpoint follows the same pattern:

@router.get("/endpoint")
async def get_something(
    db: Session = Depends(get_database),          # DB session injected
    current_user: User = Depends(get_current_user) # Auth injected
):
    # Business logic here
    return result

Key Patterns

Router Organization

PrefixModuleRoutes
/api/authAuthenticationLogin, register, refresh, user management
/api/materialsRaw MaterialsCRUD, QR generation, photo upload, weight tracking
/api/suppliersSuppliersCRUD, material catalog, quality rating
/api/siparisOrdersOrder CRUD, cable items, delivery planning, payments
/api/productionProductionSessions, work cards, operator assignments
/api/teknikTechnicalMachine config, cable design, standards, markings
/api/stockStockHalf-product stock, projections, product codes
/api/aiAI AssistantChat, search (read-only, audit-logged)

5. DATABASE STRATEGY

The database was not designed upfront. It grew organically as each module was built. This is intentional — the domain was too complex to design a schema before understanding it.

SQLite → PostgreSQL Migration Path

SQLite
Development
PostgreSQL 15
Production
Read Replicas
If needed

SQLAlchemy ORM ensures the same Python code runs against both databases. The switch requires only changing the DATABASE_URL environment variable. Connection pooling is configured for production: 20 base connections, 40 overflow, hourly recycling, pre-ping validation.

Migration Strategy

Migrations use direct SQL scripts (not Alembic) — CREATE TABLE IF NOT EXISTS and ALTER TABLE with transaction rollback on failure. 19 migration files covering orders, users, machines, materials, stock, printing, and AI audit tables. Each migration is idempotent and can be re-run safely.

Why not Alembic? For a single-developer system where the schema evolves rapidly, hand-written migration scripts with IF NOT EXISTS guards are simpler and more predictable than auto-generated migrations. The trade-off: no automatic schema diff detection. This is acceptable when the developer writes both the model and the migration.

6. FRONTEND ARCHITECTURE

The frontend is a single React application built with Ant Design Pro, serving all modules through role-based menus.

Page Organization

src/pages/
├── Dashboard/              # Ana Panel — overview + AI chat
├── Lab/                    # Laboratuvar — tests, material inspection
├── Teknik/                 # Teknik — cable design, machines, standards
│   ├── CablePlayground/    # Interactive cable design tool
│   ├── Machines/           # Machine configuration
│   └── Standards/          # Test standard management
├── Siparis/                # Sipariş — orders, customers
├── Production/             # Üretim — planning, machine interfaces
│   └── Machines/           # Per-machine production UIs
│       ├── KabatelCekme/
│       ├── Kalaylama/
│       ├── IncetelCekme/
│       ├── Buncher/
│       ├── Extruder/
│       └── ElectronBeam/
├── Stok/                   # Stok — projections, product stock
├── Hammadde/               # Hammadde — material entry, orders
└── Admin/                  # Admin — printers, users, AI settings

Key Frontend Patterns

7. AI INTEGRATION

A separate Python service provides AI-powered search and chat, accessible from the dashboard. The AI has read-only access to the entire database through 92 API endpoint tools.

Multi-Provider

Dozens of API providers supported. Provider-agnostic architecture with a base class and model registry system.

Tool-Based Access

AI doesn’t query the database directly. It calls the same REST API endpoints that the frontend uses, but with a dedicated read-only service account.

Audit Logged

Every AI query, every tool call, every response is logged to the ai_audit_logs table. Full traceability of what the AI accessed.

Why read-only? An AI assistant that can modify production data in a factory is a liability. One hallucinated material deletion could halt production. The AI can search, summarize, and answer questions — but cannot create, update, or delete anything, for now.

8. NETWORK & DEPLOYMENT

The system runs on a Mac Mini on the factory floor, accessible to all devices on the local network.

:8000
BACKEND (FastAPI)
:3000
FRONTEND (React)
LAN
192.168.x.x / 10.0.0.x

Operators use mobile Chrome on phones/tablets to scan QR codes and log production. Managers use desktop browsers for order management, design, and reporting. Both access the same backend on the same network. HTTPS is enabled for mobile device security (camera access for QR scanning requires secure context).

9. THE EVOLUTION PATH

The architecture has a planned evolution path if scale ever demands it:

Stage 1
MODULAR
MONOLITH
Current
Stage 2
DATABASE
SEPARATION
Per-module DBs
Stage 3
SERVICE
EXTRACTION
Critical modules
Stage 4
TRUE
MICROSERVICES
Only if needed

Each stage is a fallback, not a goal. The modular monolith should remain the architecture unless specific, measured problems demand the next stage. Don’t fix what isn’t broken.