Case Study • System Architecture • arc42 · C4 · ADR

ERP System Architecture

Ko&Clo • Enterprise • Private Repository

Executive Summary

Scale

150
Tables
413
API Routes
60
Tabs
26
Batch Jobs
214K LOC · py 158K · js 56K — measured 2026-09-12

Decision Process

arc42 C4 ADR
Business Goals & Requirements Architecture Drivers Core Function Quality Attribute Constraints Design Alternatives Tactic Style Pattern Trade-off what it costs Architecture Decision ADR context · decision · cost 8 decisions recorded · benefits and costs listed for each Each ADR includes context, alternatives, decision and consequences.

Reading Key — Tactic vs Pattern vs Trade-off

TACTIC
Cache the result
what to do

PATTERN
Precomputed read model
in what shape

TRADE-OFF
Freshness depends on batch success
at what cost

01

Architecture Drivers

File Ingest

Nightly Judgement

Order Sheet (.xls)

Operations Console

Quality Attributes — Priority Order

# Quality Measurable scenario Structural response
1 Accuracy
2 Freshness
3 Change velocity
4 Read performance
5 Operability
Scalability excluded

Constraints

Area Constraint Effect on architecture
Data source
Output
Hardware
Network
Business rules
Verification
02

Exploring Design Alternatives

TACTIC
Moves that buy a quality

Time-axis split

Single source of truth

Idempotent re-ingest

On-demand observability

STYLE
Architecture Styles

ADOPTED
Modular Monolith — one process, 60 tab modules

ADOPTED
Layered — service / router / view

ADOPTED
Client-Server — SPA over JSON

ADOPTED
Batch pipeline + shared store

REJECTED
Microservices · Event-Driven

PATTERN
Design Patterns

Materialized read model

Upsert / window replace / snapshot

Revalidation + version stamp

Outbound tunnel (reverse connect)

Runtime Structure — Two Flows, One Integration Point

NIGHT — 26 batch jobs · psycopg2 · writes Vendor · POS CSV · XLS · XLSX Ingest format guard · idempotent Judge · Aggregate regression · percentile Read models ~30 · snapshot_date Order sheet .xls (BIFF) · xlwt PostgreSQL 16 single source of truth · 150 tables · the only integration point DAY — FastAPI · asyncpg · reads only service · 413 routes JSON API Vue 3 · 60 tabs
03

Trade-off Ledger

Decision What it buys What it costs
Precompute at night
No build step
Mount source, no image
No migration tool
Single process
Outbound tunnel
Summary

04

Architecture Decision Records

ADR-001
Python for the backend
Accepted
Context

Alternatives

Decision

Consequences

+

−

ADR-002
No build tooling on the frontend
Accepted
Context

Alternatives

Decision

Consequences

+

−

ADR-003
Vue 3 as the UI framework
Accepted
Context

Alternatives

Decision

Consequences

+

−

ADR-004
PostgreSQL as the only source of truth, with precomputed read models
Accepted
Context

Alternatives

Decision

Consequences

+

−

ADR-005
No schema migration tool
Accepted
Context

Alternatives

Decision

Consequences

+

−

ADR-006
Mount the source instead of rebuilding the image
Accepted
Context

Alternatives

Decision

Consequences

+

−

ADR-007
One process, no auto-reload
Revised 2026-09-11
Context

Alternatives

Decision

Consequences

+

−

ADR-008
Outbound tunnel instead of a reverse proxy
Accepted
Context

Alternatives

Decision

Consequences

+

−

05

Risks & Technical Debt

Item From Impact What it is Re-open signal
Schema truth lag ADR-005 High
Batch-bound freshness ADR-004 High
Initial load cost ADR-002 Medium
Verification outside deploy ADR-002 · 006 Medium
Layer boundary erosion Strategy 5 Medium
Data divergence across nodes ADR-008 Low
Summary

Technology Stack

Backend
Python 3.11 FastAPI 0.115 uvicorn SQLModel asyncpg
Data
PostgreSQL 16 pandas psycopg2 openpyxl xlrd · xlwt
Frontend
Vue 3 (CDN) vue-router 4 ES Modules PWA
Platform
Docker Compose GitHub Actions self-hosted runner Cloudflare Tunnel cron

Impact

Consistency

Velocity

Operability

Traceability