Skip to content
Streamlord

Architecture

Ports and adapters, and why the core imports no framework.

your codehandlers and markup

Streamlord is called from here and calls nothing back. You keep your routing, your templates and your domain; the SDK is a library you call, not a framework you sit inside.

driving portDatastarStream

The way in. patchElements, patchSignals, executeScript and redirect are all on this one interface, and respondDatastar hands you one. Streamlord.readSignals is the other direction of the same door.

the corestreamlord-core

The protocol, the events, the pure encoder, the guards and a strict JSON engine. It imports no framework and depends on nothing but the standard library and coroutines, which is what lets Ktor and Spring put identical bytes on the wire.

driven portSseSink

Where frames are written. Ktor writes into a response channel, the servlet adapter into an output stream. One mutex per stream means concurrent coroutines never interleave their frames.

driven portSignalsCodec

How signals become JSON and back. kotlinx.serialization, Jackson 2 or Jackson 3, or the built-in reader that needs no JSON library at all.

driven portIncomingRequest

Where signals are read from, with the size cap applied while reading. An ApplicationCall on Ktor, an HttpServletRequest on Spring.

Press a box for what it is. Your code calls the driving port, the core calls the driven ports, and the adapters implement them. Nothing points the other way, which is why the core imports no framework.

The layers

Domain (core.domain): DatastarEvent as a sealed hierarchy with PatchElements, PatchSignals and ExecuteScript; ElementPatchMode and ElementNamespace; the non-SSE DatastarResponse types; and the guards, which are the part that makes this an SDK rather than an encoder. Wire keeps line breaks out of single-line fields, ExpressionGuard and ElementsGuard hunt the $ trap, interpolate asks where a value landed, and CspNonce holds the nonce half of Datastar’s CSP mode.

Protocol (core.protocol): SseEncoder, which is pure, SseFrame, DatastarAttributes and every constant of Datastar 1.0.4.

Ports (core.port): the driving DatastarStream; the driven SseSink, SignalsCodec and IncomingRequest.

JSON (core.json): JsonParser, JsonWriter and the JsonValue hierarchy. It is here because the core refuses a JSON dependency, not because anybody wanted to write one.

Application (core.application): Streamlord, the configured facade the adapters call, and StreamAuthorisation, which a stream carries for as long as it runs.

Adapters: everything outside streamlord-core.

Why it is shaped this way

The core never imports a framework. That is not architectural taste for its own sake; it is what makes three claims on the front page true at once.

It is why the core has no dependencies but the standard library and coroutines. There is nothing in it that needs a JSON library, because SignalsCodec is a port and the built-in implementation is a strict RFC 8259 parser and writer of about the size that one interface deserves.

It is why Ktor and Spring are equals. Neither is the port the other was bolted onto. Both are an SseSink and an IncomingRequest, and the encoder they share is the thing the golden files test.

And it is why a new realm is small. Vert.x, http4k, or a plain com.sun.net.httpserver.HttpServer is one SseSink and one IncomingRequest away. Nothing in the core has to change, and the conformance tests apply to the result unchanged.

What the pure encoder buys

SseEncoder takes a DatastarEvent and returns bytes. No IO, no coroutines, no framework. That makes it testable against the official SDK golden files byte for byte, which is exactly what the build does, and it means a protocol bug can be found without starting a server.

Where to break it

If you are extending Streamlord, the rule is short: nothing in streamlord-core may import a framework, and every module is compiled with explicitApi() so that the surface you expose is the surface you meant. Adapters may depend on their framework, and declare it compileOnly so it never reaches a consumer who did not already have it.

What went over the wire

The frames your last search produced, encoded by the same SseEncoder the golden-file tests check. Not a description of them. The frames.

Nothing yet. Search from the top of the page, and what the server sends will appear here.