Background

BSFG

Hexagonal Architecture & Ports/Adapters Pattern

What is Hexagonal Architecture?

Hexagonal Architecture = Ports and Adapters (Alistair Cockburn).

A software architecture pattern that decouples your application core from external concerns—databases, APIs, UIs, frameworks—by defining narrow interface boundaries called ports and interchangeable implementations called adapters.

Why "Hexagonal"?

The visual metaphor. The application sits at the center of a hexagon; each flat side exposes a Port (interface contract) without preference for "left" (UI) vs. "right" (database) vs. "top" (external service). The symmetry emphasizes that all dependencies are equivalent—no "primary" vs. "secondary" distinction as in layered architecture.

The Anatomy

Port

An interface—the what. Defines "I need append-only storage with offset tracking" without specifying Kafka, PostgreSQL, or S3. In BSFG: StoreBuffer, ForwardBuffer, CursorTracker interfaces.

Adapter

The implementation—the how. Satisfies the port using concrete technology. In BSFG: Kafka adapter, PostgreSQL adapter, S3 adapter, Redis adapter.

Application Core

Your BSFG logic (handoff protocol, cursor advancement). It knows only the Ports, never the Adapters. This isolation enables testing without infrastructure and deployment flexibility.

Hexagonal in BSFG

Your four buffers (ISB, IFB, ESB, EFB) are Ports (abstract contracts). The "Fast swappability" objective requires that swapping PostgreSQL for Kafka (or SQLite for S3) means changing only the Adapter outside the hexagon, not the core logic inside.

Visual Model

graph LR OPC["OPC UA
Producer"] subgraph CORE ["BSFG CORE LOGIC"] Handoff["Handoff Protocol
Cursor Advancement"] end subgraph ISBPort ["Port: ISB
(interface)"] ISBSpec["append()
truncateBefore()
replay()"] end subgraph IFBPort ["Port: IFB
(interface)"] IFBSpec["putIfAbsent()
get()
queryByTimeRange()"] end subgraph KafkaAdapt ["Kafka
Adapter"] KA["append log"] end subgraph PGAdapt ["PostgreSQL
Adapter"] PA["table+offset"] end subgraph RedisAdapt ["Redis
Adapter"] RA["SET NX"] end subgraph S3Adapt ["S3
Adapter"] SA["conditional PUT"] end OPC -->|produce| CORE CORE -->|via| ISBPort CORE -->|via| IFBPort ISBPort -->|implements| KafkaAdapt ISBPort -->|implements| PGAdapt IFBPort -->|implements| RedisAdapt IFBPort -->|implements| S3Adapt KafkaAdapt -->|concrete| Consumer["Consumer Apps"] RedisAdapt -->|concrete| Consumer

The hexagon is the boundary; the ports are the flat edges where external concerns plug in without the core knowing which specific technology is outside.

Benefits Applied to BSFG

Testability: Mock the ports. No need for a running Kafka or PostgreSQL to test the handoff protocol.

Deployment Flexibility: Choose backends for each buffer independently. Use Redis for IFB in dev, PostgreSQL in production.

Fast Swappability: A new backend technology? Implement the StoreBuffer interface and wire it in.

Resilience: If your ISB adapter (e.g., Kafka) fails, you can:

Clarity: The architecture explicitly documents dependencies.

Application to BSFG Interfaces

BSFG defines three narrow ports:

By defining these contracts, BSFG achieves maximum flexibility: the same core protocol runs against any combination of backends. The application cares about the guarantees (append, idempotency, durability), not the technology.

Key Insight

Hexagonal architecture is not just about swapping databases. It's about decoupling concerns. BSFG's handoff protocol is independent of whether you store in Kafka or PostgreSQL. The protocol is the core; the storage is an implementation detail. This separation makes BSFG maintainable, testable, and adaptable to future requirements without redesign.