Maritimo
A distributed platform for receiving, processing and displaying real-time vessel movement data.
Role: Full-stack developer and project author
Focus: Distributed services, real-time data and geospatial interfaces
Technologies: React, Leaflet, C#, ASP.NET, Rust, Ruby, PostgreSQL, RabbitMQ, SignalR and Docker
Overview
Maritimo is a collection of applications that receive, decode, store and display Automatic Identification System data.
AIS transmitters broadcast information about vessels and other maritime objects, including their position, heading, speed and identity. Maritime authorities and vessel operators use this information for traffic monitoring, navigation and collision avoidance.
I created Maritimo to revisit a problem domain I had worked with throughout my career and explore how I would approach it using smaller, independently focused services and multiple programming languages.
The challenge
Developing an AIS platform requires more than displaying markers on a map. The system must maintain connections to external receiver stations, process a continuous stream of encoded messages, distribute the resulting data between services, persist historical information and update connected users in real time.
Access to representative live data was another important challenge. To test the system under realistic conditions, I integrated public feeds from the Norwegian Coastal Administration and contacted independent station operators willing to share their AIS data. Their contributions allowed me to test the decoder with a wider variety of real-world messages and observe how the system behaved under greater load.
Architecture
I divided the platform into services with clearly defined responsibilities. RabbitMQ connected them through worker queues and publish-subscribe exchanges, allowing each component to process information independently while keeping the overall data flow loosely coupled.
The decoder was written in Rust and supported ten AIS message types. The station service supplied it with encoded messages through a RabbitMQ worker queue, and the decoder published the resulting data for the other applications to consume. Rust provided predictable performance and strong compile-time guarantees for the most processing-intensive part of the platform.
The persistence, API and real-time services were developed using C# and ASP.NET. The persister stored decoded data in PostgreSQL, while the transmitter accumulated information about vessels and forwarded live updates to the frontend through SignalR. A separate Web API exposed historical information using REST.
Frontend
The React frontend combined historical information from the REST API with live updates received through SignalR. Leaflet provided the interactive map used to display vessels, stations and their latest known positions.
The interface allowed users to inspect individual vessels and receiver stations while continuing to receive position updates without refreshing the page.
Testing and delivery
The services were supported by automated tests and CI/CD pipelines. Docker and Docker Compose provided consistent local environments and allowed the full collection of services and infrastructure dependencies to be started together.
What I learned
Maritimo gave me an opportunity to explore service boundaries, asynchronous messaging and interoperability between different programming languages in a realistic, data-intensive application.
It also reinforced the importance of testing distributed workflows with representative live data rather than treating each service as an isolated component.