Skip to content
AyoKoding

Overview

You have finished the by-example tracks for C4, DDD, Hexagonal Architecture, and FSM in the procedural track. Now the question is: how do they all wire together in a real production Go or Rust codebase that ships? This case answers that question using Go / chi / database/sql (canonical, with Three Dots Labs as the reference architecture and Boyle's Domain-Driven Design with Golang as the book-length anchor) and Rust / axum / sqlx / tokio variants, running against the same hypothetical Procure-to-Pay procurement platform backend.

This is an in-progress track. The overview and paradigm framing on this page are stable. Full guide content rolls out under the architecture-procedural-track plan.

How Procedural Wiring Differs From OOP and FP Cases

The OOP case (Spring Boot / ASP.NET Core / NestJS) and the FP case (Giraffe / Ring / Hono / Servant) both depend on framework-managed glue:

  • OOP case: @Configuration reflection-driven DI, @RestController for HTTP routing, @Transactional for transaction boundaries, framework-managed JPA / EF Core / TypeORM repositories.
  • FP case: Giraffe / Hono / Servant routing DSLs, composition root assembling records-of-functions, partial application as DI.

The procedural case is different:

  • Go composition root is main.go — explicit constructor wiring, step-debuggable, no annotations, no reflection. The wiring is plain code.
  • Rust composition root is main.rs — same pattern; Arc<dyn Trait + Send + Sync> for shared port handles across the tokio async runtime.
  • HTTP primary adapter is a thin handler — Go: chi or echo router function; Rust: axum extractor-based handler. No framework annotations on the domain.
  • Repository adapter uses database/sql (Go) or sqlx (Rust). Both are compile-time-checked but lighter-weight than ORMs.
  • Transaction boundaries are explicit — tx, _ := db.Begin(); defer tx.Rollback(); ...; tx.Commit() (Go) or let tx = pool.begin().await?; ...; tx.commit().await? (Rust). No @Transactional annotation.
  • Async / concurrency — Go's goroutines + channels (CSP) or Rust's tokio + async/await; neither is "monadic" in the FP sense.

Authority Basis

How the Four Families Compose in the Procedural Codebase

The four architecture pattern families slot together at different levels of the codebase. Go is the canonical implementation; the structural mapping applies to Rust as well:

FamilyGo (canonical)Rust variant
C4Diagram artifacts checked in alongside source; package names match C4 Component namesSame; module paths (PascalCase) match C4 Component names
DDDTop-level packages per bounded context (procurementplatformbe/purchasing, etc.); aggregates as plain structs with explicit transition methodsTop-level modules per bounded context; aggregates as struct types; lifecycle as typestate or as enum-with-match
Hexdomain/app/ (ports + services) → adapter/{in,out}/main.godomain/app/adapter/{in,out}/main.rs
FSMAggregate state as a field (int / string) with switch-dispatch transitions; looplab/fsm for declarative workflow machinesAggregate state as typestate (different struct type per state) — compile-time-enforced legal transitions; or enum for simpler runtime-checked machines

Running Domain — Procure-to-Pay Procurement Platform

Same procurement-platform-be Procure-to-Pay domain as the OOP and FP cases. Bounded contexts: purchasing, supplier, receiving, invoicing, payments, murabaha-finance. Cross-context events: PurchaseOrderIssued, PurchaseOrderAcknowledged, GoodsReceived, InvoiceMatched, PaymentDisbursed, SupplierApproved.

What This Case Will Cover

Beginner — one context = one hexagon (Go canonical, Rust where it differs):

  • Per-context package layout (procurementplatformbe/purchasing/{domain,app,adapter}).
  • Domain types as plain Go structs / Rust structs with no framework imports.
  • Application service function signatures taking small port interfaces.
  • Output port as a one-method Go interface / Rust trait.
  • HTTP primary adapter — chi handler / axum handler.
  • Composition root in main.go / main.rs — explicit constructor wiring.

Intermediate — wiring real infrastructure:

  • Postgres repository adapter using database/sql (Go) or sqlx (Rust).
  • In-memory test adapter — struct with map[ID]Entity field.
  • Domain event publisher port; outbox adapter pattern for at-least-once delivery.
  • Cross-context Anti-Corruption Layer as a translator package/module.
  • Contract code generation from OpenAPI spec.

Advanced — production patterns:

  • docker-compose integration harness (Go: testcontainers-go; Rust: testcontainers-rs).
  • Database migrations — Go: golang-migrate; Rust: sqlx-cli.
  • Banking port and payment adapter; retry decorator; circuit-breaker decorator.
  • End-to-end domain event flow across four contexts.
  • OpenTelemetry observability adapter — Go: otel-go; Rust: opentelemetry-rust.
  • murabaha-finance optional bounded context for Sharia-compliant procurement financing.
  • Hexagonal anti-patterns (domain importing framework packages; application service instantiating adapters directly).
  • Kubernetes deployment topology, configuration adapter at the deploy seam, background job adapter.

Sibling Cases

  • In OOP — Java 25 / Spring Boot 4 (canonical), Kotlin, C# / ASP.NET Core, TypeScript / NestJS.
  • In FP — F# / Giraffe (canonical), Clojure, TypeScript / Hono, Haskell / Servant.

Comparing the three side-by-side is the fastest way to see what changes when you swap framework-driven OOP wiring → records-of-functions FP wiring → explicit-main procedural wiring.

Rollout Plan

Full beginner / intermediate / advanced guide authoring tracks under plans/in-progress/architecture-procedural-track/.

Last updated May 19, 2026

Command Palette

Search for a command to run...