meridian · distributed command-and-control platform · 11 spring boot services + vue 3 spa
meridian is a working name. Client programs are under NDA and appear here under aliases.
Eleven services, a decoupled event bus, and strict Proof-of-Concept constraints.
A distributed command-and-control estate publishing domain events across Redis channels, fanned out to a Vue 3 single-page client through a centralised STOMP gateway.
Role
Lead developer for the real-time event backbone and spatial evaluation engine, navigating the transition from early-stage architecture to distributed infrastructure.
Two developers for most of it, and for more than a year the only maintainer of the services. The work started as an inherited system: two front-end applications on one back end, littered with anti-patterns, much of it not working. Alongside feature delivery I ran a migration to raise the coding standards across the estate, and contributed to the front-end architecture and several of its features.
Constraint
Built under strict Proof-of-Concept mandates. The requirements demanded a pseudo real-time, multi-user collaborative platform, while explicit limitations deferred the provisioning of production infrastructure. No Kafka cluster, no dedicated API gateway. The problem was to achieve reliable event distribution on lightweight infrastructure and optimistic network assumptions, without backing the future architecture into a corner.
Architecture worth defending
-
A decoupled producer–consumer transport, isolated from the business logic.
Services publish domain events without knowing who consumes them. The entity and area services have no knowledge that geofencing exists, so a deployment that does not need geofencing simply does not run it, and one that does subscribes to the channels it cares about. The transport is explicitly a stopgap: because the business logic never touches it, migrating to an enterprise broker changes the transport and not the services.
-
Delivery guarantees added at the application layer, not assumed from the transport.
Redis pub/sub is fire-and-forget, so a client that is offline when an event is published never receives it. At-least-once redelivery was implemented over the server-to-client hop: pending events queued, explicitly acknowledged by the client, retried on a schedule, with conflicts and failures routed to a dead-letter queue for analysis. Delivery is session-based and channel-separated over STOMP, so an event reaches exactly its intended audience and only while that audience is connected.
-
Command–query separation by structure, deliberately not CQRS.
Separate command and query controllers and service interfaces over one shared JPA entity model and one repository set. That is command–query separation, not CQRS, which requires separate read and write models — a distinction worth stating rather than blurring. Full CQRS multiplies the classes and tests to maintain, and with one back-end developer on a Proof of Concept, delivery speed was the right trade against textbook purity.
Where it falls short, stated plainly
Two Proof-of-Concept debts, both real. Authentication is centralised at the gateway with no service-layer enforcement behind it, so any caller reaching a service port is trusted. And the application-layer queues simulate, in our own code, what a real broker provides as a primitive.
Both have a mapped answer. Replacing the Redis bus with Kafka is close to a drop-in precisely because event generation is decoupled from the transport: the persistent consumer groups the custom queues currently imitate arrive for free. The centralised authentication moves into a dedicated API gateway, enforcing defence in depth at every service boundary rather than only at the edge.