Architecture and Software Design of This Project

5/8/2026

software-designarchitectureinfrastructuredocker

This website grew from a simple idea: combine finance tools, personal content, and full control over the product into one platform I can evolve over time.
Instead of one big application, I split the system into focused modules with clear responsibilities.

System at a Glance

At a high level, the runtime flow is:

  • Browser requests the website through Nginx.
  • Nginx serves the React app and forwards /api/* traffic to FastAPI.
  • FastAPI routers orchestrate feature logic and validate I/O with Pydantic models.
  • Data is stored in MongoDB (including file objects through GridFS utilities).
  • Heavy financial logic is delegated to reusable Python package code.

The result is a setup where UI concerns, API orchestration, domain calculations, and persistence concerns are intentionally separated. Here is a scheme of the setup.

Architecture

Repository Topology and Boundaries

I organized the project into multiple directories so each one can evolve independently:

  • empage: React + Vite frontend (routing, layout, visualization, markdown rendering).
  • empi: FastAPI backend (HTTP contracts, routers, API orchestration).
  • emloan: financial domain package (immo and ETF Monte Carlo calculations).
  • emdb: MongoDB access package (connections, collections, blog and file helpers).
  • emfrastructure: Docker Compose and Nginx deployment setup.

This is not microservices. It is still one cohesive product, but with modular package boundaries that make responsibilities explicit.

Frontend Design

The frontend is a React 19 single-page app with route-level composition for pages like landing, ETF Monte Carlo, immo calculator, and blog pages.
I use React Query plus Axios (/api base path) to keep network logic centralized and state transitions predictable.

Design choices on this layer:

  • Keep page routing and shell layout simple and explicit.
  • Treat server data as server state (query/mutation model) instead of ad-hoc fetch calls.
  • Render blog content from markdown using react-markdown + math plugins, so writing content stays lightweight.
  • Use Tailwind for fast iteration and consistent styling primitives.

Backend API Design

The backend uses FastAPI with routers mounted per feature domain (home, etf_montecarlo_app, house_app, blog, admin).
This keeps endpoint grouping intuitive and avoids a monolithic file with mixed concerns.

Key design principles:

  • API layer handles validation and transport concerns.
  • Domain calculations live in emloan, not inside route handlers.
  • Persistence helpers come from emdb, not directly embedded everywhere in routers.
  • Pydantic schemas define contracts for input/output and keep payloads predictable for the frontend.

One practical tradeoff appears in the immo flow: intermediate calculator state is serialized and returned in API responses, then posted back on update calls.
This makes iterative workflows easy for the UI, but stateful round-trips can become heavier than pure stateless recomputation.

Domain and Persistence Packages

Two Python packages are central to reuse:

  • emloan contains financial modeling logic (for example Monte Carlo return simulation).
  • emdb encapsulates Mongo connection setup and domain-specific DB helpers (blog, files, datasets).

This split helps keep business logic portable.
I can test financial logic as Python code without touching HTTP, and I can evolve database internals without rewriting endpoint logic.

Content and Data Lifecycle

I have a collection of scripts

  • Blog posts are authored as markdown files.
  • Scripts can convert and publish them through admin endpoints.
  • Monthly return datasets can be inserted/deleted through controlled API calls.

This workflow lets me manage content and financial datasets as code-like assets while still publishing through authenticated backend routes.

Infrastructure and Deployment

Production uses Docker Compose with:

  • Nginx as TLS entry point and reverse proxy.
  • FastAPI app container for backend execution.
  • MongoDB container for persistence.
  • Initialization job for API-key-related bootstrap tasks.
  • Umami analytics and its Postgres database.

Nginx also handles rate limiting on API routes, HTTPS redirection, static asset serving, and security headers.
That keeps edge concerns centralized and avoids coupling them to application code.

What This Design Optimizes For

This architecture optimizes for:

  • Fast iteration on frontend features.
  • Reusable finance logic in Python packages.
  • Clear separation between transport, domain, and persistence concerns.
  • Practical self-hosted deployment without excessive operational complexity.

Next Improvements

A few refinements I want to make next:

  • Strengthen state-handling strategy for long-lived calculator interactions.
  • Add more automated cross-layer tests (UI -> API contract checks).
  • Keep docs synchronized with endpoint and deployment changes as the system grows.

Overall, the design reflects how I like to build: modular pieces that can stand alone, but combine into one coherent product.

Architecture and Software Design of This Project — Emanuel Pegler