Hexagonal Architecture

The pure kernel at the centre, storage adapters at the edge — mnesis' crate boundary is the port/adapter seam.

Hexagonal Architecture

Hexagonal architecture (a.k.a. ports and adapters) puts your domain logic at the centre, talking to the outside world only through well-defined interfaces ("ports"). Concrete infrastructure ("adapters") plugs into those ports from the outside. The domain never depends on a database, a framework, or an I/O library — those depend on it.

mnesis is built exactly this way, and the architecture is enforced by the crate boundary — not by convention.

The layers

text
        pure kernel                 persistence edge              infrastructure
   ┌──────────────────┐        ┌──────────────────────┐      ┌───────────────────┐
   │ mnesis           │  <───  │ mnesis-store         │ <──  │ mnesis-fjall      │
   │ aggregates,      │        │ codecs, envelopes,   │      │ mnesis-postgres   │
   │ events, sagas    │  the   │ repository, the      │ the  │ mnesis-inmemory   │
   │                  │  ports │ subscription loop,   │ adap-│ (concrete stores) │
   │ no_std, no-alloc │        │ the store *traits*   │ ters │                   │
   │ (pure core)      │        │ no_std + alloc       │      │ std               │
   └──────────────────┘        └──────────────────────┘      └───────────────────┘

Dependencies point inwards. mnesis-fjall depends on mnesis-store depends on mnesis; never the reverse. You can compile and test the kernel with no store at all, and swap the store without touching a line of domain code.

The ports are two traits

In mnesis the "port" an adapter plugs into is deliberately tiny — just two traits:

  • RawEventStore — byte-level append, read_stream, read_all. The store knows nothing about your events; it moves opaque bytes and stamps versions.
  • WakeSource — the notify half: register a subscriber and wake it after a durable commit, so a live subscription can tail new events.

Everything richer — the typed repository, codecs, subscriptions, snapshots, backup/restore — is built generically on top of those two traits inside mnesis-store. An adapter implements the two ports and inherits the rest for free. (Optional capability ports — AtomicAppend, SnapshotStore, StreamLister — add features when an adapter can support them.)

Why the boundary is load-bearing here

This is not decoration. Because the seam is two small traits:

  • The kernel is no_std-capable — no adapter can drag std or an allocator into your domain. And because the seam is so thin, mnesis-store is no_std too (+ alloc): the whole persistence edge builds for bare-metal ARM. See Embedded & no_std.
  • The subscription loop is written once, generically, and every adapter reuses it — there is no per-adapter copy to drift.
  • A third party can write a new store against a short contract, with no access to mnesis internals, and verify it with the conformance kit.

The port/adapter boundary is why mnesis can promise a stable kernel while letting adapters evolve independently (see Stability).

Where to next