← Selected projects

Cultural Event Recommendation Engine

Software Architect & Developer · Freelance Contract

February 2020 – February 2021

Docker Elasticsearch FastAPI Kafka PostgreSQL Python

Also available in ES →

TL;DR

  • 2-person team with full autonomy: we defined scope, priorities and architecture
  • End-to-end distributed backend: ingestion, Kafka processing, Elasticsearch search
  • Rule-based recommendation engine across multiple heterogeneous event sources

The project

Together with a colleague, I co-designed and built a data product for users who wanted to discover cultural events (concerts, exhibitions, theatre, cinema) near them, filtered by their interests. We worked in a largely self-directed fashion, jointly defining features, priorities and technical decisions.

What I built

I owned the full backend architecture and most of the implementation:

  • FastAPI-based main API: serving user searches and recommendations, with filters by location, category and date.
  • Ingestion and normalisation pipelines: scrapers and connectors that collected events from multiple heterogeneous sources, deduplicated them, and normalised them to a common schema before indexing.
  • Apache Kafka to decouple ingestion from processing: events arrived from different sources at different rates, and Kafka absorbed that variability without blocking the API.
  • Elasticsearch as the search and indexing engine: full-text search, geospatial queries and faceted filtering for user-facing searches.
  • Rule-based recommendation engine: matching events to user profiles based on location, category and past preferences.
  • Fully containerised stack with Docker and Docker Compose: reproducible development environments from day one.

The architecture

Working without an existing codebase or team conventions means every structural decision is yours. I chose a distributed monolith with clear separation of concerns: not pure microservices, not a tangle. The right call for that stage. It kept the codebase navigable while leaving room to extract services if the product scaled.

The client shelved the project before it reached users. The system worked; the product decision wasn’t ours to make.