Lanka
A backend platform connecting influencers with brands to run marketing campaigns — account linking, audience analytics, campaign lifecycle, and search-based matching. Built as my BSc diploma project to take modern .NET architecture end to end.
Four bounded contexts.
Each module owns its domain, its persistence, and its integration events — boundaries enforced in code rather than by convention.
The decision that shaped it.
A modular monolith instead of microservices — and I wrote down why, along with what it would cost me.
This is one of 16 architecture decision records in the repo — the habit of writing decisions down forced me to think them through before implementing. Read ADR-003 in full ↗
How the pieces fit.
RabbitMQ with MassTransit for integration events between modules, Outbox/Inbox for reliable delivery, and saga orchestration for multi-step flows like Instagram account linking.
PostgreSQL as the relational store (one schema per module), MongoDB for analytics time-series, Redis for caching and distributed locking, Elasticsearch for search and discovery.
CQRS through MediatR pipelines — EF Core on the command side, Dapper for read-optimised queries.
YARP as a reverse-proxy gateway handling rate limiting and auth forwarding, with Keycloak as the OAuth2/OIDC provider.
.NET Aspire for local orchestration and OpenTelemetry for distributed tracing across modules.
What I'd do differently.
The genuinely useful output of a learning project. These are my own notes from building it.
Design integration event contracts upfront, as part of the module specification — mine evolved during implementation instead.
Build test helpers, factories, and fixtures as the first implementation step. Treating tests as an afterthought made them harder to add later.
Start with the simplest domain model that works and refactor when real requirements demand it, rather than anticipating them.
Document complex flows before implementing them — writing them up afterwards meant reverse-engineering my own work.