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

How Each Decision Was Made

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 · every record states the cost, not only the benefit A record with an empty consequences section has not recorded a decision.

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 follows the batch
at what cost

01

Architecture Drivers

File Ingest

Nightly Judgement

Order Sheet (.xls)

Operations Console

Quality Attributes — ranked, and the ranking is the tie-breaker

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

Constraints — what could not be changed

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
The skeleton — hard to undo

ADOPTED
Layered — service / router / view

ADOPTED
Client-Server — SPA over JSON

ADOPTED
Batch pipeline + shared store

REJECTED
Microservices · Event-Driven

PATTERN
Proven shapes, narrow scope

Materialized read model

Upsert / window replace / snapshot

Revalidation + version stamp

Outbound tunnel (reverse connect)

The Shape That Came Out — 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
The one-line summary of this ledger

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
What the whole list has in common

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