Skip to content

Latest commit

 

History

History
294 lines (200 loc) · 14.8 KB

File metadata and controls

294 lines (200 loc) · 14.8 KB

Architecture

CrankTheCode is a small FastAPI application that serves a personal site (posts + portfolio + Decision Architecture hubs + Decision Architecture Patterns hubs + a Books catalogue page) from Markdown posts, plus a lightweight static asset fingerprinting pipeline and an EPUB book builder.

What runs in production

  • Render starts Uvicorn against the repo-root shim: render.yamlmain.py
    • main.py exists as a compatibility layer for uvicorn main:app deployments and local python main.py: main.py
  • Real ASGI app + app factory live in: create_app() and module-level app

HTTP middleware and runtime policies

Both of these are defined inside create_app():

High-level shape (light “ports/adapters”)

The codebase is intentionally simple and uses a light Ports and Adapters / Clean Architecture flavor:

  • Presentation: FastAPI routers for HTML pages, JSON API, RSS, sitemap/robots.
  • Application: a service façade plus use cases.
  • Domain: typed dataclasses and tag parsing/normalization.
  • Infrastructure/Adapters: filesystem repository, Markdown rendering strategy, static asset pipeline + caching.

Key types:

Composition root for the blog service lives in the presentation layer dependency module:

flowchart TD
  U[Browser / crawler / feed reader] --> APP[FastAPI app]

  APP --> M1[Canonical redirect middleware]
  APP --> M2[Cache policy middleware]

  APP --> HTML[HTML router]
  APP --> API[API router]
  APP --> RSS[RSS router]
  APP --> SEO[Sitemap/robots router]

  HTML --> DI[get_blog_service]
  API --> DI
  RSS --> DI
  SEO --> DI

  DI --> SVC[BlogService]
  SVC --> LUC[ListPostsUseCase]
  SVC --> GUC[GetPostUseCase]

  LUC --> REP[PostsRepository]
  GUC --> REP
  LUC --> MR[MarkdownRenderer]
  GUC --> MR

  REP --> FS[FilesystemPostsRepository]
  MR --> REND[PythonMarkdownRenderer]

  FS --> MD[posts/*.md]
  HTML --> TPL[Jinja templates]
  APP --> STA[Static mounts: /static, /docs]
  STA --> CFS[CachingStaticFiles]
  REND --> ASM[AssetManifest URL rewriting]
Loading

Request flows

Homepage (/)

  • Route handler: homepage()
  • Shared request context (canonical URL, query state, defaults): _base_context()
  • Homepage JSON-LD is created server-side and emitted via template slots: jsonld_json

The homepage is intentionally a gateway (not a post listing).

It contains:

  • A featured single-post CTA for the thesis post:

  • Two gateway cards routing users into the two Decision Architecture ecosystems:

  • Decision Architecture (Structures) → /decision-architecture

  • Decision Architecture Patterns → /patterns

Template: templates/index.html

Visual structure on the homepage is intentionally separated by the green “pill” divider:

Books (/books)

The Books page presents a calm visual catalogue (covers only, linked to Amazon) with restrained spacing.

Posts index (/posts)

  • Route handler: posts_index()
  • Default behaviour hides blog posts from listing unless explicitly included via exclude_blog=0-like truthy values: exclude_blog
  • Filtering inputs:
    • legacy q=cat:<Label> is still supported via current_q: current_q
    • newer query params cat=<Label> and layer=<slug> are also tracked in base context: current_cat, current_layer

Post detail (/posts/{slug})

Decision Architecture gateways and hubs

There are two parallel “layer hub” systems.

Decision Architecture (Structures)

Decision Architecture Patterns

Shared “all layers” hub (/topics)

/topics is the shared “View all layers” destination for both ecosystems.

It renders two pill rows (and intentionally does not duplicate those destinations as a second hub-list section):

  • Patterns layers → /patterns/<layer>
  • Structures layers → /topics/<layer>

Route + template:

RSS, sitemap, robots

Content model and authoring contract (posts)

Posts are posts/*.md files with YAML frontmatter loaded via the filesystem repository: FilesystemPostsRepository.

Supported frontmatter (current)

The post storage model is: MarkdownPost. In addition to title, date and tags, it supports:

  • blurb (used for meta description and list UI)
  • one_liner (used for social preview snippets)
  • image (explicit cover image; also used as default thumbnail)
  • thumb_image (optional tile thumbnail; falls back to image): thumb_image
  • emoji (optional visual thumbnail): emoji
  • social_image (OpenGraph/Twitter image; falls back to cover): social_image
  • extra_images (gallery/screenshot images): extra_images

Normalization rules

Rendering rules (covers + screenshots)

Rendering is intentionally a use case concern (so HTML shape stays testable and consistent):

  • List views extract the first paragraph as a summary and handle cover/thumb selection/stripping: ListPostsUseCase.execute()
  • Detail views strip an explicit cover image paragraph near the top, protect author screenshots sections and can inject a controlled Screenshots section from extra_images: GetPostUseCase.execute()

Taxonomy conventions (navigation)

The site uses simple tag conventions to drive grouping and navigation:

  • Primary category tags: cat:<Label>
    • Decision Architecture (Structures) is cat:Leadership.
    • Patterns is cat:decision-architecture-patterns.
  • Layer tags: layer:<slug>

Decision Architecture (Structures) hubs are derived from cat:Leadership + layer: tags:

Patterns hubs are derived from cat:decision-architecture-patterns + layer: tags:

Templates, static assets and caching

Templates

Sidebar navigation is intentionally explicit/hardcoded (not derived from tags):

The sidebar is styled + sized in CSS:

Sidebar section headers are clickable (and highlight when any child route is active):

Static assets in runtime

Static files are served via a caching-aware StaticFiles subclass:

The app mounts /docs for public artifacts like the CV: fastapi_app.mount("/docs", ...)

EPUB files are retained in-repo but are no longer published under /docs.

Asset fingerprinting pipeline (build step)

Render runs an explicit build step before starting the app: buildCommand.

Runtime selection:

  • The app serves from static_dist/ when present, otherwise falls back to static/: static_dir
  • Environment overrides:

Book builder (Decision Architecture EPUB)

The repository also ships a small book-building subsystem that compiles selected posts into an EPUB.

Tests (architecture regression nets)

The test suite doubles as architecture enforcement by locking in outward behaviour (routing, SEO, caching and deterministic HTML output).